Module Name:    src
Committed By:   martin
Date:           Sun Apr  8 11:27:46 UTC 2012

Modified Files:
        src/lib/libc/gen: posix_spawn_fileactions.c
        src/sys/compat/netbsd32: netbsd32.h netbsd32_execve.c syscalls.master
        src/sys/kern: exec_elf.c kern_exec.c kern_exit.c
        src/sys/sys: exec.h spawn.h
        src/sys/uvm: uvm_extern.h uvm_glue.c uvm_map.c
        src/tests/lib/libc/gen/posix_spawn: t_fileactions.c

Log Message:
Rework posix_spawn locking and memory management:
 - always provide a vmspace for the new proc, initially borrowing from proc0
   (this part fixes PR 46286)
 - increase parallelism between parent and child if arguments allow this,
   avoiding a potential deadlock on exec_lock
 - add a new flag for userland to request old (lockstepped) behaviour for
   better error reporting
 - adapt test cases to the previous two and add a new variant to test the
   diagnostics flag
 - fix a few memory (and lock) leaks
 - provide netbsd32 compat


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/lib/libc/gen/posix_spawn_fileactions.c
cvs rdiff -u -r1.94 -r1.95 src/sys/compat/netbsd32/netbsd32.h \
    src/sys/compat/netbsd32/syscalls.master
cvs rdiff -u -r1.33 -r1.34 src/sys/compat/netbsd32/netbsd32_execve.c
cvs rdiff -u -r1.37 -r1.38 src/sys/kern/exec_elf.c
cvs rdiff -u -r1.347 -r1.348 src/sys/kern/kern_exec.c
cvs rdiff -u -r1.237 -r1.238 src/sys/kern/kern_exit.c
cvs rdiff -u -r1.134 -r1.135 src/sys/sys/exec.h
cvs rdiff -u -r1.1 -r1.2 src/sys/sys/spawn.h
cvs rdiff -u -r1.182 -r1.183 src/sys/uvm/uvm_extern.h
cvs rdiff -u -r1.158 -r1.159 src/sys/uvm/uvm_glue.c
cvs rdiff -u -r1.316 -r1.317 src/sys/uvm/uvm_map.c
cvs rdiff -u -r1.3 -r1.4 src/tests/lib/libc/gen/posix_spawn/t_fileactions.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libc/gen/posix_spawn_fileactions.c
diff -u src/lib/libc/gen/posix_spawn_fileactions.c:1.1 src/lib/libc/gen/posix_spawn_fileactions.c:1.2
--- src/lib/libc/gen/posix_spawn_fileactions.c:1.1	Sat Feb 11 23:31:24 2012
+++ src/lib/libc/gen/posix_spawn_fileactions.c	Sun Apr  8 11:27:44 2012
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: posix_spawn_fileactions.c,v 1.1 2012/02/11 23:31:24 martin Exp $");
+__RCSID("$NetBSD: posix_spawn_fileactions.c,v 1.2 2012/04/08 11:27:44 martin Exp $");
 
 #include "namespace.h"
 
@@ -62,7 +62,7 @@ posix_spawn_file_actions_init(posix_spaw
 int
 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
 {
-	int i;
+	unsigned int i;
 
 	if (fa == NULL)
 		return (-1);
@@ -80,7 +80,7 @@ static int
 posix_spawn_file_actions_getentry(posix_spawn_file_actions_t *fa)
 {
 	if (fa == NULL)
-		return (-1);
+		return -1;
 
 	if (fa->len < fa->size)
 		return fa->len;
@@ -89,7 +89,7 @@ posix_spawn_file_actions_getentry(posix_
 			sizeof(struct posix_spawn_file_actions_entry));
 
 	if (fa->fae == NULL)
-		return (-1);
+		return -1;
 
 	fa->size += MIN_SIZE;
 

Index: src/sys/compat/netbsd32/netbsd32.h
diff -u src/sys/compat/netbsd32/netbsd32.h:1.94 src/sys/compat/netbsd32/netbsd32.h:1.95
--- src/sys/compat/netbsd32/netbsd32.h:1.94	Tue Mar  6 07:37:05 2012
+++ src/sys/compat/netbsd32/netbsd32.h	Sun Apr  8 11:27:44 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: netbsd32.h,v 1.94 2012/03/06 07:37:05 macallan Exp $	*/
+/*	$NetBSD: netbsd32.h,v 1.95 2012/04/08 11:27:44 martin Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001, 2008 Matthew R. Green
@@ -151,6 +151,10 @@ typedef netbsd32_pointer_t netbsd32_lwpi
 typedef netbsd32_pointer_t netbsd32_ucontextp;
 typedef netbsd32_pointer_t netbsd32_caddr_t;
 typedef netbsd32_pointer_t netbsd32_lwpctlp;
+typedef netbsd32_pointer_t netbsd32_posix_spawn_file_actionsp;
+typedef netbsd32_pointer_t netbsd32_posix_spawnattrp;
+typedef netbsd32_pointer_t netbsd32_posix_spawn_file_actions_entryp;
+typedef netbsd32_pointer_t netbsd32_pid_tp;
 
 /*
  * now, the compatibility structures and their fake pointer types.
@@ -941,6 +945,28 @@ struct netbsd32_msdosfs_args {
 	int	gmtoff;		/* v3: offset from UTC in seconds */
 };
 
+struct netbsd32_posix_spawn_file_actions_entry {
+	enum { FAE32_OPEN, FAE32_DUP2, FAE32_CLOSE } fae_action;
+
+	int fae_fildes;
+	union {
+		struct {
+			netbsd32_charp path;
+			int oflag;
+			mode_t mode;
+		} open;
+		struct {
+			int newfildes;
+		} dup2;
+	} fae_data;
+};
+
+struct netbsd32_posix_spawn_file_actions {
+	unsigned int size;	/* size of fae array */
+	unsigned int len;	/* how many slots are used */
+	netbsd32_posix_spawn_file_actions_entryp fae;
+};
+
 #if 0
 int	netbsd32_kevent(struct lwp *, void *, register_t *);
 #endif
Index: src/sys/compat/netbsd32/syscalls.master
diff -u src/sys/compat/netbsd32/syscalls.master:1.94 src/sys/compat/netbsd32/syscalls.master:1.95
--- src/sys/compat/netbsd32/syscalls.master:1.94	Sat Mar 10 21:51:59 2012
+++ src/sys/compat/netbsd32/syscalls.master	Sun Apr  8 11:27:44 2012
@@ -1,4 +1,4 @@
-	$NetBSD: syscalls.master,v 1.94 2012/03/10 21:51:59 joerg Exp $
+	$NetBSD: syscalls.master,v 1.95 2012/04/08 11:27:44 martin Exp $
 
 ;	from: NetBSD: syscalls.master,v 1.81 1998/07/05 08:49:50 jonathan Exp
 ;	@(#)syscalls.master	8.2 (Berkeley) 1/13/94
@@ -1023,3 +1023,10 @@
 			    const netbsd32_timespecp_t tptr); }
 473	STD		{ int|netbsd32||__quotactl(const netbsd32_charp path, \
 			    netbsd32_voidp args); }
+474	NOERR		{ int|netbsd32||posix_spawn(netbsd32_pid_tp pid, \
+				const netbsd32_charp path, \
+				const netbsd32_posix_spawn_file_actionsp \
+				    file_actions, \
+				const netbsd32_posix_spawnattrp attrp, \
+				netbsd32_charpp argv, netbsd32_charpp envp); }
+

Index: src/sys/compat/netbsd32/netbsd32_execve.c
diff -u src/sys/compat/netbsd32/netbsd32_execve.c:1.33 src/sys/compat/netbsd32/netbsd32_execve.c:1.34
--- src/sys/compat/netbsd32/netbsd32_execve.c:1.33	Tue Jan 31 22:53:56 2012
+++ src/sys/compat/netbsd32/netbsd32_execve.c	Sun Apr  8 11:27:44 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: netbsd32_execve.c,v 1.33 2012/01/31 22:53:56 matt Exp $	*/
+/*	$NetBSD: netbsd32_execve.c,v 1.34 2012/04/08 11:27:44 martin Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Matthew R. Green
@@ -28,12 +28,16 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(0, "$NetBSD: netbsd32_execve.c,v 1.33 2012/01/31 22:53:56 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: netbsd32_execve.c,v 1.34 2012/04/08 11:27:44 martin Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/atomic.h>
 #include <sys/mount.h>
+#include <sys/namei.h>
 #include <sys/stat.h>
+#include <sys/spawn.h>
+#include <sys/uidinfo.h>
 #include <sys/vnode.h>
 #include <sys/file.h>
 #include <sys/filedesc.h>
@@ -90,3 +94,140 @@ netbsd32_fexecve(struct lwp *l, const st
 
 	return sys_fexecve(l, &ua, retval);
 }
+
+static int
+netbsd32_posix_spawn_fa_alloc(struct posix_spawn_file_actions **fap,
+    const struct netbsd32_posix_spawn_file_actions *ufa)
+{
+	struct posix_spawn_file_actions *fa;
+	struct netbsd32_posix_spawn_file_actions fa32;
+	struct netbsd32_posix_spawn_file_actions_entry *fae32 = NULL, *f32 = NULL;
+	struct posix_spawn_file_actions_entry *fae;
+	char *pbuf = NULL;
+	int error;
+	size_t fal, fal32, slen, i = 0;
+
+	error = copyin(ufa, &fa32, sizeof(fa32));
+	if (error)
+		return error;
+
+	if (fa32.len == 0)
+		return 0;
+
+	fa = kmem_alloc(sizeof(*fa), KM_SLEEP);
+	fa->len = fa->size = fa32.len;
+
+	fal = fa->len * sizeof(*fae);
+	fal32 = fa->len * sizeof(*fae32);
+
+	fa->fae = kmem_alloc(fal, KM_SLEEP);
+	fae32 = kmem_alloc(fal32, KM_SLEEP);
+	error = copyin(NETBSD32PTR64(fa32.fae), fae32, fal32);
+	if (error)
+		goto out;
+
+	pbuf = PNBUF_GET();
+	for (; i < fa->len; i++) {
+		fae = &fa->fae[i];
+		f32 = &fae32[i];
+		fae->fae_action = f32->fae_action;
+		fae->fae_fildes = f32->fae_fildes;
+		if (fae->fae_action == FAE_DUP2)
+			fae->fae_data.dup2.newfildes =
+			    f32->fae_data.dup2.newfildes;
+		if (fae->fae_action != FAE_OPEN)
+			continue;
+		error = copyinstr(NETBSD32PTR64(f32->fae_path), pbuf,
+		    MAXPATHLEN, &slen);
+		if (error)
+			goto out;
+		fae->fae_path = kmem_alloc(fal, KM_SLEEP);
+		memcpy(fae->fae_path, pbuf, slen);
+		fae->fae_oflag = f32->fae_oflag;
+		fae->fae_mode = f32->fae_mode;
+	}
+	PNBUF_PUT(pbuf);
+	if (fae32)
+		kmem_free(fae32, fal32);
+	*fap = fa;
+	return 0;
+
+out:
+	if (fae32)
+		kmem_free(fae32, fal32);
+	if (pbuf)
+		PNBUF_PUT(pbuf);
+	posix_spawn_fa_free(fa, i);
+	return error;
+}
+
+int
+netbsd32_posix_spawn(struct lwp *l,
+	const struct netbsd32_posix_spawn_args *uap, register_t *retval)
+{
+	/* {
+	syscallarg(netbsd32_pid_tp) pid;
+	syscallarg(const netbsd32_charp) path;
+	syscallarg(const netbsd32_posix_spawn_file_actionsp) file_actions;
+	syscallarg(const netbsd32_posix_spawnattrp) attrp;
+	syscallarg(netbsd32_charpp) argv;
+	syscallarg(netbsd32_charpp) envp;
+	} */
+
+	int error;
+	struct posix_spawn_file_actions *fa = NULL;
+	struct posix_spawnattr *sa = NULL;
+	pid_t pid;
+	bool child_ok = false;
+
+	error = check_posix_spawn(l);
+	if (error) {
+		*retval = error;
+		return 0;
+	}
+
+	/* copy in file_actions struct */
+	if (SCARG_P32(uap, file_actions) != NULL) {
+		error = netbsd32_posix_spawn_fa_alloc(&fa,
+		    SCARG_P32(uap, file_actions));
+		if (error)
+			goto error_exit;
+	}
+
+	/* copyin posix_spawnattr struct */
+	if (SCARG_P32(uap, attrp) != NULL) {
+		sa = kmem_alloc(sizeof(*sa), KM_SLEEP);
+		error = copyin(SCARG_P32(uap, attrp), sa, sizeof(*sa));
+		if (error)
+			goto error_exit;
+	}
+
+	/*
+	 * Do the spawn
+	 */
+	error = do_posix_spawn(l, &pid, &child_ok, SCARG_P32(uap, path), fa,
+	    sa, SCARG_P32(uap, argv), SCARG_P32(uap, envp),
+	    netbsd32_execve_fetch_element);
+	if (error)
+		goto error_exit;
+
+	if (error == 0 && SCARG_P32(uap, pid) != NULL)
+		error = copyout(&pid, SCARG_P32(uap, pid), sizeof(pid));
+
+	*retval = error;
+	return 0;
+
+ error_exit:
+ 	if (!child_ok) {
+		(void)chgproccnt(kauth_cred_getuid(l->l_cred), -1);
+		atomic_dec_uint(&nprocs);
+
+		if (sa)
+			kmem_free(sa, sizeof(*sa));
+		if (fa)
+			posix_spawn_fa_free(fa, fa->len);
+	}
+
+	*retval = error;
+	return 0;
+}

Index: src/sys/kern/exec_elf.c
diff -u src/sys/kern/exec_elf.c:1.37 src/sys/kern/exec_elf.c:1.38
--- src/sys/kern/exec_elf.c:1.37	Sat Feb 11 23:16:16 2012
+++ src/sys/kern/exec_elf.c	Sun Apr  8 11:27:44 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: exec_elf.c,v 1.37 2012/02/11 23:16:16 martin Exp $	*/
+/*	$NetBSD: exec_elf.c,v 1.38 2012/04/08 11:27:44 martin Exp $	*/
 
 /*-
  * Copyright (c) 1994, 2000, 2005 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v 1.37 2012/02/11 23:16:16 martin Exp $");
+__KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v 1.38 2012/04/08 11:27:44 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_pax.h"
@@ -421,7 +421,8 @@ elf_load_file(struct lwp *l, struct exec
 
 	p = l->l_proc;
 
-	if (p->p_vmspace)
+	KASSERT(p->p_vmspace);
+	if (__predict_true(p->p_vmspace != proc0.p_vmspace))
 		use_topdown = p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN;
 	else
 #ifdef __USING_TOPDOWN_VM

Index: src/sys/kern/kern_exec.c
diff -u src/sys/kern/kern_exec.c:1.347 src/sys/kern/kern_exec.c:1.348
--- src/sys/kern/kern_exec.c:1.347	Tue Mar 13 18:40:52 2012
+++ src/sys/kern/kern_exec.c	Sun Apr  8 11:27:44 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_exec.c,v 1.347 2012/03/13 18:40:52 elad Exp $	*/
+/*	$NetBSD: kern_exec.c,v 1.348 2012/04/08 11:27:44 martin Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -59,7 +59,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.347 2012/03/13 18:40:52 elad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.348 2012/04/08 11:27:44 martin Exp $");
 
 #include "opt_exec.h"
 #include "opt_ktrace.h"
@@ -237,14 +237,14 @@ struct execve_data {
  */
 struct spawn_exec_data {
 	struct execve_data	sed_exec;
-	size_t 			sed_actions_len;
-	struct posix_spawn_file_actions_entry 
+	struct posix_spawn_file_actions
 				*sed_actions;
 	struct posix_spawnattr	*sed_attrs;
 	struct proc		*sed_parent;
 	kcondvar_t		sed_cv_child_ready;
 	kmutex_t		sed_mtx_child;
 	int			sed_error;
+	volatile uint32_t	sed_refcnt;
 };
 
 static void *
@@ -842,8 +842,38 @@ execve_loadvm(struct lwp *l, const char 
 	return error;
 }
 
+static void
+execve_free_data(struct execve_data *data)
+{
+
+	/* free the vmspace-creation commands, and release their references */
+	kill_vmcmds(&data->ed_pack.ep_vmcmds);
+	/* kill any opened file descriptor, if necessary */
+	if (data->ed_pack.ep_flags & EXEC_HASFD) {
+		data->ed_pack.ep_flags &= ~EXEC_HASFD;
+		fd_close(data->ed_pack.ep_fd);
+	}
+
+	/* close and put the exec'd file */
+	vn_lock(data->ed_pack.ep_vp, LK_EXCLUSIVE | LK_RETRY);
+	VOP_CLOSE(data->ed_pack.ep_vp, FREAD, curlwp->l_cred);
+	vput(data->ed_pack.ep_vp);
+	pool_put(&exec_pool, data->ed_argp);
+
+	kmem_free(data->ed_pack.ep_hdr, data->ed_pack.ep_hdrlen);
+	if (data->ed_pack.ep_emul_root != NULL)
+		vrele(data->ed_pack.ep_emul_root);
+	if (data->ed_pack.ep_interp != NULL)
+		vrele(data->ed_pack.ep_interp);
+
+	pathbuf_stringcopy_put(data->ed_pathbuf, data->ed_pathstring);
+	pathbuf_destroy(data->ed_pathbuf);
+	PNBUF_PUT(data->ed_resolvedpathbuf);
+}
+
 static int
-execve_runproc(struct lwp *l, struct execve_data * restrict data)
+execve_runproc(struct lwp *l, struct execve_data * restrict data,
+	bool no_local_exec_lock, bool is_spawn)
 {
 	int error = 0;
 	struct proc		*p;
@@ -856,15 +886,20 @@ execve_runproc(struct lwp *l, struct exe
 	struct vmspace		*vm;
 	ksiginfo_t		ksi;
 	ksiginfoq_t		kq;
-	bool			proc_is_new;
 
-	KASSERT(rw_lock_held(&exec_lock));
+	/*
+	 * In case of a posix_spawn operation, the child doing the exec
+	 * might not hold the reader lock on exec_lock, but the parent
+	 * will do this instead.
+	 */
+	KASSERT(no_local_exec_lock || rw_lock_held(&exec_lock));
 	KASSERT(data != NULL);
 	if (data == NULL)
 		return (EINVAL);
 
 	p = l->l_proc;
-	proc_is_new = p->p_vmspace == NULL;
+	if (no_local_exec_lock)
+		KASSERT(is_spawn);
 
 	base_vcp = NULL;
 
@@ -893,7 +928,12 @@ execve_runproc(struct lwp *l, struct exe
 	 * for remapping.  Note that this might replace the current
 	 * vmspace with another!
 	 */
-	uvmspace_exec(l, data->ed_pack.ep_vm_minaddr, data->ed_pack.ep_vm_maxaddr);
+	if (is_spawn)
+		uvmspace_spawn(l, data->ed_pack.ep_vm_minaddr,
+		    data->ed_pack.ep_vm_maxaddr);
+	else
+		uvmspace_exec(l, data->ed_pack.ep_vm_minaddr,
+		    data->ed_pack.ep_vm_maxaddr);
 
 	/* record proc's vnode, for use by procfs and others */
         if (p->p_textvp)
@@ -1318,7 +1358,8 @@ execve_runproc(struct lwp *l, struct exe
 
 	/* Allow new references from the debugger/procfs. */
 	rw_exit(&p->p_reflock);
-	rw_exit(&exec_lock);
+	if (!no_local_exec_lock)
+		rw_exit(&exec_lock);
 
 	mutex_enter(proc_lock);
 
@@ -1360,7 +1401,8 @@ execve_runproc(struct lwp *l, struct exe
  exec_abort:
 	SDT_PROBE(proc,,,exec_failure, error, 0, 0, 0, 0);
 	rw_exit(&p->p_reflock);
-	rw_exit(&exec_lock);
+	if (!no_local_exec_lock)
+		rw_exit(&exec_lock);
 
 	pathbuf_stringcopy_put(data->ed_pathbuf, data->ed_pathstring);
 	pathbuf_destroy(data->ed_pathbuf);
@@ -1373,6 +1415,7 @@ execve_runproc(struct lwp *l, struct exe
 	 */
 	uvm_deallocate(&vm->vm_map, VM_MIN_ADDRESS,
 		VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS);
+
 	exec_free_emul_arg(&data->ed_pack);
 	pool_put(&exec_pool, data->ed_argp);
 	kmem_free(data->ed_pack.ep_hdr, data->ed_pack.ep_hdrlen);
@@ -1382,13 +1425,12 @@ execve_runproc(struct lwp *l, struct exe
 		vrele(data->ed_pack.ep_interp);
 
 	/* Acquire the sched-state mutex (exit1() will release it). */
-	if (!proc_is_new) {
+	if (!is_spawn) {
 		mutex_enter(p->p_lock);
 		exit1(l, W_EXITCODE(error, SIGABRT));
 	}
 
-	/* NOTREACHED */
-	return 0;
+	return error;
 }
 
 int
@@ -1401,7 +1443,7 @@ execve1(struct lwp *l, const char *path,
 	error = execve_loadvm(l, path, args, envs, fetch_element, &data);
 	if (error)
 		return error;
-	error = execve_runproc(l, &data);
+	error = execve_runproc(l, &data, false, false);
 	return error;
 }
 
@@ -1729,9 +1771,38 @@ exec_sigcode_map(struct proc *p, const s
 }
 
 /*
+ * Release a refcount on spawn_exec_data and destroy memory, if this
+ * was the last one.
+ */
+static void
+spawn_exec_data_release(struct spawn_exec_data *data)
+{
+	if (atomic_dec_32_nv(&data->sed_refcnt) != 0)
+		return;
+
+	cv_destroy(&data->sed_cv_child_ready);
+	mutex_destroy(&data->sed_mtx_child);
+
+	if (data->sed_actions)
+		posix_spawn_fa_free(data->sed_actions,
+		    data->sed_actions->len);
+	if (data->sed_attrs)
+		kmem_free(data->sed_attrs,
+		    sizeof(*data->sed_attrs));
+	kmem_free(data, sizeof(*data));
+}
+
+/*
  * A child lwp of a posix_spawn operation starts here and ends up in
  * cpu_spawn_return, dealing with all filedescriptor and scheduler
  * manipulations in between.
+ * The parent waits for the child, as it is not clear wether the child
+ * will be able to aquire its own exec_lock. If it can, the parent can
+ * be released early and continue running in parallel. If not (or if the
+ * magic debug flag is passed in the scheduler attribute struct), the
+ * child rides on the parent's exec lock untill it is ready to return to
+ * to userland - and only then releases the parent. This method loses
+ * concurrency, but improves error reporting.
  */
 static void
 spawn_return(void *arg)
@@ -1741,19 +1812,29 @@ spawn_return(void *arg)
 	int error, newfd;
 	size_t i;
 	const struct posix_spawn_file_actions_entry *fae;
+	pid_t ppid;
 	register_t retval;
 	bool have_reflock;
-
-	/* we have been created non-preemptable */
-	KASSERT(l->l_nopreempt == 1);
+	bool parent_is_waiting = true;
 
 	/*
-	 * The following actions may block, so we need a temporary
-	 * vmspace - borrow the kernel one
-	 */
-	l->l_proc->p_vmspace = proc0.p_vmspace;
-	pmap_activate(l);
-	KPREEMPT_ENABLE(l);
+	 * Check if we can release parent early.
+	 * We either need to have no sed_attrs, or sed_attrs does not
+	 * have POSIX_SPAWN_RETURNERROR or one of the flags, that require
+	 * safe access to the parent proc (passed in sed_parent).
+	 * We then try to get the exec_lock, and only if that works, we can
+	 * release the parent here already.
+	 */
+	ppid = spawn_data->sed_parent->p_pid;
+	if ((!spawn_data->sed_attrs
+	    || (spawn_data->sed_attrs->sa_flags
+	        & (POSIX_SPAWN_RETURNERROR|POSIX_SPAWN_SETPGROUP)) == 0)
+	    && rw_tryenter(&exec_lock, RW_READER)) {
+		parent_is_waiting = false;
+		mutex_enter(&spawn_data->sed_mtx_child);
+		cv_signal(&spawn_data->sed_cv_child_ready);
+		mutex_exit(&spawn_data->sed_mtx_child);
+	}
 
 	/* don't allow debugger access yet */
 	rw_enter(&l->l_proc->p_reflock, RW_WRITER);
@@ -1762,8 +1843,8 @@ spawn_return(void *arg)
 	error = 0;
 	/* handle posix_spawn_file_actions */
 	if (spawn_data->sed_actions != NULL) {
-		for (i = 0; i < spawn_data->sed_actions_len; i++) {
-			fae = &spawn_data->sed_actions[i];
+		for (i = 0; i < spawn_data->sed_actions->len; i++) {
+			fae = &spawn_data->sed_actions->fae[i];
 			switch (fae->fae_action) {
 			case FAE_OPEN:
 				if (fd_getfile(fae->fae_fildes) != NULL) {
@@ -1832,7 +1913,7 @@ spawn_return(void *arg)
 			    &spawn_data->sed_attrs->sa_schedparam);
 		else if (spawn_data->sed_attrs->sa_flags
 		    & POSIX_SPAWN_SETSCHEDPARAM) {
-			error = do_sched_setparam(spawn_data->sed_parent->p_pid, 0,
+			error = do_sched_setparam(ppid, 0,
 			    SCHED_NONE, &spawn_data->sed_attrs->sa_schedparam);
 		}
 		if (error)
@@ -1872,28 +1953,23 @@ spawn_return(void *arg)
 		}
 	}
 
-	/* stop using kernel vmspace */
-	KPREEMPT_DISABLE(l);
-	pmap_deactivate(l);
-	l->l_proc->p_vmspace = NULL;
-
 	/* now do the real exec */
-	rw_enter(&exec_lock, RW_READER);
-	error = execve_runproc(l, &spawn_data->sed_exec);
+	error = execve_runproc(l, &spawn_data->sed_exec, parent_is_waiting,
+	    true);
 	have_reflock = false;
 	if (error == EJUSTRETURN)
 		error = 0;
 	else if (error)
 		goto report_error;
 
-	/* we now have our own vmspace */
-	KPREEMPT_ENABLE(l);
-	KASSERT(l->l_nopreempt == 0);
+	if (parent_is_waiting) {
+		mutex_enter(&spawn_data->sed_mtx_child);
+		cv_signal(&spawn_data->sed_cv_child_ready);
+		mutex_exit(&spawn_data->sed_mtx_child);
+	}
 
-	/* done, signal parent */
-	mutex_enter(&spawn_data->sed_mtx_child);
-	cv_signal(&spawn_data->sed_cv_child_ready);
-	mutex_exit(&spawn_data->sed_mtx_child);
+	/* release our refcount on the data */
+	spawn_exec_data_release(spawn_data);
 
 	/* and finaly: leave to userland for the first time */
 	cpu_spawn_return(l);
@@ -1902,30 +1978,35 @@ spawn_return(void *arg)
 	return;
 
  report_error:
-	if (have_reflock)
+ 	if (have_reflock)
 		rw_exit(&l->l_proc->p_reflock);
 
-	/* stop using kernel vmspace (if we haven't already) */
-	if (l->l_proc->p_vmspace) {
-		KPREEMPT_DISABLE(l);
-		pmap_deactivate(l);
-		l->l_proc->p_vmspace = NULL;
-		/* do not enable preemption without vmspace */
+	if (parent_is_waiting) {
+		/* pass error to parent */
+		mutex_enter(&spawn_data->sed_mtx_child);
+		spawn_data->sed_error = error;
+		cv_signal(&spawn_data->sed_cv_child_ready);
+		mutex_exit(&spawn_data->sed_mtx_child);
+	} else {
+		rw_exit(&exec_lock);
 	}
 
- 	/*
- 	 * Set error value for parent to pick up (and take over ownership
- 	 * of spawn_data again), signal parent and exit this process.
- 	 */
-	mutex_enter(&spawn_data->sed_mtx_child);
-	spawn_data->sed_error = error;
-	cv_signal(&spawn_data->sed_cv_child_ready);
-	mutex_exit(&spawn_data->sed_mtx_child);
+	/* release our refcount on the data */
+	spawn_exec_data_release(spawn_data);
+
+	/* done, exit */
 	mutex_enter(l->l_proc->p_lock);
-	exit1(l, W_EXITCODE(error, SIGABRT));
+	/*
+	 * Posix explicitly asks for an exit code of 127 if we report
+	 * errors from the child process - so, unfortunately, there
+	 * is no way to report a more exact error code.
+	 * A NetBSD specific workaround is POSIX_SPAWN_RETURNERROR as
+	 * flag bit in the attrp argument to posix_spawn(2), see above.
+	 */
+	exit1(l, W_EXITCODE(127, SIGABRT));
 }
 
-static void
+void
 posix_spawn_fa_free(struct posix_spawn_file_actions *fa, size_t len)
 {
 
@@ -1935,7 +2016,7 @@ posix_spawn_fa_free(struct posix_spawn_f
 			continue;
 		kmem_free(fae->fae_path, strlen(fae->fae_path) + 1);
 	}
-	if (fa->len)
+	if (fa->len > 0)
 		kmem_free(fa->fae, sizeof(*fa->fae) * fa->len);
 	kmem_free(fa, sizeof(*fa));
 }
@@ -1958,9 +2039,12 @@ posix_spawn_fa_alloc(struct posix_spawn_
 		goto out;
 	}
 
-	if (fa->len == 0)
+	if (fa->len == 0) {
+		kmem_free(fa, sizeof(*fa));
 		return 0;
+	}
 
+	fa->size = fa->len;
 	size_t fal = fa->len * sizeof(*fae);
 	fae = fa->fae;
 	fa->fae = kmem_alloc(fal, KM_SLEEP);
@@ -1980,6 +2064,7 @@ posix_spawn_fa_alloc(struct posix_spawn_
 		memcpy(fae->fae_path, pbuf, fal);
 	}
 	PNBUF_PUT(pbuf);
+
 	*fap = fa;
 	return 0;
 out:
@@ -1990,29 +2075,11 @@ out:
 }
 
 int
-sys_posix_spawn(struct lwp *l1, const struct sys_posix_spawn_args *uap,
-    register_t *retval)
+check_posix_spawn(struct lwp *l1)
 {
-	/* {
-		syscallarg(pid_t *) pid;
-		syscallarg(const char *) path;
-		syscallarg(const struct posix_spawn_file_actions *) file_actions;
-		syscallarg(const struct posix_spawnattr *) attrp;
-		syscallarg(char *const *) argv;
-		syscallarg(char *const *) envp;
-	} */	
-
-	struct proc *p1, *p2;
-	struct plimit *p1_lim;
-	struct lwp *l2;
-	int error = 0, tnprocs, count;
-	struct posix_spawn_file_actions *fa = NULL;
-	struct posix_spawnattr *sa = NULL;
-	struct spawn_exec_data *spawn_data;
+	int error, tnprocs, count;
 	uid_t uid;
-	vaddr_t uaddr;
-	pid_t pid;
-	bool have_exec_lock = false;
+	struct proc *p1;
 
 	p1 = l1->l_proc;
 	uid = kauth_cred_getuid(l1->l_cred);
@@ -2030,8 +2097,7 @@ sys_posix_spawn(struct lwp *l1, const st
 
 	if (error) {
 		atomic_dec_uint(&nprocs);
-		*retval = EAGAIN;
-		return 0;
+		return EAGAIN;
 	}
 
 	/*
@@ -2042,32 +2108,45 @@ sys_posix_spawn(struct lwp *l1, const st
 	     p1, KAUTH_ARG(KAUTH_REQ_PROCESS_RLIMIT_BYPASS),
 	     &p1->p_rlimit[RLIMIT_NPROC], KAUTH_ARG(RLIMIT_NPROC)) != 0 &&
 	    __predict_false(count > p1->p_rlimit[RLIMIT_NPROC].rlim_cur)) {
-		error = EAGAIN;
-		goto error_exit;
+		(void)chgproccnt(uid, -1);
+		atomic_dec_uint(&nprocs);
+		return EAGAIN;
 	}
 
-	/* copy in file_actions struct */
-	if (SCARG(uap, file_actions) != NULL) {
-		error = posix_spawn_fa_alloc(&fa, SCARG(uap, file_actions));
-		if (error)
-			goto error_exit;
-	}
+	return 0;
+}
 
-	/* copyin posix_spawnattr struct */
-	if (SCARG(uap, attrp) != NULL) {
-		sa = kmem_alloc(sizeof(*sa), KM_SLEEP);
-		error = copyin(SCARG(uap, attrp), sa, sizeof(*sa));
-		if (error)
-			goto error_exit;
-	}
+int
+do_posix_spawn(struct lwp *l1, pid_t *pid_res, bool *child_ok, const char *path,
+	struct posix_spawn_file_actions *fa,
+	struct posix_spawnattr *sa,
+	char *const *argv, char *const *envp,
+	execve_fetch_element_t fetch)
+{
+
+	struct proc *p1, *p2;
+	struct lwp *l2;
+	int error;
+	struct spawn_exec_data *spawn_data;
+	vaddr_t uaddr;
+	pid_t pid;
+	bool have_exec_lock = false;
+
+	p1 = l1->l_proc;
+
+	/* Allocate and init spawn_data */
+	spawn_data = kmem_zalloc(sizeof(*spawn_data), KM_SLEEP);
+	spawn_data->sed_refcnt = 1; /* only parent so far */
+	cv_init(&spawn_data->sed_cv_child_ready, "pspawn");
+	mutex_init(&spawn_data->sed_mtx_child, MUTEX_DEFAULT, IPL_NONE);
+	mutex_enter(&spawn_data->sed_mtx_child);
 
 	/*
 	 * Do the first part of the exec now, collect state
 	 * in spawn_data.
 	 */
-	spawn_data = kmem_zalloc(sizeof(*spawn_data), KM_SLEEP);
-	error = execve_loadvm(l1, SCARG(uap, path), SCARG(uap, argv),
-	    SCARG(uap, envp), execve_fetch_element, &spawn_data->sed_exec);
+	error = execve_loadvm(l1, path, argv,
+	    envp, fetch, &spawn_data->sed_exec);
 	if (error == EJUSTRETURN)
 		error = 0;
 	else if (error)
@@ -2087,7 +2166,9 @@ sys_posix_spawn(struct lwp *l1, const st
 	}
 	
 	/*
-	 * Allocate new proc. Leave it's p_vmspace NULL for now.
+	 * Allocate new proc. Borrow proc0 vmspace for it, we will
+	 * replace it with its own before returning to userland
+	 * in the child.
 	 * This is a point of no return, we will have to go through
 	 * the child proc to properly clean it up past this point.
 	 */
@@ -2103,7 +2184,7 @@ sys_posix_spawn(struct lwp *l1, const st
 	    (unsigned) ((char *)&p2->p_endzero - (char *)&p2->p_startzero));
 	memcpy(&p2->p_startcopy, &p1->p_startcopy,
 	    (unsigned) ((char *)&p2->p_endcopy - (char *)&p2->p_startcopy));
-	p2->p_vmspace = NULL;
+	p2->p_vmspace = proc0.p_vmspace;
 
 	CIRCLEQ_INIT(&p2->p_sigpend.sp_info);
 
@@ -2145,10 +2226,9 @@ sys_posix_spawn(struct lwp *l1, const st
 	 * Note: p_limit (rlimit stuff) is copy-on-write, so normally
 	 * we just need increase pl_refcnt.
 	 */
-	p1_lim = p1->p_limit;
-	if (!p1_lim->pl_writeable) {
-		lim_addref(p1_lim);
-		p2->p_limit = p1_lim;
+	if (!p1->p_limit->pl_writeable) {
+		lim_addref(p1->p_limit);
+		p2->p_limit = p1->p_limit;
 	} else {
 		p2->p_limit = lim_copy(p1->p_limit);
 	}
@@ -2200,17 +2280,10 @@ sys_posix_spawn(struct lwp *l1, const st
 	/*
 	 * Prepare remaining parts of spawn data
 	 */
-	if (fa && fa->len) {
-		spawn_data->sed_actions_len = fa->len;
-		spawn_data->sed_actions = fa->fae;
-	}
-	if (sa)
-		spawn_data->sed_attrs = sa;
+	spawn_data->sed_actions = fa;
+	spawn_data->sed_attrs = sa;
 
 	spawn_data->sed_parent = p1;
-	cv_init(&spawn_data->sed_cv_child_ready, "pspawn");
-	mutex_init(&spawn_data->sed_mtx_child, MUTEX_DEFAULT, IPL_NONE);
-	mutex_enter(&spawn_data->sed_mtx_child);
 
 	/* create LWP */
 	lwp_create(l1, p2, uaddr, 0, NULL, 0, spawn_return, spawn_data,
@@ -2241,7 +2314,11 @@ sys_posix_spawn(struct lwp *l1, const st
 		kauth_cred_free(ocred);
 	}
 
+	*child_ok = true;
+	spawn_data->sed_refcnt = 2;	/* child gets it as well */
+#if 0
 	l2->l_nopreempt = 1; /* start it non-preemptable */
+#endif
 
 	/*
 	 * It's now safe for the scheduler and other processes to see the
@@ -2283,23 +2360,76 @@ sys_posix_spawn(struct lwp *l1, const st
 	mutex_exit(proc_lock);
 
 	cv_wait(&spawn_data->sed_cv_child_ready, &spawn_data->sed_mtx_child);
-	mutex_exit(&spawn_data->sed_mtx_child);
 	error = spawn_data->sed_error;
+	mutex_exit(&spawn_data->sed_mtx_child);
+	spawn_exec_data_release(spawn_data);
 
 	rw_exit(&p1->p_reflock);
 	rw_exit(&exec_lock);
 	have_exec_lock = false;
 
-	if (fa)
-		posix_spawn_fa_free(fa, fa->len);
+	*pid_res = pid;
+	return error;
 
-	if (sa) 
-		kmem_free(sa, sizeof(*sa));
+ error_exit:
+ 	if (have_exec_lock) {
+		execve_free_data(&spawn_data->sed_exec);
+		rw_exit(&p1->p_reflock);
+ 		rw_exit(&exec_lock);
+	}
+	mutex_exit(&spawn_data->sed_mtx_child);
+	spawn_exec_data_release(spawn_data);
+ 
+	return error;
+}
 
-	cv_destroy(&spawn_data->sed_cv_child_ready);
-	mutex_destroy(&spawn_data->sed_mtx_child);
+int
+sys_posix_spawn(struct lwp *l1, const struct sys_posix_spawn_args *uap,
+    register_t *retval)
+{
+	/* {
+		syscallarg(pid_t *) pid;
+		syscallarg(const char *) path;
+		syscallarg(const struct posix_spawn_file_actions *) file_actions;
+		syscallarg(const struct posix_spawnattr *) attrp;
+		syscallarg(char *const *) argv;
+		syscallarg(char *const *) envp;
+	} */	
+
+	int error;
+	struct posix_spawn_file_actions *fa = NULL;
+	struct posix_spawnattr *sa = NULL;
+	pid_t pid;
+	bool child_ok = false;
+
+	error = check_posix_spawn(l1);
+	if (error) {
+		*retval = error;
+		return 0;
+	}
+
+	/* copy in file_actions struct */
+	if (SCARG(uap, file_actions) != NULL) {
+		error = posix_spawn_fa_alloc(&fa, SCARG(uap, file_actions));
+		if (error)
+			goto error_exit;
+	}
+
+	/* copyin posix_spawnattr struct */
+	if (SCARG(uap, attrp) != NULL) {
+		sa = kmem_alloc(sizeof(*sa), KM_SLEEP);
+		error = copyin(SCARG(uap, attrp), sa, sizeof(*sa));
+		if (error)
+			goto error_exit;
+	}
 
-	kmem_free(spawn_data, sizeof(*spawn_data));
+	/*
+	 * Do the spawn
+	 */
+	error = do_posix_spawn(l1, &pid, &child_ok, SCARG(uap, path), fa, sa,
+	    SCARG(uap, argv), SCARG(uap, envp), execve_fetch_element);
+	if (error)
+		goto error_exit;
 
 	if (error == 0 && SCARG(uap, pid) != NULL)
 		error = copyout(&pid, SCARG(uap, pid), sizeof(pid));
@@ -2308,17 +2438,15 @@ sys_posix_spawn(struct lwp *l1, const st
 	return 0;
 
  error_exit:
- 	if (have_exec_lock)
- 		rw_exit(&exec_lock);
- 
-	if (fa)
-		posix_spawn_fa_free(fa, fa->len);
-
-	if (sa) 
-		kmem_free(sa, sizeof(*sa));
+	if (!child_ok) {
+		(void)chgproccnt(kauth_cred_getuid(l1->l_cred), -1);
+		atomic_dec_uint(&nprocs);
 
-	(void)chgproccnt(uid, -1);
-	atomic_dec_uint(&nprocs);
+		if (sa)
+			kmem_free(sa, sizeof(*sa));
+		if (fa)
+			posix_spawn_fa_free(fa, fa->len);
+	}
 
 	*retval = error;
 	return 0;

Index: src/sys/kern/kern_exit.c
diff -u src/sys/kern/kern_exit.c:1.237 src/sys/kern/kern_exit.c:1.238
--- src/sys/kern/kern_exit.c:1.237	Sun Feb 19 21:06:50 2012
+++ src/sys/kern/kern_exit.c	Sun Apr  8 11:27:45 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_exit.c,v 1.237 2012/02/19 21:06:50 rmind Exp $	*/
+/*	$NetBSD: kern_exit.c,v 1.238 2012/04/08 11:27:45 martin Exp $	*/
 
 /*-
  * Copyright (c) 1998, 1999, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -67,7 +67,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.237 2012/02/19 21:06:50 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.238 2012/04/08 11:27:45 martin Exp $");
 
 #include "opt_ktrace.h"
 #include "opt_perfctrs.h"
@@ -202,6 +202,7 @@ exit1(struct lwp *l, int rv)
 	p = l->l_proc;
 
 	KASSERT(mutex_owned(p->p_lock));
+	KASSERT(p->p_vmspace != NULL);
 
 	if (__predict_false(p == initproc)) {
 		panic("init died (signal %d, exit %d)",
@@ -567,12 +568,8 @@ exit1(struct lwp *l, int rv)
 	 * case these resources are in the PCB.
 	 */
 	cpu_lwp_free(l, 1);
-	/*
-	 * A new child of a posix_spawn operation might never have been
-	 * to userland - no pmap deactivation is needed in this case
-	 */
-	if (__predict_true(l->l_proc->p_vmspace != NULL))
-		pmap_deactivate(l);
+
+	pmap_deactivate(l);
 
 	/* This process no longer needs to hold the kernel lock. */
 #ifdef notyet

Index: src/sys/sys/exec.h
diff -u src/sys/sys/exec.h:1.134 src/sys/sys/exec.h:1.135
--- src/sys/sys/exec.h:1.134	Fri Feb  3 20:11:54 2012
+++ src/sys/sys/exec.h	Sun Apr  8 11:27:45 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: exec.h,v 1.134 2012/02/03 20:11:54 matt Exp $	*/
+/*	$NetBSD: exec.h,v 1.135 2012/04/08 11:27:45 martin Exp $	*/
 
 /*-
  * Copyright (c) 1992, 1993
@@ -273,6 +273,7 @@ int	coredump_write		(void *, enum uio_se
 
 void	exec_free_emul_arg	(struct exec_package *);
 
+
 /*
  * Machine dependent functions
  */
@@ -296,6 +297,14 @@ typedef	int (*execve_fetch_element_t)(ch
 int	execve1(struct lwp *, const char *, char * const *, char * const *,
     execve_fetch_element_t);
 
+struct posix_spawn_file_actions;
+struct posix_spawnattr;
+int	check_posix_spawn	(struct lwp *);
+void	posix_spawn_fa_free(struct posix_spawn_file_actions *, size_t);
+int	do_posix_spawn(struct lwp *, pid_t *, bool*, const char *,
+    struct posix_spawn_file_actions *, struct posix_spawnattr *,
+    char *const *argv, char *const *, execve_fetch_element_t);
+
 extern int	maxexec;
 
 #endif /* _KERNEL */

Index: src/sys/sys/spawn.h
diff -u src/sys/sys/spawn.h:1.1 src/sys/sys/spawn.h:1.2
--- src/sys/sys/spawn.h:1.1	Sat Feb 11 23:16:18 2012
+++ src/sys/sys/spawn.h	Sun Apr  8 11:27:45 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: spawn.h,v 1.1 2012/02/11 23:16:18 martin Exp $	*/
+/*	$NetBSD: spawn.h,v 1.2 2012/04/08 11:27:45 martin Exp $	*/
 
 /*-
  * Copyright (c) 2008 Ed Schouten <e...@freebsd.org>
@@ -67,8 +67,8 @@ typedef struct posix_spawn_file_actions_
 } posix_spawn_file_actions_entry_t;
 
 struct posix_spawn_file_actions {
-	int size;	/* size of fae array */
-	int len;	/* how many slots are used */
+	unsigned int size;	/* size of fae array */
+	unsigned int len;	/* how many slots are used */
 	posix_spawn_file_actions_entry_t *fae;	
 };
 
@@ -81,6 +81,23 @@ typedef struct posix_spawn_file_actions	
 #define POSIX_SPAWN_SETSCHEDULER	0x08
 #define POSIX_SPAWN_SETSIGDEF		0x10
 #define POSIX_SPAWN_SETSIGMASK		0x20
+/*
+ * THIS IS A NON-PORTABLE NetBSD-ONLY EXTENSION, DO NOT USE OUTSIDE
+ * OF UNIT TEST CODE!
+ * With this flag set, the kernel part of posix_spawn will not try to
+ * maximize parallelism, but instead the parent will wait for the child
+ * process to complete all file/scheduler actions and report back errors
+ * from that via the return value of the posix_spawn syscall. This is
+ * usefull for testing, as it can verify the generated error codes and
+ * match to the supposedly triggered failures.
+ * In general, the kernel will return from the posix_spawn syscall as
+ * early as possible, as soon as creating the new process is known to
+ * work. Errors might either be reported back via the return value in
+ * the parent, or (less explicit) by an error exit of the child
+ * process. Our test cases deal with both behaviours in the general
+ * case, but request the POSIX_SPAWN_RETURNERROR for some tests.
+ */
+#define POSIX_SPAWN_RETURNERROR		0x40
 
 #endif /* !_SYS_SPAWN_H_ */
 

Index: src/sys/uvm/uvm_extern.h
diff -u src/sys/uvm/uvm_extern.h:1.182 src/sys/uvm/uvm_extern.h:1.183
--- src/sys/uvm/uvm_extern.h:1.182	Sun Mar 18 13:31:14 2012
+++ src/sys/uvm/uvm_extern.h	Sun Apr  8 11:27:45 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_extern.h,v 1.182 2012/03/18 13:31:14 uebayasi Exp $	*/
+/*	$NetBSD: uvm_extern.h,v 1.183 2012/04/08 11:27:45 martin Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -647,6 +647,7 @@ struct vmspace		*uvmspace_alloc(vaddr_t,
 void			uvmspace_init(struct vmspace *, struct pmap *,
 			    vaddr_t, vaddr_t);
 void			uvmspace_exec(struct lwp *, vaddr_t, vaddr_t);
+void			uvmspace_spawn(struct lwp *, vaddr_t, vaddr_t);
 struct vmspace		*uvmspace_fork(struct vmspace *);
 void			uvmspace_addref(struct vmspace *);
 void			uvmspace_free(struct vmspace *);

Index: src/sys/uvm/uvm_glue.c
diff -u src/sys/uvm/uvm_glue.c:1.158 src/sys/uvm/uvm_glue.c:1.159
--- src/sys/uvm/uvm_glue.c:1.158	Fri Apr  6 17:16:30 2012
+++ src/sys/uvm/uvm_glue.c	Sun Apr  8 11:27:45 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_glue.c,v 1.158 2012/04/06 17:16:30 chs Exp $	*/
+/*	$NetBSD: uvm_glue.c,v 1.159 2012/04/08 11:27:45 martin Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_glue.c,v 1.158 2012/04/06 17:16:30 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_glue.c,v 1.159 2012/04/08 11:27:45 martin Exp $");
 
 #include "opt_kgdb.h"
 #include "opt_kstack.h"
@@ -419,19 +419,21 @@ uvm_proc_exit(struct proc *p)
 
 	KASSERT(p == l->l_proc);
 	ovm = p->p_vmspace;
+	KASSERT(ovm != NULL);
+
+	if (__predict_false(ovm == proc0.p_vmspace))
+		return;
 
 	/*
 	 * borrow proc0's address space.
 	 */
 	KPREEMPT_DISABLE(l);
-	if (__predict_true(ovm != NULL))
-		pmap_deactivate(l);
+	pmap_deactivate(l);
 	p->p_vmspace = proc0.p_vmspace;
 	pmap_activate(l);
 	KPREEMPT_ENABLE(l);
 
-	if (__predict_true(ovm!=NULL))
-		uvmspace_free(ovm);
+	uvmspace_free(ovm);
 }
 
 void

Index: src/sys/uvm/uvm_map.c
diff -u src/sys/uvm/uvm_map.c:1.316 src/sys/uvm/uvm_map.c:1.317
--- src/sys/uvm/uvm_map.c:1.316	Tue Mar 13 18:41:15 2012
+++ src/sys/uvm/uvm_map.c	Sun Apr  8 11:27:45 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_map.c,v 1.316 2012/03/13 18:41:15 elad Exp $	*/
+/*	$NetBSD: uvm_map.c,v 1.317 2012/04/08 11:27:45 martin Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.316 2012/03/13 18:41:15 elad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.317 2012/04/08 11:27:45 martin Exp $");
 
 #include "opt_ddb.h"
 #include "opt_uvmhist.h"
@@ -4010,6 +4010,28 @@ uvmspace_unshare(struct lwp *l)
 
 #endif
 
+
+/*
+ * uvmspace_spawn: a new process has been spawned and needs a vmspace
+ */
+
+void
+uvmspace_spawn(struct lwp *l, vaddr_t start, vaddr_t end)
+{
+	struct proc *p = l->l_proc;
+	struct vmspace *nvm;
+
+#ifdef __HAVE_CPU_VMSPACE_EXEC
+	cpu_vmspace_exec(l, start, end);
+#endif
+
+	nvm = uvmspace_alloc(start, end);
+	kpreempt_disable();
+	p->p_vmspace = nvm;
+	pmap_activate(l);
+	kpreempt_enable();
+}
+
 /*
  * uvmspace_exec: the process wants to exec a new program
  */
@@ -4021,23 +4043,11 @@ uvmspace_exec(struct lwp *l, vaddr_t sta
 	struct vmspace *nvm, *ovm = p->p_vmspace;
 	struct vm_map *map;
 
+	KASSERT(ovm != NULL);
 #ifdef __HAVE_CPU_VMSPACE_EXEC
 	cpu_vmspace_exec(l, start, end);
 #endif
 
-	/*
-	 * Special case: no vmspace yet (see posix_spawn) -
-	 * no races possible in this case.
-	 */
-	if (ovm == NULL) {
-		ovm = uvmspace_alloc(start, end);
-		kpreempt_disable();
-		p->p_vmspace = ovm;
-		pmap_activate(l);
-		kpreempt_enable();
-		return;
-	}
-
 	map = &ovm->vm_map;
 	/*
 	 * see if more than one process is using this vmspace...

Index: src/tests/lib/libc/gen/posix_spawn/t_fileactions.c
diff -u src/tests/lib/libc/gen/posix_spawn/t_fileactions.c:1.3 src/tests/lib/libc/gen/posix_spawn/t_fileactions.c:1.4
--- src/tests/lib/libc/gen/posix_spawn/t_fileactions.c:1.3	Mon Feb 20 12:22:40 2012
+++ src/tests/lib/libc/gen/posix_spawn/t_fileactions.c	Sun Apr  8 11:27:46 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: t_fileactions.c,v 1.3 2012/02/20 12:22:40 martin Exp $ */
+/* $NetBSD: t_fileactions.c,v 1.4 2012/04/08 11:27:46 martin Exp $ */
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -222,7 +222,7 @@ ATF_TC_HEAD(t_spawn_open_nonexistent, tc
 
 ATF_TC_BODY(t_spawn_open_nonexistent, tc)
 {
-	int err;
+	int err, status;
 	pid_t pid;
 	char * const args[2] = { __UNCONST("cat"), NULL };
 	posix_spawn_file_actions_t fa;
@@ -231,8 +231,58 @@ ATF_TC_BODY(t_spawn_open_nonexistent, tc
 	posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
 	    "./non/ex/ist/ent", O_RDONLY, 0);
 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
+	if (err == 0) {
+		/*
+		 * The child has been created - it should fail and
+		 * return exit code 127
+		 */
+		waitpid(pid, &status, 0);
+		ATF_REQUIRE(WEXITSTATUS(status) == 127);
+		
+	} else {
+		/*
+		 * The error has been noticed early enough, no child has
+		 * been run
+		 */
+		ATF_REQUIRE(err == ENOENT);
+	}
+	posix_spawn_file_actions_destroy(&fa);
+}
+
+ATF_TC(t_spawn_open_nonexistent_diag);
+
+ATF_TC_HEAD(t_spawn_open_nonexistent_diag, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "posix_spawn fails when a file to open does not exist "
+	    "and delivers proper diagnostic");
+	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
+}
+
+ATF_TC_BODY(t_spawn_open_nonexistent_diag, tc)
+{
+	int err;
+	pid_t pid;
+	char * const args[2] = { __UNCONST("cat"), NULL };
+	posix_spawnattr_t attr;
+	posix_spawn_file_actions_t fa;
+
+	posix_spawnattr_init(&attr);
+	/*
+	 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
+	 * will cause a "proper" return value from posix_spawn(2)
+	 * instead of a (potential) success there and a 127 exit
+	 * status from the child process (c.f. the non-diag variant
+	 * of this test).
+	 */
+	posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR);
+	posix_spawn_file_actions_init(&fa);
+	posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
+	    "./non/ex/ist/ent", O_RDONLY, 0);
+	err = posix_spawn(&pid, "/bin/cat", &fa, &attr, args, NULL);
 	ATF_REQUIRE(err == ENOENT);
 	posix_spawn_file_actions_destroy(&fa);
+	posix_spawnattr_destroy(&attr);
 }
 
 ATF_TC(t_spawn_fileactions);
@@ -327,6 +377,7 @@ ATF_TP_ADD_TCS(tp)
 {
 	ATF_TP_ADD_TC(tp, t_spawn_fileactions);
 	ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent);
+	ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent_diag);
 	ATF_TP_ADD_TC(tp, t_spawn_reopen);
 	ATF_TP_ADD_TC(tp, t_spawn_openmode);
 	ATF_TP_ADD_TC(tp, t_spawn_empty_fileactions);

Reply via email to