diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c
index bb3eecf..a3c4175 100644
--- a/sysdeps/unix/sysv/linux/spawni.c
+++ b/sysdeps/unix/sysv/linux/spawni.c
@@ -44,11 +44,12 @@
    3. Child must synchronize with parent to enforce 2. and to possible
       return execv issues.

-   The first issue is solved by blocking all signals in child, even the
-   NPTL-internal ones (SIGCANCEL and SIGSETXID).  The second and third issue
-   is done by a stack allocation in parent and a synchronization with using
-   a pipe or waitpid (in case or error).  The pipe has the advantage of
-   allowing the child the communicate an exec error.  */
+   The first issue is solved by blocking all signals in child, even
+   the NPTL-internal ones (SIGCANCEL and SIGSETXID).  The second and
+   third issue is done by a stack allocation in parent, and by using a
+   field in struct spawn_args where the child can write an error
+   code. CLONE_VFORK ensures that the parent does not run until the
+   child has either exec'ed successfully or exited.  */


 /* The Unix standard contains a long explanation of the way to signal
@@ -79,7 +80,6 @@

 struct posix_spawn_args
 {
-  int pipe[2];
   sigset_t oldmask;
   const char *file;
   int (*exec) (const char *, char *const *, char *const *);
@@ -89,6 +89,7 @@ struct posix_spawn_args
   ptrdiff_t argc;
   char *const *envp;
   int xflags;
+  int err;
 };

 /* Older version requires that shell script without shebang definition
@@ -125,11 +126,8 @@ __spawni_child (void *arguments)
   struct posix_spawn_args *args = arguments;
   const posix_spawnattr_t *restrict attr = args->attr;
   const posix_spawn_file_actions_t *file_actions = args->fa;
-  int p = args->pipe[1];
   int ret;

-  close_not_cancel (args->pipe[0]);
-
   /* The child must ensure that no signal handler are enabled because it shared
      memory with parent, so the signal disposition must be either SIG_DFL or
      SIG_IGN.  It does by iterating over all signals and although it could
@@ -203,17 +201,6 @@ __spawni_child (void *arguments)
     {
       struct __spawn_action *action = &file_actions->__actions[cnt];

-      /* Dup the pipe fd onto an unoccupied one to avoid any file
-         operation to clobber it.  */
-      if ((action->action.close_action.fd == p)
-          || (action->action.open_action.fd == p)
-          || (action->action.dup2_action.fd == p))
-        {
-          if ((ret = __dup (p)) < 0)
-        goto fail;
-          p = ret;
-        }
-
       switch (action->tag)
         {
         case spawn_do_close:
@@ -280,14 +267,12 @@ __spawni_child (void *arguments)
      (2.15).  */
   maybe_script_execute (args);

-  ret = -errno;
-
 fail:
-  /* Since sizeof errno < PIPE_BUF, the write is atomic. */
-  ret = -ret;
-  if (ret)
-    while (write_not_cancel (p, &ret, sizeof ret) < 0)
-      continue;
+  /* errno should have an appropriate non-zero value, but make sure
+     that's the case so that our parent knows we failed to
+     exec. There's no EUNKNOWN or EINTERNALBUG, so we use a value
+     which is clearly bogus.  */
+  args->err = errno ? : EHOSTDOWN;
   _exit (SPAWN_ERROR);
 }

@@ -304,9 +289,6 @@ __spawnix (pid_t * pid, const char *file,
   struct posix_spawn_args args;
   int ec;

-  if (__pipe2 (args.pipe, O_CLOEXEC))
-    return errno;
-
   /* To avoid imposing hard limits on posix_spawn{p} the total number of
      arguments is first calculated to allocate a mmap to hold all possible
      values.  */
@@ -333,15 +315,12 @@ __spawnix (pid_t * pid, const char *file,
   void *stack = __mmap (NULL, stack_size, prot,
             MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
   if (__glibc_unlikely (stack == MAP_FAILED))
-    {
-      close_not_cancel (args.pipe[0]);
-      close_not_cancel (args.pipe[1]);
-      return errno;
-    }
+    return errno;

   /* Disable asynchronous cancellation.  */
   int cs = LIBC_CANCEL_ASYNC ();

+  args.err = 0;
   args.file = file;
   args.exec = exec;
   args.fa = file_actions;
@@ -355,9 +334,8 @@ __spawnix (pid_t * pid, const char *file,

   /* The clone flags used will create a new child that will run in the same
      memory space (CLONE_VM) and the execution of calling thread will be
-     suspend until the child calls execve or _exit.  These condition as
-     signal below either by pipe write (_exit with SPAWN_ERROR) or
-     a successful execve.
+     suspend until the child calls execve or _exit.
+
      Also since the calling thread execution will be suspend, there is not
      need for CLONE_SETTLS.  Although parent and child share the same TLS
      namespace, there will be no concurrent access for TLS variables (errno
@@ -365,13 +343,10 @@ __spawnix (pid_t * pid, const char *file,
   new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size,
            CLONE_VM | CLONE_VFORK | SIGCHLD, &args);

-  close_not_cancel (args.pipe[1]);
-
   if (new_pid > 0)
     {
-      if (__read (args.pipe[0], &ec, sizeof ec) != sizeof ec)
-    ec = 0;
-      else
+      ec = args.err;
+      if (ec != 0)
     __waitpid (new_pid, NULL, 0);
     }
   else
@@ -379,8 +354,6 @@ __spawnix (pid_t * pid, const char *file,

   __munmap (stack, stack_size);

-  close_not_cancel (args.pipe[0]);
-
   if ((ec == 0) && (pid != NULL))
     *pid = new_pid;

