diff options
-rw-r--r-- | share/man/man9/Makefile | 5 | ||||
-rw-r--r-- | share/man/man9/fork1.9 | 76 | ||||
-rw-r--r-- | share/man/man9/thread_fork.9 | 109 | ||||
-rw-r--r-- | sys/arch/alpha/alpha/vm_machdep.c | 33 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/vm_machdep.c | 12 | ||||
-rw-r--r-- | sys/arch/arm/arm/vm_machdep.c | 38 | ||||
-rw-r--r-- | sys/arch/arm64/arm64/vm_machdep.c | 10 | ||||
-rw-r--r-- | sys/arch/hppa/hppa/vm_machdep.c | 8 | ||||
-rw-r--r-- | sys/arch/i386/i386/vm_machdep.c | 13 | ||||
-rw-r--r-- | sys/arch/m88k/m88k/vm_machdep.c | 19 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/vm_machdep.c | 16 | ||||
-rw-r--r-- | sys/arch/powerpc/powerpc/vm_machdep.c | 10 | ||||
-rw-r--r-- | sys/arch/sh/sh/vm_machdep.c | 29 | ||||
-rw-r--r-- | sys/arch/sparc64/sparc64/vm_machdep.c | 34 | ||||
-rw-r--r-- | sys/kern/init_main.c | 5 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 401 | ||||
-rw-r--r-- | sys/kern/kern_kthread.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_sched.c | 4 | ||||
-rw-r--r-- | sys/sys/proc.h | 12 | ||||
-rw-r--r-- | sys/uvm/uvm_extern.h | 4 |
20 files changed, 479 insertions, 363 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index bf0a01351e4..8de8c6b5d87 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.285 2017/01/24 00:58:55 mpi Exp $ +# $OpenBSD: Makefile,v 1.286 2017/02/12 04:55:08 guenther Exp $ # $NetBSD: Makefile,v 1.4 1996/01/09 03:23:01 thorpej Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -34,7 +34,8 @@ MAN= aml_evalnode.9 atomic_add_int.9 atomic_cas_uint.9 \ sensor_attach.9 \ spl.9 srp_enter.9 srpl_rc_init.9 startuphook_establish.9 \ socreate.9 sosplice.9 style.9 syscall.9 sysctl_int.9 \ - task_add.9 tc_init.9 tfind.9 time_second.9 timeout.9 tsleep.9 tvtohz.9 \ + task_add.9 tc_init.9 tfind.9 thread_fork.9 \ + time_second.9 timeout.9 tsleep.9 tvtohz.9 \ uiomove.9 uvm.9 usb_add_task.9 usbd_close_pipe.9 usbd_open_pipe.9 \ usbd_ref_wait.9 usbd_transfer.9 vfs.9 vfs_busy.9 \ vfs_cache.9 vaccess.9 vclean.9 vcount.9 vdevgone.9 vfinddev.9 vflush.9 \ diff --git a/share/man/man9/fork1.9 b/share/man/man9/fork1.9 index 82a8f8f46a7..c22c9800aa4 100644 --- a/share/man/man9/fork1.9 +++ b/share/man/man9/fork1.9 @@ -1,4 +1,4 @@ -.\" $OpenBSD: fork1.9,v 1.28 2017/01/24 07:59:58 jmc Exp $ +.\" $OpenBSD: fork1.9,v 1.29 2017/02/12 04:55:08 guenther Exp $ .\" $NetBSD: fork1.9,v 1.3 1999/03/16 00:40:47 garbled Exp $ .\" .\" Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: January 24 2017 $ +.Dd $Mdocdate: February 12 2017 $ .Dt FORK1 9 .Os .Sh NAME @@ -42,8 +42,6 @@ .Fo fork1 .Fa "struct proc *p1" .Fa "int flags" -.Fa "void *stack" -.Fa "pid_t *tidptr" .Fa "void (*func)(void *)" .Fa "void *arg" .Fa "register_t *retval" @@ -53,10 +51,10 @@ .Fn fork1 creates a new process out of .Ar p1 , -which should be the current process. +which should be the current thread. This function is used primarily to implement the -.Xr fork 2 , -.Xr __tfork 3 , +.Xr fork 2 +and .Xr vfork 2 system calls, as well as the .Xr kthread_create 9 @@ -77,11 +75,6 @@ The call is done by the .Xr vfork 2 system call. Used only for statistics. -.It Dv FORK_TFORK -The call is done by the -.Xr __tfork 3 -system call. -Used only for statistics. .It Dv FORK_PPWAIT Suspend the parent process until the child is terminated (by calling .Xr _exit 2 @@ -93,7 +86,7 @@ fdshare(). The default behavior is to copy the table through fdcopy(). .It Dv FORK_IDLE -The child will be left in the +The new thread will be left in the .Dv SIDL state. The default behavior is to make it runnable and add it to the run queue. @@ -116,62 +109,37 @@ must also be set. .It Dv FORK_PTRACE The child will start with tracing enabled, as if ptrace(PT_TRACE_ME, 0, 0, 0) had been invoked in the child. -.It Dv FORK_THREAD -The child will instead be a kernel-level thread in the same process -as the parent. -.Dv FORK_SHAREFILES , -.Dv FORK_SHAREVM , -and -.Dv FORK_SIGHAND -must also be set. .El .Pp If -.Fa stack +.Fa func is not .Dv NULL , -it will be used as the initial value of the child's stack pointer, -instead of using the child's copy of the parent's stack. +the new thread will begin execution by calling this function. +It defaults to child_return, which returns to userland. .Pp If -.Fa tidptr +.Fa arg is not .Dv NULL , -the PID of the child process will be written there in the parent -on success. -This is guaranteed to be done before -the child process is started. +it is the argument to the previous function. +It defaults to a pointer to the new thread. .Pp If .Fa retval is not .Dv NULL , -it will hold the following values after successful completion -of the fork operation: -.Bl -tag -width retval[0] -.It Fa retval[0] -This will contain the PID of the child process. -.It Fa retval[1] -In the parent process, this will contain the value 0. -In the child process, this will contain 1. -.El +the PID of the child process will be stored in +.Fa *retval +on successful completion. .Pp If -.Fa func +.Fa rnewprocp is not .Dv NULL , -the new process will begin execution by calling this function. -It defaults to child_return, which returns to userland. -.Pp -If -.Fa arg -is not -.Dv NULL , -it is the argument to the previous function. -It defaults to a pointer to the new process. -.Pp -The newly created process is returned through -.Fa *rnewprocp . +the newly created thread is stored in +.Fa *rnewprocp +on successful completion. .Sh RETURN VALUES Upon successful completion of the fork operation, .Fn fork1 @@ -179,18 +147,20 @@ returns 0. Otherwise, the following error values are returned: .Bl -tag -width [EAGAIN] .It Bq Er EAGAIN -The limit on the total number of system processes would be exceeded. +The system limits on the total number of threads or processes would +be exceeded. .It Bq Er EAGAIN The limit .Dv RLIMIT_NPROC on the total number of processes under execution by this user id would be exceeded. +.It Bq Er ENOMEM +There is insufficient swap space for the new thread. .El .Sh SEE ALSO .Xr execve 2 , .Xr fork 2 , .Xr vfork 2 , -.Xr __tfork 3 , .Xr kthread_create 9 , .Xr psignal 9 , .Xr tfind 9 diff --git a/share/man/man9/thread_fork.9 b/share/man/man9/thread_fork.9 new file mode 100644 index 00000000000..7d0e2991799 --- /dev/null +++ b/share/man/man9/thread_fork.9 @@ -0,0 +1,109 @@ +.\" $OpenBSD: thread_fork.9,v 1.1 2017/02/12 04:55:08 guenther Exp $ +.\" $NetBSD: fork1.9,v 1.3 1999/03/16 00:40:47 garbled Exp $ +.\" +.\" Copyright (c) 1998 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, +.\" NASA Ames Research Center. +.\" +.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd $Mdocdate: February 12 2017 $ +.Dt THREAD_FORK 9 +.Os +.Sh NAME +.Nm thread_fork +.Nd create a new thread inside a process +.Sh SYNOPSIS +.In sys/types.h +.In sys/proc.h +.Ft int +.Fo thread_fork +.Fa "struct proc *p1" +.Fa "void *stack" +.Fa "void *tcb" +.Fa "pid_t *tidptr" +.Fa "register_t *retval" +.Fc +.Sh DESCRIPTION +.Fn thread_fork +creates a new thread out of +.Ar p1 , +which should be the current thread. +This function is used to implement the +.Xr __tfork 3 +system call. +.Pp +.Fa stack , +which must not be +.Dv NULL , +will be used as the initial value of the new thread's stack pointer. +.Pp +If +.Fa tcb , +is not +.Dv NULL , +it will be used as the initial address of the new thread's TCB +(thread control block). +.Pp +If +.Fa tidptr +is not +.Dv NULL , +the TID of the new thread will be copied out there on success. +This is guaranteed to be done before the new thread is started. +.Pp +.Fa retval , +which must not be +.Dv NULL , +the TID of the new thread will be stored in +.Fa *retval +on successful completion. +.Sh RETURN VALUES +Upon successful completion of the operation, +.Fn thread_fork +returns 0. +Otherwise, the following error values are returned: +.Bl -tag -width [EAGAIN] +.It Bq Er EAGAIN +The system limit on the total number of threads would be exceeded. +.It Bq Er ENOMEM +There is insufficient swap space for the new thread. +.It Bq Er EINVAL +The +.Fa stack +argument was +.Dv NULL . +.El +.Sh SEE ALSO +.Xr fork 2 , +.Xr __get_tcb 3 , +.Xr __tfork 3 , +.Xr tfind 9 +.Sh HISTORY +The +.Fn thread_fork +function +appeared in +.Ox 6.1 . diff --git a/sys/arch/alpha/alpha/vm_machdep.c b/sys/arch/alpha/alpha/vm_machdep.c index 7ff834b9ec4..48f6ec11f2a 100644 --- a/sys/arch/alpha/alpha/vm_machdep.c +++ b/sys/arch/alpha/alpha/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.45 2016/10/04 05:55:09 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.46 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.55 2000/03/29 03:49:48 simonb Exp $ */ /* @@ -71,24 +71,19 @@ cpu_exit(p) * Copy and update the pcb and trap frame, making the child ready to run. * * Rig the child's kernel stack so that it will start out in - * switch_trampoline() and call child_return() with p2 as an - * argument. This causes the newly-created child process to go - * directly to user level with an apparent return value of 0 from - * fork(), while the parent process returns normally. + * proc_trampoline() and call 'func' with 'arg' as an argument. + * For normal processes this is child_return(), which causes the + * child to go directly to user level with an apparent return value + * of 0 from fork(), while the parent process returns normally. + * For kernel threads this will be a function that never return. * - * p1 is the process being forked; - * - * If an alternate user-level stack is requested (with non-NULL stack arg), - * set up the user stack pointer - * accordingly. + * An alternate user-level stack or TCB can be requested by passing + * a non-NULL value; these are poked into the PCB so they're in + * effect at the initial return to userspace. */ void -cpu_fork(p1, p2, stack, stacksize, func, arg) - struct proc *p1, *p2; - void *stack; - size_t stacksize; - void (*func)(void *); - void *arg; +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, + void (*func)(void *), void *arg) { struct user *up = p2->p_addr; @@ -115,15 +110,17 @@ cpu_fork(p1, p2, stack, stacksize, func, arg) /* * Copy pcb and stack from proc p1 to p2. - * If specified, give the child a different stack. + * If specified, give the child a different stack and/or TCB. * We do this as cheaply as possible, copying only the active * part of the stack. The stack and pcb need to agree; */ up->u_pcb = p1->p_addr->u_pcb; if (stack != NULL) - up->u_pcb.pcb_hw.apcb_usp = (u_long)stack + stacksize; + up->u_pcb.pcb_hw.apcb_usp = (u_long)stack; else up->u_pcb.pcb_hw.apcb_usp = alpha_pal_rdusp(); + if (tcb != NULL) + up->u_pcb.pcb_hw.apcb_unique = (unsigned long)tcb; /* * Arrange for a non-local goto when the new process diff --git a/sys/arch/amd64/amd64/vm_machdep.c b/sys/arch/amd64/amd64/vm_machdep.c index 2b402e5ec74..42dc47f92ea 100644 --- a/sys/arch/amd64/amd64/vm_machdep.c +++ b/sys/arch/amd64/amd64/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.35 2016/04/03 19:49:35 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.36 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.1 2003/04/26 18:39:33 fvdl Exp $ */ /*- @@ -66,10 +66,10 @@ void setredzone(struct proc *); * Finish a fork operation, with process p2 nearly set up. * Copy and update the kernel stack and pcb, making the child * ready to run, and marking it so that it can return differently - * than the parent. Returns 1 in the child process, 0 in the parent. + * than the parent. */ void -cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void (*func)(void *), void *arg) { struct pcb *pcb = &p2->p_addr->u_pcb; @@ -112,10 +112,12 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, setredzone(p2); /* - * If specified, give the child a different stack. + * If specified, give the child a different stack and/or TCB */ if (stack != NULL) - tf->tf_rsp = (u_int64_t)stack + stacksize; + tf->tf_rsp = (u_int64_t)stack; + if (tcb != NULL) + pcb->pcb_fsbase = (u_int64_t)tcb; sf = (struct switchframe *)tf - 1; sf->sf_r12 = (u_int64_t)func; diff --git a/sys/arch/arm/arm/vm_machdep.c b/sys/arch/arm/arm/vm_machdep.c index 38dba6d7d77..9b2a00a92f5 100644 --- a/sys/arch/arm/arm/vm_machdep.c +++ b/sys/arch/arm/arm/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.18 2016/07/31 09:18:01 jsg Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.19 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.31 2004/01/04 11:33:29 jdolecek Exp $ */ /* @@ -80,24 +80,21 @@ extern void proc_trampoline (void); /* * Finish a fork operation, with process p2 nearly set up. * Copy and update the pcb and trap frame, making the child ready to run. - * - * Rig the child's kernel stack so that it will start out in - * proc_trampoline() and call child_return() with p2 as an - * argument. This causes the newly-created child process to go - * directly to user level with an apparent return value of 0 from - * fork(), while the parent process returns normally. * - * p1 is the process being forked; if p1 == &proc0, we are creating - * a kernel thread, and the return path and argument are specified with - * `func' and `arg'. + * Rig the child's kernel stack so that it will start out in + * proc_trampoline() and call 'func' with 'arg' as an argument. + * For normal processes this is child_return(), which causes the + * child to go directly to user level with an apparent return value + * of 0 from fork(), while the parent process returns normally. + * For kernel threads this will be a function that never return. * - * If an alternate user-level stack is requested (with non-zero values - * in both the stack and stacksize args), set up the user stack pointer - * accordingly. + * An alternate user-level stack or TCB can be requested by passing + * a non-NULL value; these are poked into the PCB so they're in + * effect at the initial return to userspace. */ void -cpu_fork(struct proc *p1, struct proc *p2, void *stack, - size_t stacksize, void (*func) (void *), void *arg) +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, + void (*func)(void *), void *arg) { struct pcb *pcb = (struct pcb *)&p2->p_addr->u_pcb; struct trapframe *tf; @@ -130,16 +127,17 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, pmap_activate(p2); - p2->p_addr->u_pcb.pcb_tf = tf = - (struct trapframe *)pcb->pcb_un.un_32.pcb32_sp - 1; + pcb->pcb_tf = tf = (struct trapframe *)pcb->pcb_un.un_32.pcb32_sp - 1; *tf = *p1->p_addr->u_pcb.pcb_tf; /* - * If specified, give the child a different stack (make sure - * it's 8-byte aligned). + * If specified, give the child a different stack and/or TCB. + * Enforce 8-byte alignment on the stack. */ if (stack != NULL) - tf->tf_usr_sp = ((vaddr_t)(stack) + stacksize) & -8; + tf->tf_usr_sp = (vaddr_t)stack & -8; + if (tcb != NULL) + p2->p_addr->u_pcb.pcb_tcb = tcb; sf = (struct switchframe *)tf - 1; sf->sf_r4 = (u_int)func; diff --git a/sys/arch/arm64/arm64/vm_machdep.c b/sys/arch/arm64/arm64/vm_machdep.c index c2e42e53234..3a879b9dc26 100644 --- a/sys/arch/arm64/arm64/vm_machdep.c +++ b/sys/arch/arm64/arm64/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.1 2016/12/17 23:38:33 patrick Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.2 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.1 2003/04/26 18:39:33 fvdl Exp $ */ /*- @@ -66,7 +66,7 @@ * than the parent. Returns 1 in the child process, 0 in the parent. */ void -cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void (*func)(void *), void *arg) { struct pcb *pcb = (struct pcb *)&p2->p_addr->u_pcb; @@ -86,11 +86,13 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, - 0x10); tf = (struct trapframe *)STACKALIGN(tf); - p2->p_addr->u_pcb.pcb_tf = tf; + pcb->pcb_tf = tf; *tf = *p1->p_addr->u_pcb.pcb_tf; if (stack != NULL) - tf->tf_sp = STACKALIGN((u_int)(stack) + stacksize); + tf->tf_sp = STACKALIGN((u_int)(stack)); + if (tcb != NULL) + pcb->pcb_tcb = tcb; sf = (struct switchframe *)tf - 1; sf->sf_x19 = (uint64_t)func; diff --git a/sys/arch/hppa/hppa/vm_machdep.c b/sys/arch/hppa/hppa/vm_machdep.c index 11bceafc94c..eb517d2def7 100644 --- a/sys/arch/hppa/hppa/vm_machdep.c +++ b/sys/arch/hppa/hppa/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.81 2015/05/05 02:13:46 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.82 2017/02/12 04:55:08 guenther Exp $ */ /* * Copyright (c) 1999-2004 Michael Shalayeff @@ -49,7 +49,7 @@ extern struct pool hppa_fppl; void -cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void (*func)(void *), void *arg) { struct pcb *pcbp; @@ -102,10 +102,12 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, (curcpu()->ci_psw & PSL_O); /* - * If specified, give the child a different stack. + * If specified, give the child a different stack and/or TCB */ if (stack != NULL) setstack(tf, (u_long)stack, 0); /* XXX ignore error? */ + if (tcb != NULL) + tf->tf_cr27 = (u_long)tcb; /* * Build stack frames for the cpu_switchto & co. diff --git a/sys/arch/i386/i386/vm_machdep.c b/sys/arch/i386/i386/vm_machdep.c index 49901d20f3e..182795c274f 100644 --- a/sys/arch/i386/i386/vm_machdep.c +++ b/sys/arch/i386/i386/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.64 2016/04/03 17:45:30 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.65 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.61 1996/05/03 19:42:35 christos Exp $ */ /*- @@ -67,12 +67,9 @@ * Copy and update the kernel stack and pcb, making the child * ready to run, and marking it so that it can return differently * than the parent. Returns 1 in the child process, 0 in the parent. - * We currently double-map the user area so that the stack is at the same - * address in each process; in the future we will probably relocate - * the frame pointers on the stack after copying. */ void -cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void (*func)(void *), void *arg) { struct pcb *pcb = &p2->p_addr->u_pcb; @@ -104,10 +101,12 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, *tf = *p1->p_md.md_regs; /* - * If specified, give the child a different stack. + * If specified, give the child a different stack and/or TCB */ if (stack != NULL) - tf->tf_esp = (u_int)stack + stacksize; + tf->tf_esp = (u_int)stack; + if (tcb != NULL) + i386_set_threadbase(p2, (uint32_t)tcb, TSEG_GS); sf = (struct switchframe *)tf - 1; sf->sf_esi = (int)func; diff --git a/sys/arch/m88k/m88k/vm_machdep.c b/sys/arch/m88k/m88k/vm_machdep.c index d4cb331f49b..c247482c54f 100644 --- a/sys/arch/m88k/m88k/vm_machdep.c +++ b/sys/arch/m88k/m88k/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.24 2015/05/05 02:13:47 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.25 2017/02/12 04:55:08 guenther Exp $ */ /* * Copyright (c) 1998 Steve Murphree, Jr. @@ -67,18 +67,11 @@ extern void switch_exit(struct proc *); * Copy and update the kernel stack and pcb, making the child * ready to run, and marking it so that it can return differently * than the parent. Returns 1 in the child process, 0 in the parent. - * We currently double-map the user area so that the stack is at the same - * address in each process; in the future we will probably relocate - * the frame pointers on the stack after copying. */ void -cpu_fork(p1, p2, stack, stacksize, func, arg) - struct proc *p1, *p2; - void *stack; - size_t stacksize; - void (*func)(void *); - void *arg; +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, + void (*func)(void *), void *arg) { struct ksigframe { void (*func)(void *); @@ -100,10 +93,12 @@ cpu_fork(p1, p2, stack, stacksize, func, arg) p2->p_md.md_tf = (struct trapframe *)USER_REGS(p2); /* - * If specified, give the child a different stack. + * If specified, give the child a different stack and/or TCB. */ if (stack != NULL) - USER_REGS(p2)->r[31] = (u_int)stack + stacksize; + USER_REGS(p2)->r[31] = (u_int)stack; + if (tcb != NULL) + USER_REGS(p2)->r[27] = (u_int)tcb; ksfp = (struct ksigframe *)((char *)p2->p_addr + USPACE) - 1; ksfp->func = func; diff --git a/sys/arch/mips64/mips64/vm_machdep.c b/sys/arch/mips64/mips64/vm_machdep.c index f49bed30773..366d7c6969a 100644 --- a/sys/arch/mips64/mips64/vm_machdep.c +++ b/sys/arch/mips64/mips64/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.33 2016/03/06 19:42:27 mpi Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.34 2017/02/12 04:55:08 guenther Exp $ */ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1992, 1993 @@ -61,12 +61,8 @@ extern void proc_trampoline(void); * Finish a fork operation, with process p2 nearly set up. */ void -cpu_fork(p1, p2, stack, stacksize, func, arg) - struct proc *p1, *p2; - void *stack; - size_t stacksize; - void (*func)(void *); - void *arg; +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, + void (*func)(void *), void *arg) { struct cpu_info *ci = curcpu(); struct pcb *pcb; @@ -117,10 +113,12 @@ cpu_fork(p1, p2, stack, stacksize, func, arg) p2->p_md.md_regs = &p2->p_addr->u_pcb.pcb_regs; /* - * If specified, give the child a different stack. + * If specified, give the child a different stack and/or TCB. */ if (stack != NULL) - p2->p_md.md_regs->sp = (u_int64_t)stack + stacksize; + p2->p_md.md_regs->sp = (u_int64_t)stack; + if (tcb != NULL) + TCB_SET(p2, tcb); /* * Copy the process control block to the new proc and diff --git a/sys/arch/powerpc/powerpc/vm_machdep.c b/sys/arch/powerpc/powerpc/vm_machdep.c index 43e18f7401e..fc64451a15c 100644 --- a/sys/arch/powerpc/powerpc/vm_machdep.c +++ b/sys/arch/powerpc/powerpc/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.48 2015/05/05 02:13:47 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.49 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.1 1996/09/30 16:34:57 ws Exp $ */ /* @@ -49,7 +49,7 @@ * Finish a fork operation, with process p2 nearly set up. */ void -cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void (*func)(void *), void *arg) { struct trapframe *tf; @@ -92,7 +92,11 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, */ if (stack != NULL) { tf = trapframe(p2); - tf->fixreg[1] = (register_t)stack + stacksize; + tf->fixreg[1] = (register_t)stack; + } + if (tcb != NULL) { + tf = trapframe(p2); + tf->fixreg[2] = (register_t)tcb; } stktop2 = (caddr_t)((u_long)stktop2 & ~15); /* Align stack pointer */ diff --git a/sys/arch/sh/sh/vm_machdep.c b/sys/arch/sh/sh/vm_machdep.c index 77b50487b5b..16645ebfd0d 100644 --- a/sys/arch/sh/sh/vm_machdep.c +++ b/sys/arch/sh/sh/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.13 2015/05/05 02:13:47 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.14 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.53 2006/08/31 16:49:21 matt Exp $ */ /* @@ -120,23 +120,20 @@ extern void proc_trampoline(void); /* * Finish a fork operation, with process p2 nearly set up. * Copy and update the pcb and trap frame, making the child ready to run. - * + * * Rig the child's kernel stack so that it will start out in - * proc_trampoline() and call child_return() with p2 as an - * argument. This causes the newly-created child process to go - * directly to user level with an apparent return value of 0 from - * fork(), while the parent process returns normally. - * - * p1 is the process being forked; if p1 == &proc0, we are creating - * a kernel thread, and the return path and argument are specified with - * `func' and `arg'. + * proc_trampoline() and call 'func' with 'arg' as an argument. + * For normal processes this is child_return(), which causes the + * child to go directly to user level with an apparent return value + * of 0 from fork(), while the parent process returns normally. + * For kernel threads this will be a function that never return. * - * If an alternate user-level stack is requested (with non-zero values - * in both the stack and stacksize args), set up the user stack pointer - * accordingly. + * An alternate user-level stack or TCB can be requested by passing + * a non-NULL value; these are poked into the PCB so they're in + * effect at the initial return to userspace. */ void -cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void (*func)(void *), void *arg) { struct pcb *pcb; @@ -213,7 +210,9 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize, * If specified, give the child a different stack. */ if (stack != NULL) - tf->tf_r15 = (u_int)stack + stacksize; + tf->tf_r15 = (int)stack; + if (tcb != NULL) + tf->tf_gbr = (int)tcb; /* Setup switch frame */ sf = &pcb->pcb_sf; diff --git a/sys/arch/sparc64/sparc64/vm_machdep.c b/sys/arch/sparc64/sparc64/vm_machdep.c index c30e79a42a7..18e012ed879 100644 --- a/sys/arch/sparc64/sparc64/vm_machdep.c +++ b/sys/arch/sparc64/sparc64/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.35 2015/11/06 06:33:26 guenther Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.36 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: vm_machdep.c,v 1.38 2001/06/30 00:02:20 eeh Exp $ */ /* @@ -153,26 +153,19 @@ char cpu_forkname[] = "cpu_fork()"; * Copy and update the pcb and trap frame, making the child ready to run. * * Rig the child's kernel stack so that it will start out in - * proc_trampoline() and call child_return() with p2 as an - * argument. This causes the newly-created child process to go - * directly to user level with an apparent return value of 0 from - * fork(), while the parent process returns normally. + * proc_trampoline() and call 'func' with 'arg' as an argument. + * For normal processes this is child_return(), which causes the + * child to go directly to user level with an apparent return value + * of 0 from fork(), while the parent process returns normally. + * For kernel threads this will be a function that never return. * - * p1 is the process being forked; if p1 == &proc0, we are creating - * a kernel thread, and the return path and argument are specified with - * `func' and `arg'. - * - * If an alternate user-level stack is requested (with non-zero values - * in both the stack and stacksize args), set up the user stack pointer - * accordingly. + * An alternate user-level stack or TCB can be requested by passing + * a non-NULL value; these are poked into the PCB so they're in + * effect at the initial return to userspace. */ void -cpu_fork(p1, p2, stack, stacksize, func, arg) - struct proc *p1, *p2; - void *stack; - size_t stacksize; - void (*func)(void *); - void *arg; +cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, + void (*func)(void *), void *arg) { struct pcb *opcb = &p1->p_addr->u_pcb; struct pcb *npcb = &p2->p_addr->u_pcb; @@ -240,10 +233,11 @@ cpu_fork(p1, p2, stack, stacksize, func, arg) * with space reserved for the frame, and zero the frame pointer. */ if (stack != NULL) { - tf2->tf_out[6] = (u_int64_t)(u_long)stack + stacksize - - (BIAS + CC64FSZ); + tf2->tf_out[6] = (u_int64_t)(u_long)stack - (BIAS + CC64FSZ); tf2->tf_in[6] = 0; } + if (tcb != NULL) + tf2->tf_global[7] = (u_int64_t)tcb; /* Construct kernel frame to return to in cpu_switch() */ rp = (struct rwindow *)((u_long)npcb + TOPFRAMEOFF); diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index e21a8306854..faa74aa4244 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init_main.c,v 1.265 2017/01/21 05:42:03 guenther Exp $ */ +/* $OpenBSD: init_main.c,v 1.266 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */ /* @@ -437,8 +437,7 @@ main(void *framep) { struct proc *initproc; - if (fork1(p, FORK_FORK, NULL, 0, start_init, NULL, NULL, - &initproc)) + if (fork1(p, FORK_FORK, start_init, NULL, NULL, &initproc)) panic("fork init"); initprocess = initproc->p_p; } diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 867a12bdd92..38c1be4a981 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_fork.c,v 1.194 2017/02/08 20:58:30 guenther Exp $ */ +/* $OpenBSD: kern_fork.c,v 1.195 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */ /* @@ -75,12 +75,13 @@ int randompid; /* when set to 1, pid's go random */ struct forkstat forkstat; void fork_return(void *); -void tfork_child_return(void *); pid_t alloctid(void); pid_t allocpid(void); int ispidtaken(pid_t); -void process_new(struct proc *, struct process *, int); +struct proc *thread_new(struct proc *_parent, vaddr_t _uaddr); +struct process *process_new(struct proc *, struct process *, int); +int fork_check_maxthread(uid_t _uid); void fork_return(void *arg) @@ -101,14 +102,14 @@ sys_fork(struct proc *p, void *v, register_t *retval) flags = FORK_FORK; if (p->p_p->ps_ptmask & PTRACE_FORK) flags |= FORK_PTRACE; - return (fork1(p, flags, NULL, 0, fork_return, NULL, retval, NULL)); + return fork1(p, flags, fork_return, NULL, retval, NULL); } int sys_vfork(struct proc *p, void *v, register_t *retval) { - return (fork1(p, FORK_VFORK|FORK_PPWAIT, NULL, 0, NULL, - NULL, retval, NULL)); + return fork1(p, FORK_VFORK|FORK_PPWAIT, child_return, NULL, + retval, NULL); } int @@ -120,32 +121,58 @@ sys___tfork(struct proc *p, void *v, register_t *retval) } */ *uap = v; size_t psize = SCARG(uap, psize); struct __tfork param = { 0 }; - int flags; int error; if (psize == 0 || psize > sizeof(param)) - return (EINVAL); + return EINVAL; if ((error = copyin(SCARG(uap, param), ¶m, psize))) - return (error); + return error; #ifdef KTRACE if (KTRPOINT(p, KTR_STRUCT)) ktrstruct(p, "tfork", ¶m, sizeof(param)); #endif - flags = FORK_TFORK | FORK_THREAD | FORK_SIGHAND | FORK_SHAREVM - | FORK_SHAREFILES; - - return (fork1(p, flags, param.tf_stack, param.tf_tid, - tfork_child_return, param.tf_tcb, retval, NULL)); + return thread_fork(p, param.tf_stack, param.tf_tcb, param.tf_tid, + retval); } -void -tfork_child_return(void *arg) +/* + * Allocate and initialize a thread (proc) structure, given the parent thread. + */ +struct proc * +thread_new(struct proc *parent, vaddr_t uaddr) { - struct proc *p = curproc; + struct proc *p; - TCB_SET(p, arg); - child_return(p); + p = pool_get(&proc_pool, PR_WAITOK); + p->p_stat = SIDL; /* protect against others */ + p->p_flag = 0; + + /* + * Make a proc table entry for the new process. + * Start by zeroing the section of proc that is zero-initialized, + * then copy the section that is copied directly from the parent. + */ + memset(&p->p_startzero, 0, + (caddr_t)&p->p_endzero - (caddr_t)&p->p_startzero); + memcpy(&p->p_startcopy, &parent->p_startcopy, + (caddr_t)&p->p_endcopy - (caddr_t)&p->p_startcopy); + crhold(p->p_ucred); + p->p_addr = (struct user *)uaddr; + + /* + * Initialize the timeouts. + */ + timeout_set(&p->p_sleep_to, endtsleep, p); + + /* + * set priority of child to be that of parent + * XXX should move p_estcpu into the region of struct proc which gets + * copied. + */ + scheduler_fork_hook(parent, p); + + return p; } /* @@ -175,7 +202,7 @@ process_initialize(struct process *pr, struct proc *p) /* * Allocate and initialize a new process. */ -void +struct process * process_new(struct proc *p, struct process *parent, int flags) { struct process *pr; @@ -246,36 +273,16 @@ process_new(struct proc *p, struct process *parent, int flags) /* it's sufficiently inited to be globally visible */ LIST_INSERT_HEAD(&allprocess, pr, ps_list); + + return pr; } /* print the 'table full' message once per 10 seconds */ struct timeval fork_tfmrate = { 10, 0 }; int -fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, - void (*func)(void *), void *arg, register_t *retval, - struct proc **rnewprocp) +fork_check_maxthread(uid_t uid) { - struct process *curpr = curp->p_p; - struct process *pr; - struct proc *p; - uid_t uid; - struct vmspace *vm; - int count; - vaddr_t uaddr; - int s; - struct ptrace_state *newptstat = NULL; - - /* sanity check some flag combinations */ - if (flags & FORK_THREAD) { - if ((flags & FORK_SHAREFILES) == 0 || - (flags & FORK_SIGHAND) == 0 || - (flags & FORK_SYSTEM) != 0) - return (EINVAL); - } - if (flags & FORK_SIGHAND && (flags & FORK_SHAREVM) == 0) - return (EINVAL); - /* * Although process entries are dynamically created, we still keep * a global limit on the maximum number we will create. We reserve @@ -285,48 +292,80 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, * the variable nthreads is the current number of procs, maxthread is * the limit. */ - uid = curp->p_ucred->cr_ruid; if ((nthreads >= maxthread - 5 && uid != 0) || nthreads >= maxthread) { static struct timeval lasttfm; if (ratecheck(&lasttfm, &fork_tfmrate)) tablefull("proc"); - return (EAGAIN); + return EAGAIN; } nthreads++; - if ((flags & FORK_THREAD) == 0) { - if ((nprocesses >= maxprocess - 5 && uid != 0) || - nprocesses >= maxprocess) { - static struct timeval lasttfm; + return 0; +} - if (ratecheck(&lasttfm, &fork_tfmrate)) - tablefull("process"); - nthreads--; - return (EAGAIN); - } - nprocesses++; +static inline void +fork_thread_start(struct proc *p, struct proc *parent, int flags) +{ + int s; - /* - * Increment the count of processes running with - * this uid. Don't allow a nonprivileged user to - * exceed their current limit. - */ - count = chgproccnt(uid, 1); - if (uid != 0 && count > curp->p_rlimit[RLIMIT_NPROC].rlim_cur) { - (void)chgproccnt(uid, -1); - nprocesses--; - nthreads--; - return (EAGAIN); - } + SCHED_LOCK(s); + p->p_stat = SRUN; + p->p_cpu = sched_choosecpu_fork(parent, flags); + setrunqueue(p); + SCHED_UNLOCK(s); +} + +int +fork1(struct proc *curp, int flags, void (*func)(void *), void *arg, + register_t *retval, struct proc **rnewprocp) +{ + struct process *curpr = curp->p_p; + struct process *pr; + struct proc *p; + uid_t uid = curp->p_ucred->cr_ruid; + struct vmspace *vm; + int count; + vaddr_t uaddr; + int error; + struct ptrace_state *newptstat = NULL; + + KASSERT((flags & ~(FORK_FORK | FORK_VFORK | FORK_PPWAIT | FORK_PTRACE + | FORK_IDLE | FORK_SHAREVM | FORK_SHAREFILES | FORK_NOZOMBIE + | FORK_SYSTEM | FORK_SIGHAND)) == 0); + KASSERT((flags & FORK_SIGHAND) == 0 || (flags & FORK_SHAREVM)); + KASSERT(func != NULL); + + if ((error = fork_check_maxthread(uid))) + return error; + + if ((nprocesses >= maxprocess - 5 && uid != 0) || + nprocesses >= maxprocess) { + static struct timeval lasttfm; + + if (ratecheck(&lasttfm, &fork_tfmrate)) + tablefull("process"); + nthreads--; + return EAGAIN; + } + nprocesses++; + + /* + * Increment the count of processes running with this uid. + * Don't allow a nonprivileged user to exceed their current limit. + */ + count = chgproccnt(uid, 1); + if (uid != 0 && count > curp->p_rlimit[RLIMIT_NPROC].rlim_cur) { + (void)chgproccnt(uid, -1); + nprocesses--; + nthreads--; + return EAGAIN; } uaddr = uvm_uarea_alloc(); if (uaddr == 0) { - if ((flags & FORK_THREAD) == 0) { - (void)chgproccnt(uid, -1); - nprocesses--; - } + (void)chgproccnt(uid, -1); + nprocesses--; nthreads--; return (ENOMEM); } @@ -334,37 +373,9 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, /* * From now on, we're committed to the fork and cannot fail. */ + p = thread_new(curp, uaddr); + pr = process_new(p, curpr, flags); - /* Allocate new proc. */ - p = pool_get(&proc_pool, PR_WAITOK); - - p->p_stat = SIDL; /* protect against others */ - p->p_flag = 0; - - /* - * Make a proc table entry for the new process. - * Start by zeroing the section of proc that is zero-initialized, - * then copy the section that is copied directly from the parent. - */ - memset(&p->p_startzero, 0, - (caddr_t)&p->p_endzero - (caddr_t)&p->p_startzero); - memcpy(&p->p_startcopy, &curp->p_startcopy, - (caddr_t)&p->p_endcopy - (caddr_t)&p->p_startcopy); - crhold(p->p_ucred); - - /* - * Initialize the timeouts. - */ - timeout_set(&p->p_sleep_to, endtsleep, p); - - if (flags & FORK_THREAD) { - atomic_setbits_int(&p->p_flag, P_THREAD); - p->p_p = pr = curpr; - pr->ps_refcnt++; - } else { - process_new(p, curpr, flags); - pr = p->p_p; - } p->p_fd = pr->ps_fd; p->p_vmspace = pr->ps_vmspace; if (pr->ps_flags & PS_SYSTEM) @@ -380,24 +391,12 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, * Copy traceflag and tracefile if enabled. * If not inherited, these were zeroed above. */ - if ((flags & FORK_THREAD) == 0 && curpr->ps_traceflag & KTRFAC_INHERIT) + if (curpr->ps_traceflag & KTRFAC_INHERIT) ktrsettrace(pr, curpr->ps_traceflag, curpr->ps_tracevp, curpr->ps_tracecred); #endif /* - * set priority of child to be that of parent - * XXX should move p_estcpu into the region of struct proc which gets - * copied. - */ - scheduler_fork_hook(curp, p); - - if (flags & FORK_THREAD) - sigstkinit(&p->p_sigstk); - - p->p_addr = (struct user *)uaddr; - - /* * Finish creating the child thread. cpu_fork() will copy * and update the pcb and make the child ready to run. If * this is a normal user fork, the child will exit directly @@ -405,7 +404,7 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, * and will not return here. If this is a kernel thread, * the specified entry point will be executed. */ - cpu_fork(curp, p, stack, 0, func ? func : child_return, arg ? arg : p); + cpu_fork(curp, p, NULL, NULL, func, arg ? arg : p); vm = pr->ps_vmspace; @@ -415,11 +414,8 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, } else if (flags & FORK_VFORK) { forkstat.cntvfork++; forkstat.sizvfork += vm->vm_dsize + vm->vm_ssize; - } else if (flags & FORK_TFORK) { - forkstat.cnttfork++; } else { forkstat.cntkthread++; - forkstat.sizkthread += vm->vm_dsize + vm->vm_ssize; } if (pr->ps_flags & PS_TRACED && flags & FORK_FORK) @@ -429,76 +425,46 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, LIST_INSERT_HEAD(&allproc, p, p_list); LIST_INSERT_HEAD(TIDHASH(p->p_tid), p, p_hash); - if ((flags & FORK_THREAD) == 0) { - LIST_INSERT_HEAD(PIDHASH(pr->ps_pid), pr, ps_hash); - LIST_INSERT_AFTER(curpr, pr, ps_pglist); - LIST_INSERT_HEAD(&curpr->ps_children, pr, ps_sibling); - - if (pr->ps_flags & PS_TRACED) { - pr->ps_oppid = curpr->ps_pid; - if (pr->ps_pptr != curpr->ps_pptr) - proc_reparent(pr, curpr->ps_pptr); - - /* - * Set ptrace status. - */ - if (flags & FORK_FORK) { - pr->ps_ptstat = newptstat; - newptstat = NULL; - curpr->ps_ptstat->pe_report_event = PTRACE_FORK; - pr->ps_ptstat->pe_report_event = PTRACE_FORK; - curpr->ps_ptstat->pe_other_pid = pr->ps_pid; - pr->ps_ptstat->pe_other_pid = curpr->ps_pid; - } - } - } else { - TAILQ_INSERT_TAIL(&pr->ps_threads, p, p_thr_link); + LIST_INSERT_HEAD(PIDHASH(pr->ps_pid), pr, ps_hash); + LIST_INSERT_AFTER(curpr, pr, ps_pglist); + LIST_INSERT_HEAD(&curpr->ps_children, pr, ps_sibling); + + if (pr->ps_flags & PS_TRACED) { + pr->ps_oppid = curpr->ps_pid; + if (pr->ps_pptr != curpr->ps_pptr) + proc_reparent(pr, curpr->ps_pptr); + /* - * if somebody else wants to take us to single threaded mode, - * count ourselves in. + * Set ptrace status. */ - if (pr->ps_single) { - curpr->ps_singlecount++; - atomic_setbits_int(&p->p_flag, P_SUSPSINGLE); + if (newptstat != NULL) { + pr->ps_ptstat = newptstat; + newptstat = NULL; + curpr->ps_ptstat->pe_report_event = PTRACE_FORK; + pr->ps_ptstat->pe_report_event = PTRACE_FORK; + curpr->ps_ptstat->pe_other_pid = pr->ps_pid; + pr->ps_ptstat->pe_other_pid = curpr->ps_pid; } } - if (tidptr != NULL) { - pid_t tid = p->p_tid + THREAD_PID_OFFSET; - - if (copyout(&tid, tidptr, sizeof(tid))) - psignal(curp, SIGSEGV); - } - /* * For new processes, set accounting bits and mark as complete. */ - if ((flags & FORK_THREAD) == 0) { - getnanotime(&pr->ps_start); - pr->ps_acflag = AFORK; - atomic_clearbits_int(&pr->ps_flags, PS_EMBRYO); - } + getnanotime(&pr->ps_start); + pr->ps_acflag = AFORK; + atomic_clearbits_int(&pr->ps_flags, PS_EMBRYO); - /* - * Make child runnable and add to run queue. - */ - if ((flags & FORK_IDLE) == 0) { - SCHED_LOCK(s); - p->p_stat = SRUN; - p->p_cpu = sched_choosecpu_fork(curp, flags); - setrunqueue(p); - SCHED_UNLOCK(s); - } else + if ((flags & FORK_IDLE) == 0) + fork_thread_start(p, curp, flags); + else p->p_cpu = arg; - if (newptstat) - free(newptstat, M_SUBPROC, sizeof(*newptstat)); + free(newptstat, M_SUBPROC, sizeof(*newptstat)); /* * Notify any interested parties about the new process. */ - if ((flags & FORK_THREAD) == 0) - KNOTE(&curpr->ps_klist, NOTE_FORK | pr->ps_pid); + KNOTE(&curpr->ps_klist, NOTE_FORK | pr->ps_pid); /* * Update stats now that we know the fork was successful. @@ -533,17 +499,98 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr, psignal(curp, SIGTRAP); /* - * Return child pid to parent process, - * marking us as parent via retval[1]. + * Return child pid to parent process */ if (retval != NULL) { - retval[0] = (flags & FORK_THREAD) == 0 ? pr->ps_pid : - (p->p_tid + THREAD_PID_OFFSET); + retval[0] = pr->ps_pid; retval[1] = 0; } return (0); } +int +thread_fork(struct proc *curp, void *stack, void *tcb, pid_t *tidptr, + register_t *retval) +{ + struct process *pr = curp->p_p; + struct proc *p; + pid_t tid; + vaddr_t uaddr; + int error; + + if (stack == NULL) + return EINVAL; + + if ((error = fork_check_maxthread(curp->p_ucred->cr_ruid))) + return error; + + uaddr = uvm_uarea_alloc(); + if (uaddr == 0) { + nthreads--; + return ENOMEM; + } + + /* + * From now on, we're committed to the fork and cannot fail. + */ + p = thread_new(curp, uaddr); + atomic_setbits_int(&p->p_flag, P_THREAD); + sigstkinit(&p->p_sigstk); + + /* other links */ + p->p_p = pr; + pr->ps_refcnt++; + + /* local copies */ + p->p_fd = pr->ps_fd; + p->p_vmspace = pr->ps_vmspace; + + /* + * Finish creating the child thread. cpu_fork() will copy + * and update the pcb and make the child ready to run. The + * child will exit directly to user mode via child_return() + * on its first time slice and will not return here. + */ + cpu_fork(curp, p, stack, tcb, child_return, p); + + p->p_tid = alloctid(); + + LIST_INSERT_HEAD(&allproc, p, p_list); + LIST_INSERT_HEAD(TIDHASH(p->p_tid), p, p_hash); + TAILQ_INSERT_TAIL(&pr->ps_threads, p, p_thr_link); + + /* + * if somebody else wants to take us to single threaded mode, + * count ourselves in. + */ + if (pr->ps_single) { + pr->ps_singlecount++; + atomic_setbits_int(&p->p_flag, P_SUSPSINGLE); + } + + /* + * Return tid to parent thread and copy it out to userspace + */ + retval[0] = tid = p->p_tid + THREAD_PID_OFFSET; + retval[1] = 0; + if (tidptr != NULL) { + if (copyout(&tid, tidptr, sizeof(tid))) + psignal(curp, SIGSEGV); + } + + fork_thread_start(p, curp, 0); + + /* + * Update stats now that we know the fork was successful. + */ + forkstat.cnttfork++; + uvmexp.forks++; + uvmexp.forks_sharevm++; + + return 0; +} + + /* Find an unused tid */ pid_t alloctid(void) diff --git a/sys/kern/kern_kthread.c b/sys/kern/kern_kthread.c index 80ee61dd77d..c2f95169367 100644 --- a/sys/kern/kern_kthread.c +++ b/sys/kern/kern_kthread.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_kthread.c,v 1.40 2017/01/21 05:42:03 guenther Exp $ */ +/* $OpenBSD: kern_kthread.c,v 1.41 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: kern_kthread.c,v 1.3 1998/12/22 21:21:36 kleink Exp $ */ /*- @@ -66,7 +66,7 @@ kthread_create(void (*func)(void *), void *arg, * parent to wait for. */ error = fork1(&proc0, FORK_SHAREVM|FORK_SHAREFILES|FORK_NOZOMBIE| - FORK_SYSTEM|FORK_SIGHAND, NULL, 0, func, arg, NULL, &p); + FORK_SYSTEM|FORK_SIGHAND, func, arg, NULL, &p); if (error) return (error); diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index dd3d4ef0dc5..50c86ec3b01 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sched.c,v 1.44 2017/01/21 05:42:03 guenther Exp $ */ +/* $OpenBSD: kern_sched.c,v 1.45 2017/02/12 04:55:08 guenther Exp $ */ /* * Copyright (c) 2007, 2008 Artur Grabowski <art@openbsd.org> * @@ -108,7 +108,7 @@ sched_kthreads_create(void *v) static int num; if (fork1(&proc0, FORK_SHAREVM|FORK_SHAREFILES|FORK_NOZOMBIE| - FORK_SYSTEM|FORK_SIGHAND|FORK_IDLE, NULL, 0, sched_idle, ci, NULL, + FORK_SYSTEM|FORK_SIGHAND|FORK_IDLE, sched_idle, ci, NULL, &spc->spc_idleproc)) panic("fork idle"); diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 6edbd641f6d..4f015f2eeb0 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.233 2017/02/08 20:58:30 guenther Exp $ */ +/* $OpenBSD: proc.h,v 1.234 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -447,10 +447,8 @@ struct uidinfo *uid_find(uid_t); #define FORK_SYSTEM 0x00000020 #define FORK_NOZOMBIE 0x00000040 #define FORK_SHAREVM 0x00000080 -#define FORK_TFORK 0x00000100 #define FORK_SIGHAND 0x00000200 #define FORK_PTRACE 0x00000400 -#define FORK_THREAD 0x00000800 #define EXIT_NORMAL 0x00000001 #define EXIT_THREAD 0x00000002 @@ -517,10 +515,14 @@ void exit1(struct proc *, int, int); void exit2(struct proc *); int dowait4(struct proc *, pid_t, int *, int, struct rusage *, register_t *); +void cpu_fork(struct proc *_curp, struct proc *_child, void *_stack, + void *_tcb, void (*_func)(void *), void *_arg); void cpu_exit(struct proc *); void process_initialize(struct process *, struct proc *); -int fork1(struct proc *, int, void *, pid_t *, void (*)(void *), - void *, register_t *, struct proc **); +int fork1(struct proc *_curp, int _flags, void (*_func)(void *), + void *_arg, register_t *_retval, struct proc **_newprocp); +int thread_fork(struct proc *_curp, void *_stack, void *_tcb, + pid_t *_tidptr, register_t *_retval); int groupmember(gid_t, struct ucred *); void dorefreshcreds(struct process *, struct proc *); void dosigsuspend(struct proc *, sigset_t); diff --git a/sys/uvm/uvm_extern.h b/sys/uvm/uvm_extern.h index 8ef427d06cb..0187782f1cb 100644 --- a/sys/uvm/uvm_extern.h +++ b/sys/uvm/uvm_extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_extern.h,v 1.139 2016/06/05 08:35:57 stefan Exp $ */ +/* $OpenBSD: uvm_extern.h,v 1.140 2017/02/12 04:55:08 guenther Exp $ */ /* $NetBSD: uvm_extern.h,v 1.57 2001/03/09 01:02:12 chs Exp $ */ /* @@ -272,8 +272,6 @@ extern vaddr_t vm_min_kernel_address; void vmapbuf(struct buf *, vsize_t); void vunmapbuf(struct buf *, vsize_t); -void cpu_fork(struct proc *, struct proc *, void *, - size_t, void (*)(void *), void *); struct uvm_object *uao_create(vsize_t, int); void uao_detach(struct uvm_object *); void uao_detach_locked(struct uvm_object *); |