posix: Consolidate fork implementation
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 18 Jan 2021 18:18:13 +0000 (15:18 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Thu, 24 Jun 2021 13:02:06 +0000 (10:02 -0300)
The Linux nptl implementation is used as base for generic fork
implementation to handle the internal locks and mutexes.  The
system specific bits are moved a new internal _Fork symbol.

(This new implementation will be used to provide a async-signal-safe
_Fork now that POSIX has clarified that fork might not be
async-signal-safe [1]).

For Hurd it means that the __nss_database_fork_prepare_parent and
__nss_database_fork_subprocess will be run in a slight different
order.

[1] https://austingroupbugs.net/view.php?id=62

include/unistd.h
posix/Makefile
posix/_Fork.c [new file with mode: 0644]
posix/fork.c
sysdeps/generic/fork.h [new file with mode: 0644]
sysdeps/mach/hurd/_Fork.c [new file with mode: 0644]
sysdeps/mach/hurd/fork.c [deleted file]
sysdeps/nptl/_Fork.c [new file with mode: 0644]
sysdeps/nptl/fork.c [deleted file]
sysdeps/nptl/fork.h [new file with mode: 0644]
sysdeps/unix/sysv/linux/arch-fork.h

index 34872d8b4133a9c925f5b42076800316a8e0d8e9..691405a945aea0298bcc70589eb47df2815463f2 100644 (file)
@@ -140,6 +140,8 @@ extern __pid_t __vfork (void);
 libc_hidden_proto (__vfork)
 extern int __ttyname_r (int __fd, char *__buf, size_t __buflen);
 libc_hidden_proto (__ttyname_r)
+extern __pid_t _Fork (void);
+libc_hidden_proto (_Fork);
 extern int __isatty (int __fd) attribute_hidden;
 extern int __link (const char *__from, const char *__to);
 extern int __symlink (const char *__from, const char *__to);
index 8d139e54f6f0fb31c0212dfb84881eba100c2aad..3bd7d605dfb4143bbe3b864234bb17702c9aea30 100644 (file)
@@ -39,7 +39,7 @@ routines :=                                                                 \
        times                                                                 \
        wait waitpid wait3 wait4 waitid                                       \
        alarm sleep pause nanosleep                                           \
-       fork vfork _exit register-atfork                                      \
+       fork _Fork vfork _exit register-atfork                                \
        execve fexecve execv execle execl execvp execlp execvpe               \
        getpid getppid                                                        \
        getuid geteuid getgid getegid getgroups setuid setgid group_member    \
@@ -266,6 +266,7 @@ CFLAGS-execl.os = -fomit-frame-pointer
 CFLAGS-execvp.os = -fomit-frame-pointer
 CFLAGS-execlp.os = -fomit-frame-pointer
 CFLAGS-nanosleep.c += -fexceptions -fasynchronous-unwind-tables
+CFLAGS-fork.c = $(libio-mtsafe)
 
 tstgetopt-ARGS = -a -b -cfoobar --required foobar --optional=bazbug \
                --none random --col --color --colour
diff --git a/posix/_Fork.c b/posix/_Fork.c
new file mode 100644 (file)
index 0000000..4a998c0
--- /dev/null
@@ -0,0 +1,34 @@
+/* _Fork implementation.  Generic version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <unistd.h>
+
+/* Clone the calling process, creating an exact copy.  Return -1 for errors,
+   0 to the new process, and the process ID of the new process to the
+   old process.
+   Different than fork, this functions is marked as async-signal-safe by
+   POSIX (by Austin Group issue 62).  */
+pid_t
+_Fork (void)
+{
+  __set_errno (ENOSYS);
+  return -1;
+}
+libc_hidden_def (_Fork)
+stub_warning (_Fork)
index 05bda04ac5a7926f7b88fa412cb0b5a143744f64..44caf8d16633fc83ad7b90325cfbb535f7590e3a 100644 (file)
@@ -1,4 +1,5 @@
-/* Copyright (C) 1991-2021 Free Software Foundation, Inc.
+/* fork - create a child process.
+   Copyright (C) 1991-2021 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <errno.h>
-#include <unistd.h>
+#include <fork.h>
+#include <libio/libioP.h>
+#include <ldsodefs.h>
+#include <malloc/malloc-internal.h>
+#include <nss/nss_database.h>
+#include <register-atfork.h>
+#include <stdio-lock.h>
+#include <sys/single_threaded.h>
+#include <unwind-link.h>
 
+static void
+fresetlockfiles (void)
+{
+  _IO_ITER i;
+
+  for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
+    if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0)
+      _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock));
+}
 
-/* Clone the calling process, creating an exact copy.
-   Return -1 for errors, 0 to the new process,
-   and the process ID of the new process to the old process.  */
-int
-__fork (void)
+pid_t
+__libc_fork (void)
 {
-  __set_errno (ENOSYS);
-  return -1;
+  /* Determine if we are running multiple threads.  We skip some fork
+     handlers in the single-thread case, to make fork safer to use in
+     signal handlers.  */
+  bool multiple_threads = __libc_single_threaded == 0;
+
+  __run_fork_handlers (atfork_run_prepare, multiple_threads);
+
+  struct nss_database_data nss_database_data;
+
+  /* If we are not running multiple threads, we do not have to
+     preserve lock state.  If fork runs from a signal handler, only
+     async-signal-safe functions can be used in the child.  These data
+     structures are only used by unsafe functions, so their state does
+     not matter if fork was called from a signal handler.  */
+  if (multiple_threads)
+    {
+      call_function_static_weak (__nss_database_fork_prepare_parent,
+                                &nss_database_data);
+
+      _IO_list_lock ();
+
+      /* Acquire malloc locks.  This needs to come last because fork
+        handlers may use malloc, and the libio list lock has an
+        indirect malloc dependency as well (via the getdelim
+        function).  */
+      call_function_static_weak (__malloc_fork_lock_parent);
+    }
+
+  pid_t pid = _Fork ();
+
+  if (pid == 0)
+    {
+      fork_system_setup ();
+
+      /* Reset the lock state in the multi-threaded case.  */
+      if (multiple_threads)
+       {
+         __libc_unwind_link_after_fork ();
+
+         fork_system_setup_after_fork ();
+
+         /* Release malloc locks.  */
+         call_function_static_weak (__malloc_fork_unlock_child);
+
+         /* Reset the file list.  These are recursive mutexes.  */
+         fresetlockfiles ();
+
+         /* Reset locks in the I/O code.  */
+         _IO_list_resetlock ();
+
+         call_function_static_weak (__nss_database_fork_subprocess,
+                                    &nss_database_data);
+       }
+
+      /* Reset the lock the dynamic loader uses to protect its data.  */
+      __rtld_lock_initialize (GL(dl_load_lock));
+
+      reclaim_stacks ();
+
+      /* Run the handlers registered for the child.  */
+      __run_fork_handlers (atfork_run_child, multiple_threads);
+    }
+  else
+    {
+      /* Release acquired locks in the multi-threaded case.  */
+      if (multiple_threads)
+       {
+         /* Release malloc locks, parent process variant.  */
+         call_function_static_weak (__malloc_fork_unlock_parent);
+
+         /* We execute this even if the 'fork' call failed.  */
+         _IO_list_unlock ();
+       }
+
+      /* Run the handlers registered for the parent.  */
+      __run_fork_handlers (atfork_run_parent, multiple_threads);
+    }
+
+  return pid;
 }
+weak_alias (__libc_fork, __fork)
 libc_hidden_def (__fork)
-stub_warning (fork)
-
-weak_alias (__fork, fork)
+weak_alias (__libc_fork, fork)
diff --git a/sysdeps/generic/fork.h b/sysdeps/generic/fork.h
new file mode 100644 (file)
index 0000000..be42cf2
--- /dev/null
@@ -0,0 +1,32 @@
+/* System specific fork hooks.  Generic version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+static inline void
+fork_system_setup (void)
+{
+}
+
+static inline void
+fork_system_setup_after_fork (void)
+{
+}
+
+static inline void
+reclaim_stacks (void)
+{
+}
diff --git a/sysdeps/mach/hurd/_Fork.c b/sysdeps/mach/hurd/_Fork.c
new file mode 100644 (file)
index 0000000..df4ee05
--- /dev/null
@@ -0,0 +1,719 @@
+/* Copyright (C) 1994-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <hurd/signal.h>
+#include <hurd/threadvar.h>
+#include <setjmp.h>
+#include <thread_state.h>
+#include <sysdep.h>            /* For stack growth direction.  */
+#include "set-hooks.h"
+#include <assert.h>
+#include "hurdmalloc.h"                /* XXX */
+#include <tls.h>
+#include <malloc/malloc-internal.h>
+#include <nss/nss_database.h>
+#include <unwind-link.h>
+#include <register-atfork.h>
+
+#undef __fork
+
+
+/* Things that want to be locked while forking.  */
+symbol_set_declare (_hurd_fork_locks)
+
+/* Things that want to be called before we fork, to prepare the parent for
+   task_create, when the new child task will inherit our address space.  */
+DEFINE_HOOK (_hurd_fork_prepare_hook, (void));
+
+/* Things that want to be called when we are forking, with the above all
+   locked.  They are passed the task port of the child.  The child process
+   is all set up except for doing proc_child, and has no threads yet.  */
+DEFINE_HOOK (_hurd_fork_setup_hook, (void));
+
+/* Things to be run in the child fork.  */
+DEFINE_HOOK (_hurd_fork_child_hook, (void));
+
+/* Things to be run in the parent fork.  */
+DEFINE_HOOK (_hurd_fork_parent_hook, (void));
+
+
+/* Clone the calling process, creating an exact copy.
+   Return -1 for errors, 0 to the new process,
+   and the process ID of the new process to the old process.  */
+pid_t
+_Fork (void)
+{
+  jmp_buf env;
+  pid_t pid;
+  size_t i;
+  error_t err;
+  struct hurd_sigstate *volatile ss;
+
+  ss = _hurd_self_sigstate ();
+retry:
+  __spin_lock (&ss->critical_section_lock);
+
+#undef LOSE
+#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
+
+  if (! setjmp (env))
+    {
+      process_t newproc;
+      task_t newtask;
+      thread_t thread, sigthread;
+      mach_port_urefs_t thread_refs, sigthread_refs;
+      struct machine_thread_state state;
+      mach_msg_type_number_t statecount;
+      mach_port_t *portnames = NULL;
+      mach_msg_type_number_t nportnames = 0;
+      mach_port_type_t *porttypes = NULL;
+      mach_msg_type_number_t nporttypes = 0;
+      thread_t *threads = NULL;
+      mach_msg_type_number_t nthreads = 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.  */
+      {
+       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);
+
+      /* Acquire malloc locks.  This needs to come last because fork
+        handlers may use malloc, and the libio list lock has an
+        indirect malloc dependency as well (via the getdelim
+        function).  */
+      call_function_static_weak (__malloc_fork_lock_parent);
+      _hurd_malloc_fork_prepare ();
+
+      newtask = MACH_PORT_NULL;
+      thread = sigthread = MACH_PORT_NULL;
+      newproc = MACH_PORT_NULL;
+
+      /* Lock all the port cells for the standard ports while we copy the
+        address space.  We want to insert all the send rights into the
+        child with the same names.  */
+      for (i = 0; i < _hurd_nports; ++i)
+       __spin_lock (&_hurd_ports[i].lock);
+      ports_locked = 1;
+
+
+      /* Keep our SS locked while stopping other threads, so they don't get a
+         chance to have it locked in the copied space.  */
+      __spin_lock (&ss->lock);
+      /* Stop all other threads while copying the address space,
+        so nothing changes.  */
+      err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
+      __spin_unlock (&ss->lock);
+      if (!err)
+       {
+         stopped = 1;
+
+#define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
+
+#ifdef XXX_KERNEL_PAGE_FAULT_BUG
+         /* Gag me with a pitchfork.
+            The bug scenario is this:
+
+            - The page containing __mach_task_self_ is paged out.
+            - The signal thread was faulting on that page when we
+              suspended it via proc_dostop.  It holds some lock, or set
+              some busy bit, or somesuch.
+            - Now this thread faults on that same page.
+            - GRATUIOUS DEADLOCK
+
+            We can break the deadlock by aborting the thread that faulted
+            first, which if the bug happened was the signal thread because
+            it is the only other thread and we just suspended it.
+            */
+         __thread_abort (_hurd_msgport_thread);
+#endif
+         /* Create the child task.  It will inherit a copy of our memory.  */
+         err = __task_create (__mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+                              NULL, 0, /* OSF Mach */
+#endif
+                              1, &newtask);
+       }
+
+      /* Unlock the global signal state lock, so we do not
+        block the signal thread any longer than necessary.  */
+      __mutex_unlock (&_hurd_siglock);
+
+      if (err)
+       LOSE;
+
+      /* Fetch the names of all ports used in this task.  */
+      if (err = __mach_port_names (__mach_task_self (),
+                                  &portnames, &nportnames,
+                                  &porttypes, &nporttypes))
+       LOSE;
+      if (nportnames != nporttypes)
+       {
+         err = EGRATUITOUS;
+         LOSE;
+       }
+
+      /* Get send rights for all the threads in this task.
+        We want to avoid giving these rights to the child.  */
+      if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
+       LOSE;
+
+      /* Get the child process's proc server port.  We will insert it into
+        the child with the same name as we use for our own proc server
+        port; and we will need it to set the child's message port.  */
+      if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
+                                 newtask, &newproc))
+       LOSE;
+
+      /* Insert all our port rights into the child task.  */
+      thread_refs = sigthread_refs = 0;
+      for (i = 0; i < nportnames; ++i)
+       {
+         if (porttypes[i] & MACH_PORT_TYPE_RECEIVE)
+           {
+             /* This is a receive right.  We want to give the child task
+                its own new receive right under the same name.  */
+             if (err = __mach_port_allocate_name (newtask,
+                                                  MACH_PORT_RIGHT_RECEIVE,
+                                                  portnames[i]))
+               LOSE;
+             if (porttypes[i] & MACH_PORT_TYPE_SEND)
+               {
+                 /* Give the child as many send rights for its receive
+                    right as we have for ours.  */
+                 mach_port_urefs_t refs;
+                 mach_port_t port;
+                 mach_msg_type_name_t poly;
+                 if (err = __mach_port_get_refs (__mach_task_self (),
+                                                 portnames[i],
+                                                 MACH_PORT_RIGHT_SEND,
+                                                 &refs))
+                   LOSE;
+                 if (err = __mach_port_extract_right (newtask,
+                                                      portnames[i],
+                                                      MACH_MSG_TYPE_MAKE_SEND,
+                                                      &port, &poly))
+                   LOSE;
+                 if (portnames[i] == _hurd_msgport)
+                   {
+                     /* We just created a receive right for the child's
+                        message port and are about to insert send rights
+                        for it.  Now, while we happen to have a send right
+                        for it, give it to the proc server.  */
+                     mach_port_t old;
+                     if (err = __proc_setmsgport (newproc, port, &old))
+                       LOSE;
+                     if (old != MACH_PORT_NULL)
+                       /* XXX what to do here? */
+                       __mach_port_deallocate (__mach_task_self (), old);
+                     /* The new task will receive its own exceptions
+                        on its message port.  */
+                     if (err =
+#ifdef TASK_EXCEPTION_PORT
+                         __task_set_special_port (newtask,
+                                                  TASK_EXCEPTION_PORT,
+                                                  port)
+#elif defined (EXC_MASK_ALL)
+                         __task_set_exception_ports
+                         (newtask, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
+                                                    | EXC_MASK_MACH_SYSCALL
+                                                    | EXC_MASK_RPC_ALERT),
+                          port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
+#else
+# error task_set_exception_port?
+#endif
+                         )
+                       LOSE;
+                   }
+                 if (err = __mach_port_insert_right (newtask,
+                                                     portnames[i],
+                                                     port,
+                                                     MACH_MSG_TYPE_MOVE_SEND))
+                   LOSE;
+                 if (refs > 1
+                     && (err = __mach_port_mod_refs (newtask,
+                                                     portnames[i],
+                                                     MACH_PORT_RIGHT_SEND,
+                                                     refs - 1)))
+                   LOSE;
+               }
+             if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
+               {
+                 /* Give the child a send-once right for its receive right,
+                    since we have one for ours.  */
+                 mach_port_t port;
+                 mach_msg_type_name_t poly;
+                 if (err = __mach_port_extract_right
+                     (newtask,
+                      portnames[i],
+                      MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                      &port, &poly))
+                   LOSE;
+                 if (err = __mach_port_insert_right
+                     (newtask,
+                      portnames[i], port,
+                      MACH_MSG_TYPE_MOVE_SEND_ONCE))
+                   LOSE;
+               }
+           }
+         else if (porttypes[i]
+                  & (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
+           {
+             /* This is a send right or a dead name.
+                Give the child as many references for it as we have.  */
+             mach_port_urefs_t refs = 0, *record_refs = NULL;
+             mach_port_t insert;
+             mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND;
+             if (portnames[i] == newtask || portnames[i] == newproc)
+               /* Skip the name we use for the child's task or proc ports.  */
+               continue;
+             if (portnames[i] == __mach_task_self ())
+               /* For the name we use for our own task port,
+                  insert the child's task port instead.  */
+               insert = newtask;
+             else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
+               {
+                 /* Use the proc server port for the new task.  */
+                 insert = newproc;
+                 insert_type = MACH_MSG_TYPE_COPY_SEND;
+               }
+             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
+                    after we create it.  */
+                 insert = MACH_PORT_NULL;
+                 record_refs = &thread_refs;
+                 /* Allocate a dead name right for this name as a
+                    placeholder, so the kernel will not chose this name
+                    for any other new port (it might use it for one of the
+                    rights created when a thread is created).  */
+                 if (err = __mach_port_allocate_name
+                     (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
+                   LOSE;
+               }
+             else if (portnames[i] == _hurd_msgport_thread)
+               /* For the name we use for our signal thread's thread port,
+                  we will insert the thread port for the child's signal
+                  thread after we create it.  */
+               {
+                 insert = MACH_PORT_NULL;
+                 record_refs = &sigthread_refs;
+                 /* Allocate a dead name right as a placeholder.  */
+                 if (err = __mach_port_allocate_name
+                     (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
+                   LOSE;
+               }
+             else
+               {
+                 /* Skip the name we use for any of our own thread ports.  */
+                 mach_msg_type_number_t j;
+                 for (j = 0; j < nthreads; ++j)
+                   if (portnames[i] == threads[j])
+                     break;
+                 if (j < nthreads)
+                   continue;
+
+                 /* Copy our own send right.  */
+                 insert = portnames[i];
+               }
+             /* Find out how many user references we have for
+                the send right with this name.  */
+             if (err = __mach_port_get_refs (__mach_task_self (),
+                                             portnames[i],
+                                             MACH_PORT_RIGHT_SEND,
+                                             record_refs ?: &refs))
+               LOSE;
+             if (insert == MACH_PORT_NULL)
+               continue;
+             if (insert == portnames[i]
+                 && (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
+               /* This is a dead name; allocate another dead name
+                  with the same name in the child.  */
+             allocate_dead_name:
+               err = __mach_port_allocate_name (newtask,
+                                                MACH_PORT_RIGHT_DEAD_NAME,
+                                                portnames[i]);
+             else
+               /* Insert the chosen send right into the child.  */
+               err = __mach_port_insert_right (newtask,
+                                               portnames[i],
+                                               insert, insert_type);
+             switch (err)
+               {
+               case KERN_NAME_EXISTS:
+                 {
+                   /* It already has a send right under this name (?!).
+                      Well, it starts out with a send right for its task
+                      port, and inherits the bootstrap and exception ports
+                      from us.  */
+                   mach_port_t childport;
+                   mach_msg_type_name_t poly;
+                   assert (__mach_port_extract_right (newtask, portnames[i],
+                                                      MACH_MSG_TYPE_COPY_SEND,
+                                                      &childport,
+                                                      &poly) == 0
+                           && childport == insert
+                           && __mach_port_deallocate (__mach_task_self (),
+                                                      childport) == 0);
+                   break;
+                 }
+
+               case KERN_INVALID_CAPABILITY:
+                 /* The port just died.  It was a send right,
+                    and now it's a dead name.  */
+                 goto allocate_dead_name;
+
+               default:
+                 LOSE;
+                 break;
+
+               case KERN_SUCCESS:
+                 /* Give the child as many user references as we have.  */
+                 if (refs > 1
+                     && (err = __mach_port_mod_refs (newtask,
+                                                     portnames[i],
+                                                     MACH_PORT_RIGHT_SEND,
+                                                     refs - 1)))
+                   LOSE;
+               }
+           }
+       }
+
+      /* Unlock the standard port cells.  The child must unlock its own
+        copies too.  */
+      for (i = 0; i < _hurd_nports; ++i)
+       __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)))
+       LOSE;
+
+      /* Insert send rights for those threads.  We previously allocated
+        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, ss->thread))
+         || (err = __mach_port_insert_right (newtask, ss->thread,
+                                             thread,
+                                             MACH_MSG_TYPE_COPY_SEND)))
+       LOSE;
+      /* XXX consumed? (_hurd_sigthread is no more) */
+      if (thread_refs > 1
+         && (err = __mach_port_mod_refs (newtask, ss->thread,
+                                         MACH_PORT_RIGHT_SEND,
+                                         thread_refs - 1)))
+       LOSE;
+      if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
+         && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread))
+             || (err = __mach_port_insert_right (newtask,
+                                                 _hurd_msgport_thread,
+                                                 sigthread,
+                                                 MACH_MSG_TYPE_COPY_SEND))))
+       LOSE;
+      if (sigthread_refs > 1
+         && (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
+                                         MACH_PORT_RIGHT_SEND,
+                                         sigthread_refs - 1)))
+       LOSE;
+
+      /* This seems like a convenient juncture to copy the proc server's
+        idea of what addresses our argv and envp are found at from the
+        parent into the child.  Since we happen to know that the child
+        shares our memory image, it is we who should do this copying.  */
+      {
+       vm_address_t argv, envp;
+       err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
+              ?: __proc_set_arg_locations (newproc, argv, envp));
+       if (err)
+         LOSE;
+      }
+
+      /* Set the child signal thread up to run the msgport server function
+        using the same signal thread stack copied from our address space.
+        We fetch the state before longjmp'ing it so that miscellaneous
+        registers not affected by longjmp (such as i386 segment registers)
+        are in their normal default state.  */
+      statecount = MACHINE_THREAD_STATE_COUNT;
+      if (err = __thread_get_state (_hurd_msgport_thread,
+                                   MACHINE_THREAD_STATE_FLAVOR,
+                                   (natural_t *) &state, &statecount))
+       LOSE;
+#ifdef STACK_GROWTH_UP
+      if (__hurd_sigthread_stack_base == 0)
+       {
+         state.SP &= __hurd_threadvar_stack_mask;
+         state.SP += __hurd_threadvar_stack_offset;
+       }
+      else
+       state.SP = __hurd_sigthread_stack_base;
+#else
+      if (__hurd_sigthread_stack_end == 0)
+       {
+         /* The signal thread has a stack assigned by pthread.
+            The threadvar_stack variables conveniently tell us how
+            to get to the highest address in the stack, just below
+            the per-thread variables.  */
+         state.SP &= __hurd_threadvar_stack_mask;
+         state.SP += __hurd_threadvar_stack_offset;
+       }
+      else
+       state.SP = __hurd_sigthread_stack_end;
+#endif
+      MACHINE_THREAD_STATE_SET_PC (&state,
+                                  (unsigned long int) _hurd_msgport_receive);
+
+      /* Do special signal thread setup for TLS if needed.  */
+      if (err = _hurd_tls_fork (sigthread, _hurd_msgport_thread, &state))
+       LOSE;
+
+      if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
+                                   (natural_t *) &state, statecount))
+       LOSE;
+      /* We do not thread_resume SIGTHREAD here because the child
+        fork needs to do more setup before it can take signals.  */
+
+      /* Set the child user thread up to return 1 from the setjmp above.  */
+      _hurd_longjmp_thread_state (&state, env, 1);
+
+      /* Do special thread setup for TLS if needed.  */
+      if (err = _hurd_tls_fork (thread, ss->thread, &state))
+       LOSE;
+
+      if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
+                                   (natural_t *) &state, statecount))
+       LOSE;
+
+      /* Get the PID of the child from the proc server.  We must do this
+        before calling proc_child below, because at that point any
+        authorized POSIX.1 process may kill the child task with SIGKILL.  */
+      if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
+       LOSE;
+
+      /* Register the child with the proc server.  It is important that
+        this be that last thing we do before starting the child thread
+        running.  Once proc_child has been done for the task, it appears
+        as a POSIX.1 process.  Any errors we get must be detected before
+        this point, and the child must have a message port so it responds
+        to POSIX.1 signals.  */
+      if (err = __USEPORT (PROC, __proc_child (port, newtask)))
+       LOSE;
+
+      /* This must be the absolutely last thing we do; we can't assume that
+        the child will remain alive for even a moment once we do this.  We
+        ignore errors because we have committed to the fork and are not
+        allowed to return them after the process becomes visible to
+        POSIX.1 (which happened right above when we called proc_child).  */
+      (void) __thread_resume (thread);
+
+    lose:
+      if (ports_locked)
+       for (i = 0; i < _hurd_nports; ++i)
+         __spin_unlock (&_hurd_ports[i].lock);
+
+      resume_threads ();
+
+      if (newtask != MACH_PORT_NULL)
+       {
+         if (err)
+           __task_terminate (newtask);
+         __mach_port_deallocate (__mach_task_self (), newtask);
+       }
+      if (thread != MACH_PORT_NULL)
+       __mach_port_deallocate (__mach_task_self (), thread);
+      if (sigthread != MACH_PORT_NULL)
+       __mach_port_deallocate (__mach_task_self (), sigthread);
+      if (newproc != MACH_PORT_NULL)
+       __mach_port_deallocate (__mach_task_self (), newproc);
+
+      if (portnames)
+       __vm_deallocate (__mach_task_self (),
+                        (vm_address_t) portnames,
+                        nportnames * sizeof (*portnames));
+      if (porttypes)
+       __vm_deallocate (__mach_task_self (),
+                        (vm_address_t) porttypes,
+                        nporttypes * sizeof (*porttypes));
+      if (threads)
+       {
+         for (i = 0; i < nthreads; ++i)
+           __mach_port_deallocate (__mach_task_self (), threads[i]);
+         __vm_deallocate (__mach_task_self (),
+                          (vm_address_t) threads,
+                          nthreads * sizeof (*threads));
+       }
+
+      /* Release malloc locks.  */
+      _hurd_malloc_fork_parent ();
+      call_function_static_weak (__malloc_fork_unlock_parent);
+
+      /* Run things that want to run in the parent to restore it to
+        normality.  Usually prepare hooks and parent hooks are
+        symmetrical: the prepare hook arrests state in some way for the
+        fork, and the parent hook restores the state for the parent to
+        continue executing normally.  */
+      RUN_HOOK (_hurd_fork_parent_hook, ());
+    }
+  else
+    {
+      struct hurd_sigstate *oldstates;
+
+      /* We are the child task.  Unlock the standard port cells, which were
+        locked in the parent when we copied its memory.  The parent has
+        inserted send rights with the names that were in the cells then.  */
+      for (i = 0; i < _hurd_nports; ++i)
+       __spin_unlock (&_hurd_ports[i].lock);
+
+      /* Claim our sigstate structure and unchain the rest: the
+        threads existed in the parent task but don't exist in this
+        task (the child process).  Delay freeing them until later
+        because some of the further setup and unlocking might be
+        required for free to work.  Before we finish cleaning up,
+        we will reclaim the signal thread's sigstate structure (if
+        it had one).  */
+      oldstates = _hurd_sigstates;
+      if (oldstates == ss)
+       oldstates = ss->next;
+      else
+       {
+         while (_hurd_sigstates->next != ss)
+           _hurd_sigstates = _hurd_sigstates->next;
+         _hurd_sigstates->next = ss->next;
+       }
+      ss->next = NULL;
+      _hurd_sigstates = ss;
+      __mutex_unlock (&_hurd_siglock);
+      /* Earlier on, the global sigstate may have been tainted and now needs to
+         be reinitialized.  Nobody is interested in its present state anymore:
+         we're not, the signal thread will be restarted, and there are no other
+         threads.
+
+         We can't simply allocate a fresh global sigstate here, as
+         _hurd_thread_sigstate will call malloc and that will deadlock trying
+         to determine the current thread's sigstate.  */
+#if 0
+      _hurd_thread_sigstate_init (_hurd_global_sigstate, MACH_PORT_NULL);
+#else
+      /* Only reinitialize the lock -- otherwise we might have to do additional
+         setup as done in hurdsig.c:_hurdsig_init.  */
+      __spin_lock_init (&_hurd_global_sigstate->lock);
+#endif
+
+      /* We are one of the (exactly) two threads in this new task, we
+        will take the task-global signals.  */
+      _hurd_sigstate_set_global_rcv (ss);
+
+      /* Fetch our new process IDs from the proc server.  No need to
+        refetch our pgrp; it is always inherited from the parent (so
+        _hurd_pgrp is already correct), and the proc server will send us a
+        proc_newids notification when it changes.  */
+      err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
+                                            &_hurd_orphaned));
+
+      /* Forking clears the trace flag and pending masks.  */
+      __sigemptyset (&_hurdsig_traced);
+      __sigemptyset (&_hurd_global_sigstate->pending);
+      __sigemptyset (&ss->pending);
+
+      __libc_unwind_link_after_fork ();
+
+      /* Release malloc locks.  */
+      _hurd_malloc_fork_child ();
+      call_function_static_weak (__malloc_fork_unlock_child);
+
+      /* Run things that want to run in the child task to set up.  */
+      RUN_HOOK (_hurd_fork_child_hook, ());
+
+      /* Set up proc server-assisted fault recovery for the signal thread.  */
+      _hurdsig_fault_init ();
+
+      /* Start the signal thread listening on the message port.  */
+      if (!err)
+       err = __thread_resume (_hurd_msgport_thread);
+
+      /* Reclaim the signal thread's sigstate structure and free the
+        other old sigstate structures.  */
+      while (oldstates != NULL)
+       {
+         struct hurd_sigstate *next = oldstates->next;
+
+         if (oldstates->thread == _hurd_msgport_thread)
+           {
+             /* If we have a second signal state structure then we
+                must have been through here before--not good.  */
+             assert (_hurd_sigstates->next == 0);
+             _hurd_sigstates->next = oldstates;
+             oldstates->next = 0;
+           }
+         else
+           free (oldstates);
+
+         oldstates = next;
+       }
+
+      /* XXX what to do if we have any errors here? */
+
+      pid = 0;
+    }
+
+  /* Unlock things we locked before creating the child task.
+     They are locked in both the parent and child tasks.  */
+  {
+    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);
+  if (err == EINTR)
+    /* Got a signal while inside an RPC of the critical section, retry again */
+    goto retry;
+
+  return err ? __hurd_fail (err) : pid;
+}
+libc_hidden_def (_Fork)
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
deleted file mode 100644 (file)
index 92a5169..0000000
+++ /dev/null
@@ -1,736 +0,0 @@
-/* Copyright (C) 1994-2021 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <https://www.gnu.org/licenses/>.  */
-
-#include <errno.h>
-#include <unistd.h>
-#include <hurd.h>
-#include <hurd/signal.h>
-#include <hurd/threadvar.h>
-#include <setjmp.h>
-#include <thread_state.h>
-#include <sysdep.h>            /* For stack growth direction.  */
-#include "set-hooks.h"
-#include <assert.h>
-#include "hurdmalloc.h"                /* XXX */
-#include <tls.h>
-#include <malloc/malloc-internal.h>
-#include <nss/nss_database.h>
-#include <unwind-link.h>
-#include <register-atfork.h>
-
-#undef __fork
-
-
-/* Things that want to be locked while forking.  */
-symbol_set_declare (_hurd_fork_locks)
-
-/* Things that want to be called before we fork, to prepare the parent for
-   task_create, when the new child task will inherit our address space.  */
-DEFINE_HOOK (_hurd_fork_prepare_hook, (void));
-
-/* Things that want to be called when we are forking, with the above all
-   locked.  They are passed the task port of the child.  The child process
-   is all set up except for doing proc_child, and has no threads yet.  */
-DEFINE_HOOK (_hurd_fork_setup_hook, (void));
-
-/* Things to be run in the child fork.  */
-DEFINE_HOOK (_hurd_fork_child_hook, (void));
-
-/* Things to be run in the parent fork.  */
-DEFINE_HOOK (_hurd_fork_parent_hook, (void));
-
-
-/* Clone the calling process, creating an exact copy.
-   Return -1 for errors, 0 to the new process,
-   and the process ID of the new process to the old process.  */
-pid_t
-__fork (void)
-{
-  jmp_buf env;
-  pid_t pid;
-  size_t i;
-  error_t err;
-  struct hurd_sigstate *volatile ss;
-  struct nss_database_data nss_database_data;
-
-  __run_fork_handlers (atfork_run_prepare, true);
-
-  ss = _hurd_self_sigstate ();
-retry:
-  __spin_lock (&ss->critical_section_lock);
-
-#undef LOSE
-#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
-
-  if (! setjmp (env))
-    {
-      process_t newproc;
-      task_t newtask;
-      thread_t thread, sigthread;
-      mach_port_urefs_t thread_refs, sigthread_refs;
-      struct machine_thread_state state;
-      mach_msg_type_number_t statecount;
-      mach_port_t *portnames = NULL;
-      mach_msg_type_number_t nportnames = 0;
-      mach_port_type_t *porttypes = NULL;
-      mach_msg_type_number_t nporttypes = 0;
-      thread_t *threads = NULL;
-      mach_msg_type_number_t nthreads = 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, ());
-
-      call_function_static_weak (__nss_database_fork_prepare_parent,
-                                &nss_database_data);
-
-      /* Lock things that want to be locked before we fork.  */
-      {
-       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);
-
-      /* Acquire malloc locks.  This needs to come last because fork
-        handlers may use malloc, and the libio list lock has an
-        indirect malloc dependency as well (via the getdelim
-        function).  */
-      call_function_static_weak (__malloc_fork_lock_parent);
-      _hurd_malloc_fork_prepare ();
-
-      newtask = MACH_PORT_NULL;
-      thread = sigthread = MACH_PORT_NULL;
-      newproc = MACH_PORT_NULL;
-
-      /* Lock all the port cells for the standard ports while we copy the
-        address space.  We want to insert all the send rights into the
-        child with the same names.  */
-      for (i = 0; i < _hurd_nports; ++i)
-       __spin_lock (&_hurd_ports[i].lock);
-      ports_locked = 1;
-
-
-      /* Keep our SS locked while stopping other threads, so they don't get a
-         chance to have it locked in the copied space.  */
-      __spin_lock (&ss->lock);
-      /* Stop all other threads while copying the address space,
-        so nothing changes.  */
-      err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
-      __spin_unlock (&ss->lock);
-      if (!err)
-       {
-         stopped = 1;
-
-#define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
-
-#ifdef XXX_KERNEL_PAGE_FAULT_BUG
-         /* Gag me with a pitchfork.
-            The bug scenario is this:
-
-            - The page containing __mach_task_self_ is paged out.
-            - The signal thread was faulting on that page when we
-              suspended it via proc_dostop.  It holds some lock, or set
-              some busy bit, or somesuch.
-            - Now this thread faults on that same page.
-            - GRATUIOUS DEADLOCK
-
-            We can break the deadlock by aborting the thread that faulted
-            first, which if the bug happened was the signal thread because
-            it is the only other thread and we just suspended it.
-            */
-         __thread_abort (_hurd_msgport_thread);
-#endif
-         /* Create the child task.  It will inherit a copy of our memory.  */
-         err = __task_create (__mach_task_self (),
-#ifdef KERN_INVALID_LEDGER
-                              NULL, 0, /* OSF Mach */
-#endif
-                              1, &newtask);
-       }
-
-      /* Unlock the global signal state lock, so we do not
-        block the signal thread any longer than necessary.  */
-      __mutex_unlock (&_hurd_siglock);
-
-      if (err)
-       LOSE;
-
-      /* Fetch the names of all ports used in this task.  */
-      if (err = __mach_port_names (__mach_task_self (),
-                                  &portnames, &nportnames,
-                                  &porttypes, &nporttypes))
-       LOSE;
-      if (nportnames != nporttypes)
-       {
-         err = EGRATUITOUS;
-         LOSE;
-       }
-
-      /* Get send rights for all the threads in this task.
-        We want to avoid giving these rights to the child.  */
-      if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
-       LOSE;
-
-      /* Get the child process's proc server port.  We will insert it into
-        the child with the same name as we use for our own proc server
-        port; and we will need it to set the child's message port.  */
-      if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
-                                 newtask, &newproc))
-       LOSE;
-
-      /* Insert all our port rights into the child task.  */
-      thread_refs = sigthread_refs = 0;
-      for (i = 0; i < nportnames; ++i)
-       {
-         if (porttypes[i] & MACH_PORT_TYPE_RECEIVE)
-           {
-             /* This is a receive right.  We want to give the child task
-                its own new receive right under the same name.  */
-             if (err = __mach_port_allocate_name (newtask,
-                                                  MACH_PORT_RIGHT_RECEIVE,
-                                                  portnames[i]))
-               LOSE;
-             if (porttypes[i] & MACH_PORT_TYPE_SEND)
-               {
-                 /* Give the child as many send rights for its receive
-                    right as we have for ours.  */
-                 mach_port_urefs_t refs;
-                 mach_port_t port;
-                 mach_msg_type_name_t poly;
-                 if (err = __mach_port_get_refs (__mach_task_self (),
-                                                 portnames[i],
-                                                 MACH_PORT_RIGHT_SEND,
-                                                 &refs))
-                   LOSE;
-                 if (err = __mach_port_extract_right (newtask,
-                                                      portnames[i],
-                                                      MACH_MSG_TYPE_MAKE_SEND,
-                                                      &port, &poly))
-                   LOSE;
-                 if (portnames[i] == _hurd_msgport)
-                   {
-                     /* We just created a receive right for the child's
-                        message port and are about to insert send rights
-                        for it.  Now, while we happen to have a send right
-                        for it, give it to the proc server.  */
-                     mach_port_t old;
-                     if (err = __proc_setmsgport (newproc, port, &old))
-                       LOSE;
-                     if (old != MACH_PORT_NULL)
-                       /* XXX what to do here? */
-                       __mach_port_deallocate (__mach_task_self (), old);
-                     /* The new task will receive its own exceptions
-                        on its message port.  */
-                     if (err =
-#ifdef TASK_EXCEPTION_PORT
-                         __task_set_special_port (newtask,
-                                                  TASK_EXCEPTION_PORT,
-                                                  port)
-#elif defined (EXC_MASK_ALL)
-                         __task_set_exception_ports
-                         (newtask, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
-                                                    | EXC_MASK_MACH_SYSCALL
-                                                    | EXC_MASK_RPC_ALERT),
-                          port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
-#else
-# error task_set_exception_port?
-#endif
-                         )
-                       LOSE;
-                   }
-                 if (err = __mach_port_insert_right (newtask,
-                                                     portnames[i],
-                                                     port,
-                                                     MACH_MSG_TYPE_MOVE_SEND))
-                   LOSE;
-                 if (refs > 1
-                     && (err = __mach_port_mod_refs (newtask,
-                                                     portnames[i],
-                                                     MACH_PORT_RIGHT_SEND,
-                                                     refs - 1)))
-                   LOSE;
-               }
-             if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
-               {
-                 /* Give the child a send-once right for its receive right,
-                    since we have one for ours.  */
-                 mach_port_t port;
-                 mach_msg_type_name_t poly;
-                 if (err = __mach_port_extract_right
-                     (newtask,
-                      portnames[i],
-                      MACH_MSG_TYPE_MAKE_SEND_ONCE,
-                      &port, &poly))
-                   LOSE;
-                 if (err = __mach_port_insert_right
-                     (newtask,
-                      portnames[i], port,
-                      MACH_MSG_TYPE_MOVE_SEND_ONCE))
-                   LOSE;
-               }
-           }
-         else if (porttypes[i]
-                  & (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
-           {
-             /* This is a send right or a dead name.
-                Give the child as many references for it as we have.  */
-             mach_port_urefs_t refs = 0, *record_refs = NULL;
-             mach_port_t insert;
-             mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND;
-             if (portnames[i] == newtask || portnames[i] == newproc)
-               /* Skip the name we use for the child's task or proc ports.  */
-               continue;
-             if (portnames[i] == __mach_task_self ())
-               /* For the name we use for our own task port,
-                  insert the child's task port instead.  */
-               insert = newtask;
-             else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
-               {
-                 /* Use the proc server port for the new task.  */
-                 insert = newproc;
-                 insert_type = MACH_MSG_TYPE_COPY_SEND;
-               }
-             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
-                    after we create it.  */
-                 insert = MACH_PORT_NULL;
-                 record_refs = &thread_refs;
-                 /* Allocate a dead name right for this name as a
-                    placeholder, so the kernel will not chose this name
-                    for any other new port (it might use it for one of the
-                    rights created when a thread is created).  */
-                 if (err = __mach_port_allocate_name
-                     (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
-                   LOSE;
-               }
-             else if (portnames[i] == _hurd_msgport_thread)
-               /* For the name we use for our signal thread's thread port,
-                  we will insert the thread port for the child's signal
-                  thread after we create it.  */
-               {
-                 insert = MACH_PORT_NULL;
-                 record_refs = &sigthread_refs;
-                 /* Allocate a dead name right as a placeholder.  */
-                 if (err = __mach_port_allocate_name
-                     (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
-                   LOSE;
-               }
-             else
-               {
-                 /* Skip the name we use for any of our own thread ports.  */
-                 mach_msg_type_number_t j;
-                 for (j = 0; j < nthreads; ++j)
-                   if (portnames[i] == threads[j])
-                     break;
-                 if (j < nthreads)
-                   continue;
-
-                 /* Copy our own send right.  */
-                 insert = portnames[i];
-               }
-             /* Find out how many user references we have for
-                the send right with this name.  */
-             if (err = __mach_port_get_refs (__mach_task_self (),
-                                             portnames[i],
-                                             MACH_PORT_RIGHT_SEND,
-                                             record_refs ?: &refs))
-               LOSE;
-             if (insert == MACH_PORT_NULL)
-               continue;
-             if (insert == portnames[i]
-                 && (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
-               /* This is a dead name; allocate another dead name
-                  with the same name in the child.  */
-             allocate_dead_name:
-               err = __mach_port_allocate_name (newtask,
-                                                MACH_PORT_RIGHT_DEAD_NAME,
-                                                portnames[i]);
-             else
-               /* Insert the chosen send right into the child.  */
-               err = __mach_port_insert_right (newtask,
-                                               portnames[i],
-                                               insert, insert_type);
-             switch (err)
-               {
-               case KERN_NAME_EXISTS:
-                 {
-                   /* It already has a send right under this name (?!).
-                      Well, it starts out with a send right for its task
-                      port, and inherits the bootstrap and exception ports
-                      from us.  */
-                   mach_port_t childport;
-                   mach_msg_type_name_t poly;
-                   assert (__mach_port_extract_right (newtask, portnames[i],
-                                                      MACH_MSG_TYPE_COPY_SEND,
-                                                      &childport,
-                                                      &poly) == 0
-                           && childport == insert
-                           && __mach_port_deallocate (__mach_task_self (),
-                                                      childport) == 0);
-                   break;
-                 }
-
-               case KERN_INVALID_CAPABILITY:
-                 /* The port just died.  It was a send right,
-                    and now it's a dead name.  */
-                 goto allocate_dead_name;
-
-               default:
-                 LOSE;
-                 break;
-
-               case KERN_SUCCESS:
-                 /* Give the child as many user references as we have.  */
-                 if (refs > 1
-                     && (err = __mach_port_mod_refs (newtask,
-                                                     portnames[i],
-                                                     MACH_PORT_RIGHT_SEND,
-                                                     refs - 1)))
-                   LOSE;
-               }
-           }
-       }
-
-      /* Unlock the standard port cells.  The child must unlock its own
-        copies too.  */
-      for (i = 0; i < _hurd_nports; ++i)
-       __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)))
-       LOSE;
-
-      /* Insert send rights for those threads.  We previously allocated
-        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, ss->thread))
-         || (err = __mach_port_insert_right (newtask, ss->thread,
-                                             thread,
-                                             MACH_MSG_TYPE_COPY_SEND)))
-       LOSE;
-      /* XXX consumed? (_hurd_sigthread is no more) */
-      if (thread_refs > 1
-         && (err = __mach_port_mod_refs (newtask, ss->thread,
-                                         MACH_PORT_RIGHT_SEND,
-                                         thread_refs - 1)))
-       LOSE;
-      if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
-         && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread))
-             || (err = __mach_port_insert_right (newtask,
-                                                 _hurd_msgport_thread,
-                                                 sigthread,
-                                                 MACH_MSG_TYPE_COPY_SEND))))
-       LOSE;
-      if (sigthread_refs > 1
-         && (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
-                                         MACH_PORT_RIGHT_SEND,
-                                         sigthread_refs - 1)))
-       LOSE;
-
-      /* This seems like a convenient juncture to copy the proc server's
-        idea of what addresses our argv and envp are found at from the
-        parent into the child.  Since we happen to know that the child
-        shares our memory image, it is we who should do this copying.  */
-      {
-       vm_address_t argv, envp;
-       err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
-              ?: __proc_set_arg_locations (newproc, argv, envp));
-       if (err)
-         LOSE;
-      }
-
-      /* Set the child signal thread up to run the msgport server function
-        using the same signal thread stack copied from our address space.
-        We fetch the state before longjmp'ing it so that miscellaneous
-        registers not affected by longjmp (such as i386 segment registers)
-        are in their normal default state.  */
-      statecount = MACHINE_THREAD_STATE_COUNT;
-      if (err = __thread_get_state (_hurd_msgport_thread,
-                                   MACHINE_THREAD_STATE_FLAVOR,
-                                   (natural_t *) &state, &statecount))
-       LOSE;
-#ifdef STACK_GROWTH_UP
-      if (__hurd_sigthread_stack_base == 0)
-       {
-         state.SP &= __hurd_threadvar_stack_mask;
-         state.SP += __hurd_threadvar_stack_offset;
-       }
-      else
-       state.SP = __hurd_sigthread_stack_base;
-#else
-      if (__hurd_sigthread_stack_end == 0)
-       {
-         /* The signal thread has a stack assigned by pthread.
-            The threadvar_stack variables conveniently tell us how
-            to get to the highest address in the stack, just below
-            the per-thread variables.  */
-         state.SP &= __hurd_threadvar_stack_mask;
-         state.SP += __hurd_threadvar_stack_offset;
-       }
-      else
-       state.SP = __hurd_sigthread_stack_end;
-#endif
-      MACHINE_THREAD_STATE_SET_PC (&state,
-                                  (unsigned long int) _hurd_msgport_receive);
-
-      /* Do special signal thread setup for TLS if needed.  */
-      if (err = _hurd_tls_fork (sigthread, _hurd_msgport_thread, &state))
-       LOSE;
-
-      if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
-                                   (natural_t *) &state, statecount))
-       LOSE;
-      /* We do not thread_resume SIGTHREAD here because the child
-        fork needs to do more setup before it can take signals.  */
-
-      /* Set the child user thread up to return 1 from the setjmp above.  */
-      _hurd_longjmp_thread_state (&state, env, 1);
-
-      /* Do special thread setup for TLS if needed.  */
-      if (err = _hurd_tls_fork (thread, ss->thread, &state))
-       LOSE;
-
-      if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
-                                   (natural_t *) &state, statecount))
-       LOSE;
-
-      /* Get the PID of the child from the proc server.  We must do this
-        before calling proc_child below, because at that point any
-        authorized POSIX.1 process may kill the child task with SIGKILL.  */
-      if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
-       LOSE;
-
-      /* Register the child with the proc server.  It is important that
-        this be that last thing we do before starting the child thread
-        running.  Once proc_child has been done for the task, it appears
-        as a POSIX.1 process.  Any errors we get must be detected before
-        this point, and the child must have a message port so it responds
-        to POSIX.1 signals.  */
-      if (err = __USEPORT (PROC, __proc_child (port, newtask)))
-       LOSE;
-
-      /* This must be the absolutely last thing we do; we can't assume that
-        the child will remain alive for even a moment once we do this.  We
-        ignore errors because we have committed to the fork and are not
-        allowed to return them after the process becomes visible to
-        POSIX.1 (which happened right above when we called proc_child).  */
-      (void) __thread_resume (thread);
-
-    lose:
-      if (ports_locked)
-       for (i = 0; i < _hurd_nports; ++i)
-         __spin_unlock (&_hurd_ports[i].lock);
-
-      resume_threads ();
-
-      if (newtask != MACH_PORT_NULL)
-       {
-         if (err)
-           __task_terminate (newtask);
-         __mach_port_deallocate (__mach_task_self (), newtask);
-       }
-      if (thread != MACH_PORT_NULL)
-       __mach_port_deallocate (__mach_task_self (), thread);
-      if (sigthread != MACH_PORT_NULL)
-       __mach_port_deallocate (__mach_task_self (), sigthread);
-      if (newproc != MACH_PORT_NULL)
-       __mach_port_deallocate (__mach_task_self (), newproc);
-
-      if (portnames)
-       __vm_deallocate (__mach_task_self (),
-                        (vm_address_t) portnames,
-                        nportnames * sizeof (*portnames));
-      if (porttypes)
-       __vm_deallocate (__mach_task_self (),
-                        (vm_address_t) porttypes,
-                        nporttypes * sizeof (*porttypes));
-      if (threads)
-       {
-         for (i = 0; i < nthreads; ++i)
-           __mach_port_deallocate (__mach_task_self (), threads[i]);
-         __vm_deallocate (__mach_task_self (),
-                          (vm_address_t) threads,
-                          nthreads * sizeof (*threads));
-       }
-
-      /* Release malloc locks.  */
-      _hurd_malloc_fork_parent ();
-      call_function_static_weak (__malloc_fork_unlock_parent);
-
-      /* Run things that want to run in the parent to restore it to
-        normality.  Usually prepare hooks and parent hooks are
-        symmetrical: the prepare hook arrests state in some way for the
-        fork, and the parent hook restores the state for the parent to
-        continue executing normally.  */
-      RUN_HOOK (_hurd_fork_parent_hook, ());
-    }
-  else
-    {
-      struct hurd_sigstate *oldstates;
-
-      /* We are the child task.  Unlock the standard port cells, which were
-        locked in the parent when we copied its memory.  The parent has
-        inserted send rights with the names that were in the cells then.  */
-      for (i = 0; i < _hurd_nports; ++i)
-       __spin_unlock (&_hurd_ports[i].lock);
-
-      /* Claim our sigstate structure and unchain the rest: the
-        threads existed in the parent task but don't exist in this
-        task (the child process).  Delay freeing them until later
-        because some of the further setup and unlocking might be
-        required for free to work.  Before we finish cleaning up,
-        we will reclaim the signal thread's sigstate structure (if
-        it had one).  */
-      oldstates = _hurd_sigstates;
-      if (oldstates == ss)
-       oldstates = ss->next;
-      else
-       {
-         while (_hurd_sigstates->next != ss)
-           _hurd_sigstates = _hurd_sigstates->next;
-         _hurd_sigstates->next = ss->next;
-       }
-      ss->next = NULL;
-      _hurd_sigstates = ss;
-      __mutex_unlock (&_hurd_siglock);
-      /* Earlier on, the global sigstate may have been tainted and now needs to
-         be reinitialized.  Nobody is interested in its present state anymore:
-         we're not, the signal thread will be restarted, and there are no other
-         threads.
-
-         We can't simply allocate a fresh global sigstate here, as
-         _hurd_thread_sigstate will call malloc and that will deadlock trying
-         to determine the current thread's sigstate.  */
-#if 0
-      _hurd_thread_sigstate_init (_hurd_global_sigstate, MACH_PORT_NULL);
-#else
-      /* Only reinitialize the lock -- otherwise we might have to do additional
-         setup as done in hurdsig.c:_hurdsig_init.  */
-      __spin_lock_init (&_hurd_global_sigstate->lock);
-#endif
-
-      /* We are one of the (exactly) two threads in this new task, we
-        will take the task-global signals.  */
-      _hurd_sigstate_set_global_rcv (ss);
-
-      /* Fetch our new process IDs from the proc server.  No need to
-        refetch our pgrp; it is always inherited from the parent (so
-        _hurd_pgrp is already correct), and the proc server will send us a
-        proc_newids notification when it changes.  */
-      err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
-                                            &_hurd_orphaned));
-
-      /* Forking clears the trace flag and pending masks.  */
-      __sigemptyset (&_hurdsig_traced);
-      __sigemptyset (&_hurd_global_sigstate->pending);
-      __sigemptyset (&ss->pending);
-
-      __libc_unwind_link_after_fork ();
-
-      /* Release malloc locks.  */
-      _hurd_malloc_fork_child ();
-      call_function_static_weak (__malloc_fork_unlock_child);
-
-      call_function_static_weak (__nss_database_fork_subprocess,
-                                &nss_database_data);
-
-      /* Run things that want to run in the child task to set up.  */
-      RUN_HOOK (_hurd_fork_child_hook, ());
-
-      /* Set up proc server-assisted fault recovery for the signal thread.  */
-      _hurdsig_fault_init ();
-
-      /* Start the signal thread listening on the message port.  */
-      if (!err)
-       err = __thread_resume (_hurd_msgport_thread);
-
-      /* Reclaim the signal thread's sigstate structure and free the
-        other old sigstate structures.  */
-      while (oldstates != NULL)
-       {
-         struct hurd_sigstate *next = oldstates->next;
-
-         if (oldstates->thread == _hurd_msgport_thread)
-           {
-             /* If we have a second signal state structure then we
-                must have been through here before--not good.  */
-             assert (_hurd_sigstates->next == 0);
-             _hurd_sigstates->next = oldstates;
-             oldstates->next = 0;
-           }
-         else
-           free (oldstates);
-
-         oldstates = next;
-       }
-
-      /* XXX what to do if we have any errors here? */
-
-      pid = 0;
-    }
-
-  /* Unlock things we locked before creating the child task.
-     They are locked in both the parent and child tasks.  */
-  {
-    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);
-  if (err == EINTR)
-    /* Got a signal while inside an RPC of the critical section, retry again */
-    goto retry;
-
-  if (!err)
-    {
-      __run_fork_handlers (pid == 0 ? atfork_run_child : atfork_run_parent,
-                          true);
-    }
-
-  return err ? __hurd_fail (err) : pid;
-}
-libc_hidden_def (__fork)
-
-weak_alias (__fork, fork)
diff --git a/sysdeps/nptl/_Fork.c b/sysdeps/nptl/_Fork.c
new file mode 100644 (file)
index 0000000..6eae146
--- /dev/null
@@ -0,0 +1,52 @@
+/* _Fork implementation.  Linux version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <arch-fork.h>
+#include <pthreadP.h>
+
+/* Pointer to the fork generation counter in the thread library.  */
+extern unsigned long int *__fork_generation_pointer attribute_hidden;
+
+pid_t
+_Fork (void)
+{
+  pid_t pid = arch_fork (&THREAD_SELF->tid);
+  if (pid == 0)
+    {
+      struct pthread *self = THREAD_SELF;
+
+      /* Initialize the robust mutex list setting in the kernel which has
+        been reset during the fork.  We do not check for errors because if
+        it fails here, it must have failed at process startup as well and
+        nobody could have used robust mutexes.
+        Before we do that, we have to clear the list of robust mutexes
+        because we do not inherit ownership of mutexes from the parent.
+        We do not have to set self->robust_head.futex_offset since we do
+        inherit the correct value from the parent.  We do not need to clear
+        the pending operation because it must have been zero when fork was
+        called.  */
+#if __PTHREAD_MUTEX_HAVE_PREV
+      self->robust_prev = &self->robust_head;
+#endif
+      self->robust_head.list = &self->robust_head;
+      INTERNAL_SYSCALL_CALL (set_robust_list, &self->robust_head,
+                            sizeof (struct robust_list_head));
+    }
+  return pid;
+}
+libc_hidden_def (_Fork)
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
deleted file mode 100644 (file)
index 39ab797..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <https://www.gnu.org/licenses/>.  */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sysdep.h>
-#include <libio/libioP.h>
-#include <tls.h>
-#include <hp-timing.h>
-#include <ldsodefs.h>
-#include <stdio-lock.h>
-#include <atomic.h>
-#include <pthreadP.h>
-#include <register-atfork.h>
-#include <arch-fork.h>
-#include <futex-internal.h>
-#include <malloc/malloc-internal.h>
-#include <nss/nss_database.h>
-#include <unwind-link.h>
-#include <sys/single_threaded.h>
-#include <list.h>
-
-static void
-fresetlockfiles (void)
-{
-  _IO_ITER i;
-
-  for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
-    if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0)
-      _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock));
-}
-
-/* In case of a fork() call the memory allocation in the child will be
-   the same but only one thread is running.  All stacks except that of
-   the one running thread are not used anymore.  We have to recycle
-   them.  */
-static void
-reclaim_stacks (void)
-{
-  struct pthread *self = (struct pthread *) THREAD_SELF;
-
-  /* No locking necessary.  The caller is the only stack in use.  But
-     we have to be aware that we might have interrupted a list
-     operation.  */
-
-  if (GL (dl_in_flight_stack) != 0)
-    {
-      bool add_p = GL (dl_in_flight_stack) & 1;
-      list_t *elem = (list_t *) (GL (dl_in_flight_stack) & ~(uintptr_t) 1);
-
-      if (add_p)
-       {
-         /* We always add at the beginning of the list.  So in this case we
-            only need to check the beginning of these lists to see if the
-            pointers at the head of the list are inconsistent.  */
-         list_t *l = NULL;
-
-         if (GL (dl_stack_used).next->prev != &GL (dl_stack_used))
-           l = &GL (dl_stack_used);
-         else if (GL (dl_stack_cache).next->prev != &GL (dl_stack_cache))
-           l = &GL (dl_stack_cache);
-
-         if (l != NULL)
-           {
-             assert (l->next->prev == elem);
-             elem->next = l->next;
-             elem->prev = l;
-             l->next = elem;
-           }
-       }
-      else
-       {
-         /* We can simply always replay the delete operation.  */
-         elem->next->prev = elem->prev;
-         elem->prev->next = elem->next;
-       }
-
-      GL (dl_in_flight_stack) = 0;
-    }
-
-  /* Mark all stacks except the still running one as free.  */
-  list_t *runp;
-  list_for_each (runp, &GL (dl_stack_used))
-    {
-      struct pthread *curp = list_entry (runp, struct pthread, list);
-      if (curp != self)
-       {
-         /* This marks the stack as free.  */
-         curp->tid = 0;
-
-         /* Account for the size of the stack.  */
-         GL (dl_stack_cache_actsize) += curp->stackblock_size;
-
-         if (curp->specific_used)
-           {
-             /* Clear the thread-specific data.  */
-             memset (curp->specific_1stblock, '\0',
-                     sizeof (curp->specific_1stblock));
-
-             curp->specific_used = false;
-
-             for (size_t cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
-               if (curp->specific[cnt] != NULL)
-                 {
-                   memset (curp->specific[cnt], '\0',
-                           sizeof (curp->specific_1stblock));
-
-                   /* We have allocated the block which we do not
-                      free here so re-set the bit.  */
-                   curp->specific_used = true;
-                 }
-           }
-       }
-    }
-
-  /* Add the stack of all running threads to the cache.  */
-  list_splice (&GL (dl_stack_used), &GL (dl_stack_cache));
-
-  /* Remove the entry for the current thread to from the cache list
-     and add it to the list of running threads.  Which of the two
-     lists is decided by the user_stack flag.  */
-  list_del (&self->list);
-
-  /* Re-initialize the lists for all the threads.  */
-  INIT_LIST_HEAD (&GL (dl_stack_used));
-  INIT_LIST_HEAD (&GL (dl_stack_user));
-
-  if (__glibc_unlikely (THREAD_GETMEM (self, user_stack)))
-    list_add (&self->list, &GL (dl_stack_user));
-  else
-    list_add (&self->list, &GL (dl_stack_used));
-}
-
-pid_t
-__libc_fork (void)
-{
-  pid_t pid;
-
-  /* Determine if we are running multiple threads.  We skip some fork
-     handlers in the single-thread case, to make fork safer to use in
-     signal handlers.  POSIX requires that fork is async-signal-safe,
-     but our current fork implementation is not.  */
-  bool multiple_threads = __libc_single_threaded == 0;
-
-  __run_fork_handlers (atfork_run_prepare, multiple_threads);
-
-  struct nss_database_data nss_database_data;
-
-  /* If we are not running multiple threads, we do not have to
-     preserve lock state.  If fork runs from a signal handler, only
-     async-signal-safe functions can be used in the child.  These data
-     structures are only used by unsafe functions, so their state does
-     not matter if fork was called from a signal handler.  */
-  if (multiple_threads)
-    {
-      call_function_static_weak (__nss_database_fork_prepare_parent,
-                                &nss_database_data);
-
-      _IO_list_lock ();
-
-      /* Acquire malloc locks.  This needs to come last because fork
-        handlers may use malloc, and the libio list lock has an
-        indirect malloc dependency as well (via the getdelim
-        function).  */
-      call_function_static_weak (__malloc_fork_lock_parent);
-    }
-
-  pid = arch_fork (&THREAD_SELF->tid);
-
-  if (pid == 0)
-    {
-      struct pthread *self = THREAD_SELF;
-
-      /* See __pthread_once.  */
-      __fork_generation += __PTHREAD_ONCE_FORK_GEN_INCR;
-
-      /* Initialize the robust mutex list setting in the kernel which has
-        been reset during the fork.  We do not check for errors because if
-        it fails here, it must have failed at process startup as well and
-        nobody could have used robust mutexes.
-        Before we do that, we have to clear the list of robust mutexes
-        because we do not inherit ownership of mutexes from the parent.
-        We do not have to set self->robust_head.futex_offset since we do
-        inherit the correct value from the parent.  We do not need to clear
-        the pending operation because it must have been zero when fork was
-        called.  */
-#if __PTHREAD_MUTEX_HAVE_PREV
-      self->robust_prev = &self->robust_head;
-#endif
-      self->robust_head.list = &self->robust_head;
-      INTERNAL_SYSCALL_CALL (set_robust_list, &self->robust_head,
-                            sizeof (struct robust_list_head));
-
-      /* Reset the lock state in the multi-threaded case.  */
-      if (multiple_threads)
-       {
-         __libc_unwind_link_after_fork ();
-
-         /* There is one thread running.  */
-         __nptl_nthreads = 1;
-
-         /* Initialize thread library locks.  */
-         GL (dl_stack_cache_lock) = LLL_LOCK_INITIALIZER;
-         __default_pthread_attr_lock = LLL_LOCK_INITIALIZER;
-
-         /* Release malloc locks.  */
-         call_function_static_weak (__malloc_fork_unlock_child);
-
-         /* Reset the file list.  These are recursive mutexes.  */
-         fresetlockfiles ();
-
-         /* Reset locks in the I/O code.  */
-         _IO_list_resetlock ();
-
-         call_function_static_weak (__nss_database_fork_subprocess,
-                                    &nss_database_data);
-       }
-
-      /* Reset the lock the dynamic loader uses to protect its data.  */
-      __rtld_lock_initialize (GL(dl_load_lock));
-
-      reclaim_stacks ();
-
-      /* Run the handlers registered for the child.  */
-      __run_fork_handlers (atfork_run_child, multiple_threads);
-    }
-  else
-    {
-      /* Release acquired locks in the multi-threaded case.  */
-      if (multiple_threads)
-       {
-         /* Release malloc locks, parent process variant.  */
-         call_function_static_weak (__malloc_fork_unlock_parent);
-
-         /* We execute this even if the 'fork' call failed.  */
-         _IO_list_unlock ();
-       }
-
-      /* Run the handlers registered for the parent.  */
-      __run_fork_handlers (atfork_run_parent, multiple_threads);
-    }
-
-  return pid;
-}
-weak_alias (__libc_fork, __fork)
-libc_hidden_def (__fork)
-weak_alias (__libc_fork, fork)
diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h
new file mode 100644 (file)
index 0000000..3134d7a
--- /dev/null
@@ -0,0 +1,148 @@
+/* System specific fork hooks.  Linux version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _FORK_H
+#define _FORK_H
+
+#include <assert.h>
+#include <ldsodefs.h>
+#include <list.h>
+#include <pthreadP.h>
+#include <sysdep.h>
+
+static inline void
+fork_system_setup (void)
+{
+  /* See __pthread_once.  */
+  __fork_generation += __PTHREAD_ONCE_FORK_GEN_INCR;
+}
+
+static void
+fork_system_setup_after_fork (void)
+{
+  /* There is one thread running.  */
+  __nptl_nthreads = 1;
+
+  /* Initialize thread library locks.  */
+  GL (dl_stack_cache_lock) = LLL_LOCK_INITIALIZER;
+  __default_pthread_attr_lock = LLL_LOCK_INITIALIZER;
+}
+
+/* In case of a fork() call the memory allocation in the child will be
+   the same but only one thread is running.  All stacks except that of
+   the one running thread are not used anymore.  We have to recycle
+   them.  */
+static void
+reclaim_stacks (void)
+{
+  struct pthread *self = (struct pthread *) THREAD_SELF;
+
+  /* No locking necessary.  The caller is the only stack in use.  But
+     we have to be aware that we might have interrupted a list
+     operation.  */
+
+  if (GL (dl_in_flight_stack) != 0)
+    {
+      bool add_p = GL (dl_in_flight_stack) & 1;
+      list_t *elem = (list_t *) (GL (dl_in_flight_stack) & ~(uintptr_t) 1);
+
+      if (add_p)
+       {
+         /* We always add at the beginning of the list.  So in this case we
+            only need to check the beginning of these lists to see if the
+            pointers at the head of the list are inconsistent.  */
+         list_t *l = NULL;
+
+         if (GL (dl_stack_used).next->prev != &GL (dl_stack_used))
+           l = &GL (dl_stack_used);
+         else if (GL (dl_stack_cache).next->prev != &GL (dl_stack_cache))
+           l = &GL (dl_stack_cache);
+
+         if (l != NULL)
+           {
+             assert (l->next->prev == elem);
+             elem->next = l->next;
+             elem->prev = l;
+             l->next = elem;
+           }
+       }
+      else
+       {
+         /* We can simply always replay the delete operation.  */
+         elem->next->prev = elem->prev;
+         elem->prev->next = elem->next;
+       }
+
+      GL (dl_in_flight_stack) = 0;
+    }
+
+  /* Mark all stacks except the still running one as free.  */
+  list_t *runp;
+  list_for_each (runp, &GL (dl_stack_used))
+    {
+      struct pthread *curp = list_entry (runp, struct pthread, list);
+      if (curp != self)
+       {
+         /* This marks the stack as free.  */
+         curp->tid = 0;
+
+         /* Account for the size of the stack.  */
+         GL (dl_stack_cache_actsize) += curp->stackblock_size;
+
+         if (curp->specific_used)
+           {
+             /* Clear the thread-specific data.  */
+             memset (curp->specific_1stblock, '\0',
+                     sizeof (curp->specific_1stblock));
+
+             curp->specific_used = false;
+
+             for (size_t cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
+               if (curp->specific[cnt] != NULL)
+                 {
+                   memset (curp->specific[cnt], '\0',
+                           sizeof (curp->specific_1stblock));
+
+                   /* We have allocated the block which we do not
+                      free here so re-set the bit.  */
+                   curp->specific_used = true;
+                 }
+           }
+       }
+    }
+
+  /* Add the stack of all running threads to the cache.  */
+  list_splice (&GL (dl_stack_used), &GL (dl_stack_cache));
+
+  /* Remove the entry for the current thread to from the cache list
+     and add it to the list of running threads.  Which of the two
+     lists is decided by the user_stack flag.  */
+  list_del (&self->list);
+
+  /* Re-initialize the lists for all the threads.  */
+  INIT_LIST_HEAD (&GL (dl_stack_used));
+  INIT_LIST_HEAD (&GL (dl_stack_user));
+
+  if (__glibc_unlikely (THREAD_GETMEM (self, user_stack)))
+    list_add (&self->list, &GL (dl_stack_user));
+  else
+    list_add (&self->list, &GL (dl_stack_used));
+}
+
+
+#endif
index 35d9397ff8dc69771cbe9525c7282a415f648a06..b846da08f98839aef336868de24850626428509c 100644 (file)
@@ -19,6 +19,9 @@
 #ifndef __ARCH_FORK_H
 #define __ARCH_FORK_H
 
+#include <sysdep.h>
+#include <sched.h>
+#include <signal.h>
 #include <unistd.h>
 
 /* Call the clone syscall with fork semantic.  The CTID address is used