about summary refs log tree commit diff
path: root/sysdeps/mach
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/mach')
-rw-r--r--sysdeps/mach/hurd/fork.c72
-rw-r--r--sysdeps/mach/hurd/i386/trampoline.c29
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