diff options
Diffstat (limited to 'sysdeps/mach/hurd')
-rw-r--r-- | sysdeps/mach/hurd/fork.c | 72 | ||||
-rw-r--r-- | sysdeps/mach/hurd/i386/trampoline.c | 29 |
2 files changed, 79 insertions, 22 deletions
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c index b8b15743cd..66b1ba84c7 100644 --- a/sysdeps/mach/hurd/fork.c +++ b/sysdeps/mach/hurd/fork.c @@ -29,11 +29,7 @@ Cambridge, MA 02139, USA. */ /* Things that want to be locked while forking. */ -struct - { - size_t n; - struct mutex *locks[0]; - } _hurd_fork_locks; +symbol_set_declare (_hurd_fork_locks) /* Things that want to be called before we fork, to prepare the parent for @@ -62,7 +58,6 @@ __fork (void) pid_t pid; size_t i; error_t err; - thread_t thread_self = __mach_thread_self (); struct hurd_sigstate *volatile ss; ss = _hurd_self_sigstate (); @@ -87,14 +82,32 @@ __fork (void) mach_msg_type_number_t nporttypes = 0; thread_t *threads = NULL; mach_msg_type_number_t nthreads = 0; - int ports_locked = 0; + int ports_locked = 0, stopped = 0; + + void resume_threads (void) + { + if (! stopped) + return; + + assert (threads); + + for (i = 0; i < nthreads; ++i) + if (threads[i] != ss->thread) + __thread_resume (threads[i]); + stopped = 0; + } /* Run things that prepare for forking before we create the task. */ RUN_HOOK (_hurd_fork_prepare_hook, ()); /* Lock things that want to be locked before we fork. */ - for (i = 0; i < _hurd_fork_locks.n; ++i) - __mutex_lock (_hurd_fork_locks.locks[i]); + { + void *const *p; + for (p = symbol_set_first_element (_hurd_fork_locks); + ! symbol_set_end_p (_hurd_fork_locks, p); + ++p) + __mutex_lock (*p); + } __mutex_lock (&_hurd_siglock); newtask = MACH_PORT_NULL; @@ -108,8 +121,16 @@ __fork (void) __spin_lock (&_hurd_ports[i].lock); ports_locked = 1; - /* Create the child task. It will inherit a copy of our memory. */ - err = __task_create (__mach_task_self (), 1, &newtask); + /* Stop all other threads while copying the address space, + so nothing changes. */ + err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread); + if (!err) + { + stopped = 1; + + /* Create the child task. It will inherit a copy of our memory. */ + err = __task_create (__mach_task_self (), 1, &newtask); + } /* Unlock the global signal state lock, so we do not block the signal thread any longer than necessary. */ @@ -263,7 +284,7 @@ __fork (void) if (err = __proc_task2proc (portnames[i], newtask, &insert)) LOSE; } - else if (portnames[i] == thread_self) + else if (portnames[i] == ss->thread) { /* For the name we use for our own thread port, we will insert the thread port for the child main user thread @@ -372,6 +393,10 @@ __fork (void) __spin_unlock (&_hurd_ports[i].lock); ports_locked = 0; + /* All state has now been copied from the parent. It is safe to + resume other parent threads. */ + resume_threads (); + /* Create the child main user thread and signal thread. */ if ((err = __thread_create (newtask, &thread)) || (err = __thread_create (newtask, &sigthread))) @@ -381,8 +406,8 @@ __fork (void) dead name rights with the names we want to give the thread ports in the child as placeholders. Now deallocate them so we can use the names. */ - if ((err = __mach_port_deallocate (newtask, thread_self)) || - (err = __mach_port_insert_right (newtask, thread_self, + if ((err = __mach_port_deallocate (newtask, ss->thread)) || + (err = __mach_port_insert_right (newtask, ss->thread, thread, MACH_MSG_TYPE_COPY_SEND))) LOSE; /* We have one extra user reference created at the beginning of this @@ -390,9 +415,9 @@ __fork (void) accounted for in the child below). This extra right gets consumed in the child by the store into _hurd_sigthread in the child fork. */ if (thread_refs > 1 && - (err = __mach_port_mod_refs (newtask, thread_self, + (err = __mach_port_mod_refs (newtask, ss->thread, MACH_PORT_RIGHT_SEND, - thread_refs - 1))) + thread_refs))) LOSE; if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none. */ && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) || @@ -474,6 +499,8 @@ __fork (void) for (i = 0; i < _hurd_nports; ++i) __spin_unlock (&_hurd_ports[i].lock); + resume_threads (); + if (newtask != MACH_PORT_NULL) { if (err) @@ -486,8 +513,6 @@ __fork (void) __mach_port_deallocate (__mach_task_self (), sigthread); if (newproc != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), newproc); - if (thread_self != MACH_PORT_NULL) - __mach_port_deallocate (__mach_task_self (), thread_self); if (portnames) __vm_deallocate (__mach_task_self (), @@ -525,7 +550,7 @@ __fork (void) /* We are the only thread in this new task, so we will take the task-global signals. */ - _hurd_sigthread = thread_self; + _hurd_sigthread = ss->thread; /* Unchain the sigstate structures for threads that existed in the parent task but don't exist in this task (the child process). @@ -575,8 +600,13 @@ __fork (void) /* Unlock things we locked before creating the child task. They are locked in both the parent and child tasks. */ - for (i = 0; i < _hurd_fork_locks.n; ++i) - __mutex_unlock (_hurd_fork_locks.locks[i]); + { + void *const *p; + for (p = symbol_set_first_element (_hurd_fork_locks); + ! symbol_set_end_p (_hurd_fork_locks, p); + ++p) + __mutex_unlock (*p); + } _hurd_critical_section_unlock (ss); diff --git a/sysdeps/mach/hurd/i386/trampoline.c b/sysdeps/mach/hurd/i386/trampoline.c index 7adffc40be..9e947a46e7 100644 --- a/sysdeps/mach/hurd/i386/trampoline.c +++ b/sysdeps/mach/hurd/i386/trampoline.c @@ -45,6 +45,9 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler, struct machine_thread_all_state *state) { __label__ trampoline, rpc_wait_trampoline, firewall; + extern const void _hurd_intr_rpc_msg_in_trap; + extern const void _hurd_intr_rpc_msg_cx_sp; + extern const void _hurd_intr_rpc_msg_sp_restored; void *volatile sigsp; struct sigcontext *scp; struct @@ -80,6 +83,11 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler, if (! machine_get_basic_state (ss->thread, state)) return NULL; + /* Save the original SP in the gratuitous `esp' slot. + We may need to reset the SP (the `uesp' slot) to avoid clobbering an + interrupted RPC frame. */ + state->basic.esp = state->basic.uesp; + if ((ss->actions[signo].sa_flags & SA_ONSTACK) && !(ss->sigaltstack.ss_flags & (SA_DISABLE|SA_ONSTACK))) { @@ -88,6 +96,24 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler, /* XXX need to set up base of new stack for per-thread variables, cthreads. */ } + /* This code has intimate knowledge of the special mach_msg system call + done in intr-msg.c; that code does: + movl %esp, %ecx + leal ARGS, %esp + _hurd_intr_rpc_msg_cx_sp: movl $-25, %eax + _hurd_intr_rpc_msg_do_trap: lcall $7, $0 + _hurd_intr_rpc_msg_in_trap: movl %ecx, %esp + _hurd_intr_rpc_msg_sp_restored: + We must check for the window during which %esp points at the + mach_msg arguments. The space below until %ecx is used by + the _hurd_intr_rpc_mach_msg frame, and must not be clobbered. */ + else if (state->basic.eip >= (int) &_hurd_intr_rpc_msg_cx_sp && + state->basic.eip < (int) &_hurd_intr_rpc_msg_sp_restored) + /* The SP now points at the mach_msg args, but there is more stack + space used below it. The real SP is saved in %ecx; we must push the + new frame below there, and restore that value as the SP on + sigreturn. */ + sigsp = (char *) (state->basic.uesp = state->basic.ecx); else sigsp = (char *) state->basic.uesp; @@ -166,7 +192,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler, message reception, since the request message has already been sent. */ - struct mach_msg_trap_args *args = (void *) state->basic.uesp; + struct mach_msg_trap_args *args = (void *) state->basic.esp; if (_hurdsig_catch_fault (SIGSEGV)) { @@ -192,6 +218,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler, state->basic.eip = (int) &&rpc_wait_trampoline; /* The reply-receiving trampoline code runs initially on the original user stack. We pass it the signal stack pointer in %ebx. */ + state->basic.uesp = state->basic.esp; /* Restore mach_msg syscall SP. */ state->basic.ebx = (int) sigsp; /* After doing the message receive, the trampoline code will need to update the %eax value to be restored by sigreturn. To simplify |