? arch/i386/compile/GENERIC
? arch/i386/compile/KAZOO
? arch/i386/conf/KAZOO
Index: arch/i386/conf/GENERIC
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/conf/GENERIC,v
retrieving revision 1.594
diff -u -r1.594 GENERIC
--- arch/i386/conf/GENERIC	25 Feb 2004 18:56:26 -0000	1.594
+++ arch/i386/conf/GENERIC	18 Jun 2004 16:57:03 -0000
@@ -690,7 +690,7 @@
 
 # PCI network interfaces
 an*	at pci? dev ? function ?	# Aironet PC4500/PC4800 (802.11)
-ath*	at pci? dev ? function ?	# Atheros 5210/5211/5212 802.11
+#ath*	at pci? dev ? function ?	# Atheros 5210/5211/5212 802.11
 atw*	at pci? dev ? function ?	# ADMtek ADM8211 (802.11)
 bce* 	at pci? dev ? function ?	# Broadcom 4401 10/100 Ethernet
 bge* 	at pci? dev ? function ?	# Broadcom 570x gigabit Ethernet
Index: conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.657
diff -u -r1.657 files
--- conf/files	17 Feb 2004 05:03:15 -0000	1.657
+++ conf/files	18 Jun 2004 16:57:12 -0000
@@ -1125,6 +1125,7 @@
 file	kern/init_sysent.c
 file	kern/kern_acct.c
 file	kern/kern_clock.c
+file	kern/kern_cpt.c
 file	kern/kern_descrip.c
 file	kern/kern_event.c
 file	kern/kern_exec.c
Index: kern/init_main.c
===================================================================
RCS file: /cvsroot/src/sys/kern/init_main.c,v
retrieving revision 1.233
diff -u -r1.233 init_main.c
--- kern/init_main.c	9 Mar 2004 02:35:45 -0000	1.233
+++ kern/init_main.c	18 Jun 2004 16:57:15 -0000
@@ -147,6 +147,7 @@
 #ifdef LKM
 #include <sys/lkm.h>
 #endif
+#include <sys/cpt.h>
 
 #include <sys/syscall.h>
 #include <sys/sa.h>
@@ -431,6 +432,8 @@
 	ipsec_attach();
 #endif
 
+	cpt_init();
+
 	/*
 	 * Initialize protocols.  Block reception of incoming packets
 	 * until everything is ready.
Index: kern/init_sysent.c
===================================================================
RCS file: /cvsroot/src/sys/kern/init_sysent.c,v
retrieving revision 1.153
diff -u -r1.153 init_sysent.c
--- kern/init_sysent.c	29 Jan 2004 02:00:03 -0000	1.153
+++ kern/init_sysent.c	18 Jun 2004 16:57:16 -0000
@@ -1,14 +1,14 @@
-/* $NetBSD: init_sysent.c,v 1.153 2004/01/29 02:00:03 tsarna Exp $ */
+/* $NetBSD$ */
 
 /*
  * System call switch table.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * created from	NetBSD: syscalls.master,v 1.135 2004/01/02 18:52:17 cl Exp 
+ * created from	NetBSD: syscalls.master,v 1.136 2004/01/29 02:00:03 tsarna Exp 
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: init_sysent.c,v 1.153 2004/01/29 02:00:03 tsarna Exp $");
+__KERNEL_RCSID(0, "$NetBSD$");
 
 #include "opt_ktrace.h"
 #include "opt_nfsserver.h"
@@ -406,9 +406,9 @@
 	{ 3, s(struct compat_43_sys_getsockname_args), 0,
 	    compat_43(sys_getsockname) },	/* 150 = compat_43 ogetsockname */
 	{ 0, 0, 0,
-	    sys_nosys },			/* 151 = unimplemented */
-	{ 0, 0, 0,
-	    sys_nosys },			/* 152 = unimplemented */
+	    sys_cptfork },			/* 151 = cptfork */
+	{ 3, s(struct sys_cptctl_args), 0,
+	    sys_cptctl },			/* 152 = cptctl */
 	{ 0, 0, 0,
 	    sys_nosys },			/* 153 = unimplemented */
 	{ 0, 0, 0,
Index: kern/kern_cpt.c
===================================================================
RCS file: kern/kern_cpt.c
diff -N kern/kern_cpt.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ kern/kern_cpt.c	18 Jun 2004 16:57:17 -0000
@@ -0,0 +1,509 @@
+/*	$Id$	*/
+
+/*-
+ * Copyright (c) 2004 Cubical Solutions Ltd.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$Id$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/pool.h>
+#include <sys/cpt.h>
+
+#include <sys/sa.h>
+#include <sys/syscallargs.h>
+
+#include <uvm/uvm_extern.h>
+#include <uvm/uvm_amap.h>
+
+#ifdef DEBUG
+#define CPT_DEBUG 0
+#endif
+
+#ifdef CPT_DEBUG
+#define DPRINTF(x)	if (cptdebug) printf x;
+static int cptdebug = CPT_DEBUG;
+#else
+#define DPRINTF(x)
+#endif
+
+MALLOC_DEFINE(M_CPT, "cpt", "kernel checkpointing support");
+
+static int	cpt_pages(struct vm_map *, struct __cpt_range *);
+static int	cpt_query(struct __cpt_range *, struct cpt_range *, size_t);
+
+static int	cpt_install(struct proc *, struct cpt_range *, size_t);
+static int	cpt_purge(struct proc *, struct cpt_range *, size_t);
+static void	cpt_purgeall(struct proc *);
+
+static struct __cpt_range *	cpt_range_get(void);
+static void			cpt_range_put(struct __cpt_range *); 
+
+static struct pool cptr_pool;
+
+void
+cpt_init()
+{
+
+	pool_init(&cptr_pool, sizeof(struct __cpt_range),0,0,0, "cptpl",
+	    &pool_allocator_nointr);
+}
+
+static struct __cpt_range *
+cpt_range_get()
+{
+	struct __cpt_range *cptr;
+
+	cptr = pool_get(&cptr_pool, PR_WAITOK);
+	memset(cptr, 0, sizeof(struct __cpt_range));
+
+	return cptr;
+}
+static void
+cpt_range_put(struct __cpt_range *cptr)
+{
+
+	if (cptr->page)
+		free(cptr->page, M_CPT);
+	pool_put(&cptr_pool, cptr);
+}
+
+#define CPTDIRTY(a)							\
+do {									\
+	if (!dirtystart)						\
+		dirtystart = (vaddr_t)(a);				\
+	runlen++;							\
+} while (/*CONSTCOND*/0)
+#define CPTCLEAN()							\
+do {									\
+	if (dirtystart) {						\
+		DPRINTF(("CPTCLEAN: adding range (0x%lx,%d)\n",		\
+		    (unsigned long)dirtystart, (int)runlen*PAGE_SIZE));	\
+		struct __cpt_range *cp_tmp;				\
+		cp_tmp = cpt_range_get();				\
+		cp_tmp->cptr.addr = (caddr_t)dirtystart;		\
+		cp_tmp->cptr.len = runlen * PAGE_SIZE;			\
+		LIST_INSERT_HEAD(&p2->p_cptr_dirty,			\
+		    cp_tmp, entries);					\
+		numdirty++;						\
+	}								\
+	runlen = 0;							\
+	dirtystart = 0;							\
+} while (/*CONSTCOND*/0)
+/*
+ * Scan dirty ranges from the pre-defined range.  This is called from
+ * fork1(), and requires both the parent and child struct proc.  Uses
+ * parent to figure out ranges and child to store dirty ranges.
+ */
+void
+cpt_dirtys(struct proc *p1, struct proc *p2)
+{
+	struct __cpt_range *cptr;
+	vaddr_t dirtystart;
+	size_t numdirty, runlen;
+	int i, error;
+
+	dirtystart = 0;
+	numdirty = 0;
+	runlen = 0;
+
+	LIST_INIT(&p2->p_cptr_dirty);
+
+	DPRINTF(("cpt_dirtys: scanning for dirty pages\n"));
+	LIST_FOREACH(cptr, &p1->p_cptr, entries) {
+		error = cpt_pages(&p1->p_vmspace->vm_map, cptr);
+
+		/*
+		 * XXX: just ignoring the error is *very* wrong, we
+		 * should signal the luser program that it should
+		 * expect a screwup (sautéed).
+		 */
+		if (error)
+			continue;
+		for (i = 0; i < cptr->cptr.len / PAGE_SIZE; i++) {
+			if (pmap_is_modified(cptr->page[i])) {
+#ifdef CPT_DEBUG
+				if (cptdebug > 1)
+					DPRINTF(("vm_page at %p dirty\n",
+					    cptr->page[i]));
+#endif
+				CPTDIRTY((caddr_t)cptr->cptr.addr+i*PAGE_SIZE);
+				pmap_clear_modify(cptr->page[i]);
+			} else {
+				CPTCLEAN();
+			}
+		}
+		CPTCLEAN();
+	}
+	p2->p_cptr_numdirty = numdirty;
+}
+#undef CPTDIRTY
+#undef CPTCLEAN
+
+int
+sys_cptfork(struct lwp *l, void *v, register_t *retval)
+{
+
+	DPRINTF(("cptfork for process at %p\n", l->l_proc));
+	return fork1(l, FORK_CPT, SIGCHLD, NULL, 0, NULL, NULL, retval, NULL);
+}
+
+ssize_t
+sys_cptctl(struct lwp *l, void *v, register_t *retval)
+{
+	struct sys_cptctl_args /* {
+		syscallarg(struct cpt_range *) ranges;
+		syscallarg(size_t) nranges;
+		syscallarg(int) op;
+	} */ *uap = v;
+	int error;
+
+	switch (SCARG(uap, op)) {
+	case CPT_INSTALL:
+		DPRINTF(("sys_cptctl: installing %lu range(s)\n",
+		   (unsigned long)SCARG(uap, nranges)));
+		return cpt_install(l->l_proc,
+		    SCARG(uap, ranges), SCARG(uap, nranges));
+
+	case CPT_PURGE:
+		DPRINTF(("sys_cptctl: purging %lu range(s)\n",
+		   (unsigned long)SCARG(uap, nranges)));
+		return cpt_purge(l->l_proc,
+		    SCARG(uap, ranges), SCARG(uap, nranges));
+
+	case CPT_PURGE_ALL:
+		DPRINTF(("sys_cptctl: purging all ranges\n"));
+		cpt_purgeall(l->l_proc);
+		return 0;
+
+	case CPT_QUERY:
+		/*
+		 * If there's not enough space available, simply
+		 * return the amount of space required.
+		 */
+		if (SCARG(uap, nranges) < l->l_proc->p_cptr_numdirty) {
+			*retval = l->l_proc->p_cptr_numdirty;
+			return 0;
+		}
+
+		/*
+		 * Return number of ranges copied.
+		 */
+		error = cpt_query(LIST_FIRST(&l->l_proc->p_cptr_dirty),
+		    SCARG(uap, ranges), l->l_proc->p_cptr_numdirty);
+		if (!error)
+			*retval = l->l_proc->p_cptr_numdirty;
+		return error;
+
+	case CPT_CLEAN:
+	case CPT_CLEAN_ALL:
+	case CPT_SOIL:
+	case CPT_SOIL_ALL:
+		printf("cptctl: not implemented\n");
+		return 0; /* XXX */
+
+	default:
+		return EINVAL;
+	}
+}
+
+static int
+cpt_install(struct proc *p, struct cpt_range *cptr_user, size_t nranges)
+{
+	struct __cpt_range *cptr;
+	int error;
+
+	/*
+	 * No point in installing zero ranges.
+	 */
+	if (nranges == 0)
+		return EINVAL;
+
+	/*
+	 * Loop through ranges:
+	 *   + fill out __cpt_range structures, i.e. copy range list from
+	 *     userspace and cache struct vmpage's.
+	 *   + call uvm code to modify map entry flags.
+	 */
+	for (; nranges > 0; nranges--, cptr_user++) {
+		cptr = cpt_range_get();
+		if (cptr == NULL)
+			return ENOMEM;
+		error = copyin(cptr_user,&cptr->cptr,sizeof(struct cpt_range));
+		if (error)
+			goto fail;
+
+		/* adjust to page ranges */
+		/* XXX: casts? */
+		cptr->cptr.addr = (void *)trunc_page((vaddr_t)cptr->cptr.addr);
+		cptr->cptr.len = round_page(cptr->cptr.len);
+
+		DPRINTF(("cpt_install: %p/%zu\n",
+		    cptr->cptr.addr, cptr->cptr.len));
+		error = uvm_map_cpt(&p->p_vmspace->vm_map,
+		    (vaddr_t)cptr->cptr.addr,
+		    (vaddr_t)cptr->cptr.addr + cptr->cptr.len,
+		    TRUE);
+		if (error)
+			goto fail;
+
+		/*
+		 * If we're installing ranges for the first time (or after
+		 * removing all in between), initialize.
+		 */
+		if ((p->p_vmspace->vm_map.flags & UVM_MAP_CPT) == 0) {
+			p->p_vmspace->vm_map.flags |= UVM_MAP_CPT;
+			LIST_INIT(&p->p_cptr);
+		}
+
+		LIST_INSERT_HEAD(&p->p_cptr, cptr, entries);
+	}
+	return 0;
+
+fail:
+	cpt_range_put(cptr);
+	return error;
+}
+
+#define CPTR_COMPARE(a,b) ((a).addr == (b).addr && (a).len == (b).len)
+static int
+cpt_purge(struct proc *p, struct cpt_range *cptr_user, size_t nranges)
+{
+	struct __cpt_range *cptr;
+	struct cpt_range cptr_tmp;
+	int error, found;
+
+	if (nranges == 0)
+		return EINVAL;
+
+	/*
+	 * Work through ranges one-by-one, locate "offending" entry
+	 * from list and remove it.  Call uvm code to modify map entry
+	 * flags back to normal.
+	 */
+	for (; nranges > 0; nranges--, cptr_user++) {
+		error = copyin(cptr_user, &cptr_tmp, sizeof(struct cpt_range));
+		if (error)
+			return error;
+		found = 0;
+		LIST_FOREACH(cptr, &p->p_cptr, entries) {
+			if (CPTR_COMPARE(cptr->cptr, cptr_tmp)) {
+				LIST_REMOVE(cptr, entries);
+				error = uvm_map_cpt(&p->p_vmspace->vm_map,
+				    (vaddr_t)cptr->cptr.addr,
+				    (vaddr_t)cptr->cptr.addr + cptr->cptr.len,
+				    FALSE);
+				cpt_range_put(cptr);
+				if (error)
+					return error;
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			return EINVAL;
+	}
+
+	/*
+	 * Remove flag that map has checkpointing entries if we remove
+	 * the last entry.
+	 */
+	if (LIST_EMPTY(&p->p_cptr))
+		p->p_vmspace->vm_map.flags &= ~UVM_MAP_CPT;
+
+	return 0;
+}
+
+static void
+cpt_purgeall(struct proc *p)
+{
+	struct __cpt_range *cptr;
+
+	while (!LIST_EMPTY(&p->p_cptr)) {
+		cptr = LIST_FIRST(&p->p_cptr);
+		LIST_REMOVE(cptr, entries);
+		cpt_range_put(cptr);
+	}
+	uvm_map_cpt_purgeall(&p->p_vmspace->vm_map);
+	p->p_vmspace->vm_map.flags &= ~UVM_MAP_CPT;
+}
+
+static int
+cpt_query(struct __cpt_range *cptr_src, struct cpt_range *cptr_to, size_t num)
+{
+	int error;
+
+	for (; num > 0; num--, cptr_to++) {
+		KASSERT(cptr_src != NULL);
+
+#ifdef CPT_DEBUG
+		if (cptdebug > 1)
+			DPRINTF(("cpt_query: copying from %p to %p\n",
+			    &cptr_src->cptr, cptr_to));
+#endif
+		error = copyout(&cptr_src->cptr, cptr_to,
+		    sizeof(struct cpt_range));
+		if (error)
+			return error;
+		cptr_src = LIST_NEXT(cptr_src, entries);
+	}
+
+	return 0;
+}
+
+/*
+ * Locate vm_page's.  This means digging the mappings of each virtual
+ * address belonging to the range from the bowels of UVM.
+ */
+static int
+cpt_pages(struct vm_map *map, struct __cpt_range *cptr)
+{
+	struct vm_map_entry *entry;
+	struct vm_amap *amap;
+	struct vm_anon *anon;
+	caddr_t startaddr;
+	vaddr_t currva;
+	size_t npages;
+	int error, i;
+
+	i = 0;
+	/*
+	 * If we have already reserved memory for the pages, don't
+	 * reserve again.  The amount of pages per in-kernel __cpt_range
+	 * doesn't change.
+	 */
+	if (cptr->page == NULL) {
+		npages = cptr->cptr.len / PAGE_SIZE;
+		cptr->page = malloc(npages * sizeof(struct vm_page),
+		    M_CPT, M_WAITOK);
+	}
+
+	DPRINTF(("cpt_pages: start: %p, len %x\n",
+	    cptr->cptr.addr, cptr->cptr.len));
+
+	/* lock vm_map and dig out vm_page's */
+	vm_map_lock_read(map);
+	for (startaddr = (caddr_t)cptr->cptr.addr;
+	    startaddr < (caddr_t)cptr->cptr.addr + cptr->cptr.len;
+	    startaddr += PAGE_SIZE) {
+
+		if (uvm_map_lookup_entry(map, (vaddr_t)startaddr, &entry)
+		    == FALSE) {
+			DPRINTF(("cpt_pages: couldn't find entry for "
+			    "address %p\n", startaddr));
+			error = EFAULT;
+			goto fail;
+		}
+
+#if 0
+		/*
+		 * Check that entry is wired.  We do it here, because we
+		 * don't have the vm_map_entry earlier.
+		 */
+		if (!VM_MAPENT_ISWIRED(entry)) {
+			DPRINTF(("cpt_pages: entry at %p not wired\n",
+			    startaddr));
+			error = EFAULT;
+			goto fail;
+		}
+#endif
+
+		/* Get amap */
+		amap = entry->aref.ar_amap;
+		if (!amap) {
+			DPRINTF(("cpt_pages: no amap for %p\n", startaddr));
+			error = EFAULT; /* XXX? */
+			goto fail;
+		}
+
+		amap_lock(amap);
+		for (currva = (vaddr_t)startaddr;
+		    currva < entry->end
+		    && startaddr < (caddr_t)cptr->cptr.addr + cptr->cptr.len;
+		    currva += PAGE_SIZE, startaddr += PAGE_SIZE) {
+			/*
+			 * Lookup anon, easier to do it lookup call/anon
+			 * instead of using amap_lookups.  This is not
+			 * along the critical path anyways.
+			 */
+			anon = amap_lookup(&entry->aref, currva - entry->start);
+			if (!anon) {
+				DPRINTF(("cpt_pages: can't find amap "
+				    "for 0x%lx\n", currva));
+				error = EFAULT;
+				goto fail_amap;
+			}
+			cptr->page[i++] = anon->u.an_page;
+#ifdef CPT_DEBUG
+			if (cptdebug > 2)
+				DPRINTF(("cpt_pages: page %lx: vm_page at %p\n",
+				    currva+entry->start, anon->u.an_page));
+#endif
+		}
+		amap_unlock(amap);
+	}
+	vm_map_unlock(map);
+	return 0;
+
+fail_amap:
+	amap_unlock(amap);
+fail:
+	vm_map_unlock(map);
+	free(cptr->page, M_CPT);
+	return error;
+}
+
+/*
+ * Free appropriate structures.
+ */
+void
+cpt_proc_exit(struct proc *p)
+{
+	struct __cpt_range *cptr;
+
+	/*
+	 * If we are the parent, we'll have a range-list.
+	 */
+	cpt_purgeall(p);
+
+	/*
+	 * For the cptfork() child, we need to free the dirty-list.
+	 */
+	while (!LIST_EMPTY(&p->p_cptr_dirty)) {
+		KASSERT(p->p_cptr_numdirty > 0);
+
+		cptr = LIST_FIRST(&p->p_cptr_dirty);
+		LIST_REMOVE(cptr, entries);
+		cpt_range_put(cptr);
+
+		p->p_cptr_numdirty--;
+	}
+}
Index: kern/kern_exit.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_exit.c,v
retrieving revision 1.138
diff -u -r1.138 kern_exit.c
--- kern/kern_exit.c	5 Mar 2004 07:27:22 -0000	1.138
+++ kern/kern_exit.c	18 Jun 2004 16:57:17 -0000
@@ -113,6 +113,7 @@
 #include <sys/mount.h>
 #include <sys/syscallargs.h>
 #include <sys/systrace.h>
+#include <sys/cpt.h>
 
 #include <machine/cpu.h>
 
@@ -259,6 +260,11 @@
 #endif
 
 	/*
+	 * Free all relevant checkpointing information.
+	 */
+	cpt_proc_exit(p);
+
+	/*
 	 * Close open files and release open-file table.
 	 * This may block!
 	 */
Index: kern/kern_fork.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_fork.c,v
retrieving revision 1.114
diff -u -r1.114 kern_fork.c
--- kern/kern_fork.c	12 Feb 2004 23:47:21 -0000	1.114
+++ kern/kern_fork.c	18 Jun 2004 16:57:18 -0000
@@ -98,6 +98,7 @@
 #include <sys/sched.h>
 #include <sys/signalvar.h>
 #include <sys/systrace.h>
+#include <sys/cpt.h>
 
 #include <sys/sa.h>
 #include <sys/syscallargs.h>
@@ -203,7 +204,7 @@
 	struct proc	*p1, *p2, *parent;
 	uid_t		uid;
 	struct lwp	*l2;
-	int		count, s;
+	int		count, s, uvmflags;
 	vaddr_t		uaddr;
 	boolean_t	inmem;
 
@@ -317,6 +318,10 @@
 	else
 		p2->p_cwdi = cwdinit(p1);
 
+	/* If we're doing cptfork(), record dirty pages into child. */
+	if (flags & FORK_CPT)
+		cpt_dirtys(p1, p2);
+
 	/*
 	 * If p_limit is still copy-on-write, bump refcnt,
 	 * otherwise get a copy that won't be modified.
@@ -389,7 +394,12 @@
 	 */
 	PHOLD(l1);
 
-	uvm_proc_fork(p1, p2, (flags & FORK_SHAREVM) ? TRUE : FALSE);
+	uvmflags = 0;
+	if (flags & FORK_SHAREVM)
+		uvmflags |= UVM_FORK_SHARE;
+	if (flags & FORK_CPT)
+		uvmflags |= UVM_FORK_CPT;
+	uvm_proc_fork(p1, p2, uvmflags);
 
 	/*
 	 * Finish creating the child process.
Index: kern/syscalls.c
===================================================================
RCS file: /cvsroot/src/sys/kern/syscalls.c,v
retrieving revision 1.148
diff -u -r1.148 syscalls.c
--- kern/syscalls.c	29 Jan 2004 02:00:03 -0000	1.148
+++ kern/syscalls.c	18 Jun 2004 16:57:18 -0000
@@ -1,14 +1,14 @@
-/* $NetBSD: syscalls.c,v 1.148 2004/01/29 02:00:03 tsarna Exp $ */
+/* $NetBSD$ */
 
 /*
  * System call names.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * created from	NetBSD: syscalls.master,v 1.135 2004/01/02 18:52:17 cl Exp 
+ * created from	NetBSD: syscalls.master,v 1.136 2004/01/29 02:00:03 tsarna Exp 
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: syscalls.c,v 1.148 2004/01/29 02:00:03 tsarna Exp $");
+__KERNEL_RCSID(0, "$NetBSD$");
 
 #if defined(_KERNEL_OPT)
 #include "opt_ktrace.h"
@@ -196,8 +196,8 @@
 	"quotactl",			/* 148 = quotactl */
 	"compat_43_oquota",	/* 149 = compat_43 oquota */
 	"compat_43_ogetsockname",	/* 150 = compat_43 ogetsockname */
-	"#151 (unimplemented)",		/* 151 = unimplemented */
-	"#152 (unimplemented)",		/* 152 = unimplemented */
+	"cptfork",			/* 151 = cptfork */
+	"cptctl",			/* 152 = cptctl */
 	"#153 (unimplemented)",		/* 153 = unimplemented */
 	"#154 (unimplemented)",		/* 154 = unimplemented */
 #if defined(NFS) || defined(NFSSERVER) || !defined(_KERNEL)
Index: kern/syscalls.master
===================================================================
RCS file: /cvsroot/src/sys/kern/syscalls.master,v
retrieving revision 1.136
diff -u -r1.136 syscalls.master
--- kern/syscalls.master	29 Jan 2004 02:00:03 -0000	1.136
+++ kern/syscalls.master	18 Jun 2004 16:57:19 -0000
@@ -307,8 +307,9 @@
 ; system calls.  (This includes various calls added for compatibity
 ; with other Unix variants.)
 ; Some of these calls are now supported by BSD...
-151	UNIMPL
-152	UNIMPL
+151	STD		{ int sys_cptfork(void); }
+152	STD		{ int sys_cptctl(struct cpt_range *ranges, \
+			    size_t nranges, int op); }
 153	UNIMPL
 154	UNIMPL
 #if defined(NFS) || defined(NFSSERVER) || !defined(_KERNEL)
Index: sys/cpt.h
===================================================================
RCS file: sys/cpt.h
diff -N sys/cpt.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/cpt.h	18 Jun 2004 16:57:20 -0000
@@ -0,0 +1,80 @@
+/*	$Id$	*/
+
+/*-     
+ * Copyright (c) 2004 Cubical Solutions Ltd.
+ * All Rights Reserved.
+ *      
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions   
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *      
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. 
+ */             
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/syslimits.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+
+#include <uvm/uvm_extern.h>
+
+/*
+ * operation types
+ */
+#define CPT_INSTALL	1
+#define CPT_PURGE	2
+#define CPT_PURGE_ALL	3
+#define CPT_QUERY	4
+#define CPT_CLEAN	5
+#define CPT_CLEAN_ALL	6
+#define CPT_SOIL	7
+#define CPT_SOIL_ALL	8
+
+/*
+ * Checkpoint memory range descriptors.
+ */
+struct cpt_range {
+	void *addr;
+	size_t len;
+
+};
+
+#ifdef _KERNEL
+/*
+ * We want to use the same struct in the kernel in a list-like fashion
+ * as opposed to the array-oriented approach in userland.
+ */
+struct __cpt_range {
+	struct cpt_range cptr;		/* range for this struct */
+	struct vm_page **page;		/* cached vm_page array for range */
+	
+	LIST_ENTRY(__cpt_range) entries;
+};
+#endif /* _KERNEL */
+
+__BEGIN_DECLS
+void	cpt_init(void);
+void	cpt_proc_exit(struct proc *);
+void	cpt_dirtys(struct proc *, struct proc *);
+
+/* syscalls */
+pid_t	cptfork(size_t *);
+ssize_t	cptctl(struct cpt_range *, size_t, int);
+__END_DECLS
Index: sys/proc.h
===================================================================
RCS file: /cvsroot/src/sys/sys/proc.h,v
retrieving revision 1.190
diff -u -r1.190 proc.h
--- sys/proc.h	13 Feb 2004 11:36:23 -0000	1.190
+++ sys/proc.h	18 Jun 2004 16:57:21 -0000
@@ -231,6 +231,10 @@
 	const struct execsw *p_execsw;	/* Exec package information */
 	struct klist	p_klist;	/* Knotes attached to this process */
 
+	LIST_HEAD(,__cpt_range) p_cptr;	/* cpt areas currently installed */
+	LIST_HEAD(,__cpt_range) p_cptr_dirty; /* changed memory ranges list */
+	size_t		p_cptr_numdirty; /* number of elements on dirty-list */
+
 /*
  * End area that is zeroed on creation
  */
@@ -371,6 +375,7 @@
 #define	FORK_SHARESIGS	0x10		/* Share signal actions */
 #define	FORK_NOWAIT	0x20		/* Make init the parent of the child */
 #define	FORK_CLEANFILES	0x40		/* Start with a clean descriptor set */
+#define	FORK_CPT	0x80		/* cpt semantics for uvmspace_fork() */
 
 /*
  * Allow machine-dependent code to override curproc in <machine/cpu.h> for
Index: sys/syscall.h
===================================================================
RCS file: /cvsroot/src/sys/sys/syscall.h,v
retrieving revision 1.146
diff -u -r1.146 syscall.h
--- sys/syscall.h	29 Jan 2004 02:00:03 -0000	1.146
+++ sys/syscall.h	18 Jun 2004 16:57:22 -0000
@@ -1,10 +1,10 @@
-/* $NetBSD: syscall.h,v 1.146 2004/01/29 02:00:03 tsarna Exp $ */
+/* $NetBSD$ */
 
 /*
  * System call numbers.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * created from	NetBSD: syscalls.master,v 1.135 2004/01/02 18:52:17 cl Exp 
+ * created from	NetBSD: syscalls.master,v 1.136 2004/01/29 02:00:03 tsarna Exp 
  */
 
 /* syscall: "syscall" ret: "int" args: "int" "..." */
@@ -458,6 +458,12 @@
 /* syscall: "compat_43_ogetsockname" ret: "int" args: "int" "caddr_t" "int *" */
 #define	SYS_compat_43_ogetsockname	150
 
+/* syscall: "cptfork" ret: "int" args: */
+#define	SYS_cptfork	151
+
+/* syscall: "cptctl" ret: "int" args: "struct cpt_range *" "size_t" "int" */
+#define	SYS_cptctl	152
+
 #if defined(NFS) || defined(NFSSERVER) || !defined(_KERNEL)
 /* syscall: "nfssvc" ret: "int" args: "int" "void *" */
 #define	SYS_nfssvc	155
Index: sys/syscallargs.h
===================================================================
RCS file: /cvsroot/src/sys/sys/syscallargs.h,v
retrieving revision 1.128
diff -u -r1.128 syscallargs.h
--- sys/syscallargs.h	29 Jan 2004 02:00:03 -0000	1.128
+++ sys/syscallargs.h	18 Jun 2004 16:57:23 -0000
@@ -1,10 +1,10 @@
-/* $NetBSD: syscallargs.h,v 1.128 2004/01/29 02:00:03 tsarna Exp $ */
+/* $NetBSD$ */
 
 /*
  * System call argument lists.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * created from	NetBSD: syscalls.master,v 1.135 2004/01/02 18:52:17 cl Exp 
+ * created from	NetBSD: syscalls.master,v 1.136 2004/01/29 02:00:03 tsarna Exp 
  */
 
 #ifndef _SYS__SYSCALLARGS_H_
@@ -697,6 +697,12 @@
 	syscallarg(caddr_t) asa;
 	syscallarg(int *) alen;
 };
+
+struct sys_cptctl_args {
+	syscallarg(struct cpt_range *) ranges;
+	syscallarg(size_t) nranges;
+	syscallarg(int) op;
+};
 #if defined(NFS) || defined(NFSSERVER) || !defined(_KERNEL)
 
 struct sys_nfssvc_args {
@@ -1766,6 +1772,10 @@
 
 int	compat_43_sys_getsockname(struct lwp *, void *, register_t *);
 
+int	sys_cptfork(struct lwp *, void *, register_t *);
+
+int	sys_cptctl(struct lwp *, void *, register_t *);
+
 #if defined(NFS) || defined(NFSSERVER) || !defined(_KERNEL)
 int	sys_nfssvc(struct lwp *, void *, register_t *);
 
Index: uvm/uvm_extern.h
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_extern.h,v
retrieving revision 1.89
diff -u -r1.89 uvm_extern.h
--- uvm/uvm_extern.h	13 Feb 2004 13:47:16 -0000	1.89
+++ uvm/uvm_extern.h	18 Jun 2004 16:57:24 -0000
@@ -201,6 +201,12 @@
 #define	UVM_LK_ENTER	0x00000001	/* map locked on entry */
 #define	UVM_LK_EXIT	0x00000002	/* leave map locked on exit */
 
+ /*
+  * flags for forking
+  */
+#define	UVM_FORK_SHARE	0x01		/* share vm space */
+#define	UVM_FORK_CPT	0x02		/* cpt semantics */
+
 /*
  * structures
  */
@@ -561,7 +567,7 @@
 #if defined(KGDB)
 void			uvm_chgkprot __P((caddr_t, size_t, int));
 #endif
-void			uvm_proc_fork __P((struct proc *, struct proc *, boolean_t));
+void			uvm_proc_fork __P((struct proc *, struct proc *, int));
 void			uvm_lwp_fork __P((struct lwp *, struct lwp *,
 			    void *, size_t, void (*)(void *), void *));
 int			uvm_coredump_walkmap __P((struct proc *,
@@ -664,7 +670,7 @@
 void			uvmspace_init __P((struct vmspace *, struct pmap *,
 				vaddr_t, vaddr_t));
 void			uvmspace_exec __P((struct lwp *, vaddr_t, vaddr_t));
-struct vmspace		*uvmspace_fork __P((struct vmspace *));
+struct vmspace		*uvmspace_fork __P((struct vmspace *, int));
 void			uvmspace_free __P((struct vmspace *));
 void			uvmspace_share __P((struct proc *, struct proc *));
 void			uvmspace_unshare __P((struct lwp *));
Index: uvm/uvm_glue.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_glue.c,v
retrieving revision 1.77
diff -u -r1.77 uvm_glue.c
--- uvm/uvm_glue.c	9 Feb 2004 13:11:21 -0000	1.77
+++ uvm/uvm_glue.c	18 Jun 2004 16:57:24 -0000
@@ -215,16 +215,16 @@
  * - the address space is copied as per parent map's inherit values
  */
 void
-uvm_proc_fork(p1, p2, shared)
+uvm_proc_fork(p1, p2, flags)
 	struct proc *p1, *p2;
-	boolean_t shared;
+	int flags;
 {
 
-	if (shared == TRUE) {
+	if (flags & UVM_FORK_SHARE) {
 		p2->p_vmspace = NULL;
 		uvmspace_share(p1, p2);
 	} else {
-		p2->p_vmspace = uvmspace_fork(p1->p_vmspace);
+		p2->p_vmspace = uvmspace_fork(p1->p_vmspace, flags);
 	}
 
 	cpu_proc_fork(p1, p2);
Index: uvm/uvm_map.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_map.c,v
retrieving revision 1.164
diff -u -r1.164 uvm_map.c
--- uvm/uvm_map.c	24 Mar 2004 07:47:33 -0000	1.164
+++ uvm/uvm_map.c	18 Jun 2004 16:57:27 -0000
@@ -2690,6 +2690,89 @@
 }
 
 /*
+ * uvm_map_cpt: control map entry checkpointability features
+ *
+ * => map must be unlocked
+ */
+
+int
+uvm_map_cpt(struct vm_map *map, vaddr_t start, vaddr_t end, boolean_t cpt)
+{
+	struct vm_map_entry *entry;
+	UVMHIST_FUNC("uvm_map_cpt"); UVMHIST_CALLED(maphist);
+	UVMHIST_LOG(maphist,"(map=0x%x,start=0x%x,end=0x%x,cpt=0x%x)",
+	    map, start, end, cpt);
+	
+	vm_map_lock(map);
+	VM_MAP_RANGE_CHECK(map, start, end);
+
+	/* see if the range is mapped */
+	if (uvm_map_lookup_entry(map, start, &entry) == FALSE) {
+		vm_map_unlock(map);
+		return EFAULT;
+	}
+
+	while ((entry != &map->header) && (entry->start < end)) {
+		/*
+		 * Check if entry is wired as we require.
+		 */
+		if (VM_MAPENT_ISWIRED(entry) == 0) {
+			vm_map_unlock(map);
+			return EFAULT;
+		}
+
+		/*
+		 * Technically we need to clip only the first and last
+		 * entries (if even those), but the macros handle the
+		 * situation, so let's just invoke them always.
+		 */
+		UVM_MAP_CLIP_START(map, entry, start);
+		UVM_MAP_CLIP_END(map, entry, end);
+
+		if (cpt) {
+			if (entry->flags & UVM_MAP_CPT) {
+				vm_map_unlock(map);
+				return EINVAL;
+			}
+			entry->flags |= UVM_MAP_CPT;
+		} else {
+			if ((entry->flags & UVM_MAP_CPT) == 0) {
+				vm_map_unlock(map);
+				return EINVAL;
+			}
+			entry->flags &= ~UVM_MAP_CPT;
+		}
+
+		entry = entry->next;
+	}
+
+	vm_map_unlock(map);
+	UVMHIST_LOG(maphist,"<- done (OK)",0,0,0,0);
+	return 0;
+}
+
+/*
+ * Remove all checkpointing entry flags from the map.
+ */
+
+void
+uvm_map_cpt_purgeall(struct vm_map *map)
+{
+	struct vm_map_entry *entry;
+	UVMHIST_FUNC("uvm_map_cpt_purgeall"); UVMHIST_CALLED(maphist);
+	UVMHIST_LOG(maphist,"(map=0x%x)", map, 0,0,0);
+
+	vm_map_lock(map);
+	for (entry = map->header.next; entry != &map->header;
+	    entry = entry->next) {
+		entry->flags &= ~UVM_MAP_CPT;
+	}
+
+	vm_map_unlock(map);
+	UVMHIST_LOG(maphist,"<- done (OK)",0,0,0,0);
+}
+
+/*
  * uvm_map_pageable: sets the pageability of a range in a map.
  *
  * => wires map entries.  should not be used for transient page locking.
@@ -3466,7 +3549,7 @@
 		return;
 
 	/* make a new vmspace, still holding old one */
-	nvm = uvmspace_fork(ovm);
+	nvm = uvmspace_fork(ovm, 0);
 
 	pmap_deactivate(l);		/* unbind old vmspace */
 	p->p_vmspace = nvm;
@@ -3611,13 +3694,14 @@
  */
 
 struct vmspace *
-uvmspace_fork(struct vmspace *vm1)
+uvmspace_fork(struct vmspace *vm1, int flags)
 {
 	struct vmspace *vm2;
 	struct vm_map *old_map = &vm1->vm_map;
 	struct vm_map *new_map;
 	struct vm_map_entry *old_entry;
 	struct vm_map_entry *new_entry;
+	vm_inherit_t inherit;
 	UVMHIST_FUNC("uvmspace_fork"); UVMHIST_CALLED(maphist);
 
 	vm_map_lock(old_map);
@@ -3644,7 +3728,38 @@
 		KASSERT(UVM_ET_ISCOPYONWRITE(old_entry) ||
 			!UVM_ET_ISNEEDSCOPY(old_entry));
 
-		switch (old_entry->inheritance) {
+		/*
+		 * If we're doing cptfork(), special inheritance treatment.
+		 */
+		if (flags & UVM_FORK_CPT) {
+			if (old_entry->flags & UVM_MAP_CPT) {
+				inherit = MAP_INHERIT_COPY;
+			} else {
+				/*
+				 * The only map entrys that can have cow
+				 * inheritance are the ones marked CPT.
+				 * If COW inheritance is attempted for
+				 * non-CPT map entrys, switch that to
+				 * shared inheritance.
+				 */
+				if (old_entry->inheritance == MAP_INHERIT_COPY)
+					inherit = MAP_INHERIT_SHARE;
+				else
+					inherit = old_entry->inheritance;
+
+				/*
+				 * However, we need a COW stack to avoid
+				 * processes mangling each other.
+				 */
+				if (old_entry->start>=(vaddr_t)vm1->vm_maxsaddr
+				   && old_entry->end<=(vaddr_t)vm1->vm_minsaddr)
+					inherit = MAP_INHERIT_COPY;
+			}
+		} else {
+			inherit = old_entry->inheritance;
+		}
+
+		switch (inherit) {
 		case MAP_INHERIT_NONE:
 
 			/*
@@ -3784,7 +3899,8 @@
 			 * allocated any needed amap (above).
 			 */
 
-			if (VM_MAPENT_ISWIRED(old_entry)) {
+			if (VM_MAPENT_ISWIRED(old_entry)
+			    && !(flags & UVM_FORK_CPT)) {
 
 			  /*
 			   * resolve all copy-on-write faults now
Index: uvm/uvm_map.h
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_map.h,v
retrieving revision 1.39
diff -u -r1.39 uvm_map.h
--- uvm/uvm_map.h	10 Feb 2004 01:30:49 -0000	1.39
+++ uvm/uvm_map.h	18 Jun 2004 16:57:28 -0000
@@ -145,6 +145,7 @@
 #define UVM_MAP_STATIC		0x01		/* static map entry */
 #define UVM_MAP_KMEM		0x02		/* from kmem entry pool */
 #define	UVM_MAP_NOMERGE		0x10		/* this entry is not mergable */
+#define UVM_MAP_CPT		0x20		/* entry has cpt-semantics */
 
 };
 
@@ -238,6 +239,7 @@
 #define	VM_MAP_WANTLOCK		0x10		/* rw: want to write-lock */
 #define	VM_MAP_DYING		0x20		/* rw: map is being destroyed */
 #define	VM_MAP_TOPDOWN		0x40		/* ro: arrange map top-down */
+#define VM_MAP_CPT		0x80		/* rw: map has cpt entries */
 
 /* XXX: number of kernel maps and entries to statically allocate */
 
@@ -300,6 +302,8 @@
 int		uvm_map_inherit(struct vm_map *, vaddr_t, vaddr_t,
 		    vm_inherit_t);
 int		uvm_map_advice(struct vm_map *, vaddr_t, vaddr_t, int);
+int		uvm_map_cpt(struct vm_map *, vaddr_t, vaddr_t, int);
+void		uvm_map_cpt_purgeall(struct vm_map *);
 void		uvm_map_init(void);
 boolean_t	uvm_map_lookup_entry(struct vm_map *, vaddr_t,
 		    struct vm_map_entry **);
Index: uvm/uvm_stat.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_stat.c,v
retrieving revision 1.22
diff -u -r1.22 uvm_stat.c
--- uvm/uvm_stat.c	9 Dec 2001 03:07:20 -0000	1.22
+++ uvm/uvm_stat.c	18 Jun 2004 16:57:28 -0000
@@ -61,7 +61,7 @@
 #endif
 
 #ifdef UVMHIST_PRINT
-int uvmhist_print_enabled = 1;
+int uvmhist_print_enabled = 0;
 #endif
 
 #ifdef DDB
