diff --git a/gdb/NEWS b/gdb/NEWS
index 00cca93..db62e35 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -64,8 +64,11 @@ Qbtrace-conf:bts:size
   Set the requested ring buffer size for branch tracing in BTS format.
 
 T Stop Reply Packet's reason
-  The T stop reply packet supports a new stop reason 'xfork', which
-  signifies that the specified inferior has executed a fork.
+  The T stop reply packet supports new stop reasons 'xfork', 'vfork',
+  and 'vforkdone'.  The 'xfork' and 'vfork' reasons signify that the
+  specified inferior has executed a fork or vfork, respectively. The
+  'vforkdone' reason signifies that the a vforked child process has
+  executed either an exec or exit.
 
 * The info record command now shows the recording format and the
   branch tracing configuration for the current thread when using
@@ -81,8 +84,8 @@ T Stop Reply Packet's reason
   GDB's qSupported query.
 
   GDBserver extended-remote Linux targets now provides basic support
-  for fork events.  This enables follow-fork-mode and detach-on-fork
-  for those targets with Linux kernels 2.5.60 and later.
+  for fork and vfork events.  This enables follow-fork-mode and
+  detach-on-fork for those targets with Linux kernels 2.5.60 and later.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2276976..e23b49c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35190,6 +35190,20 @@ The packet indicates that @code{fork} was called, and @var{r}
 is the ptid of the new child process.  This packet is only
 applicable to targets that support fork events.
 
+@cindex vfork events, remote reply
+@item vfork
+The packet indicates that @code{vfork} was called, and @var{r}
+is the ptid of the new child process.  This packet is only
+applicable to targets that support vfork events.
+
+@cindex vforkdone events, remote reply
+@item vforkdone
+The packet indicates that a child process created by a vfork
+has either called @code{exec} or terminated, so that the
+address spaces of the parent and child process are no longer
+shared. The @var{r} part is ignored.  This packet is only
+applicable to targets that support vforkdone events.
+
 @cindex replay log events, remote reply
 @item replaylog
 The packet indicates that the target cannot continue replaying 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 9a50353..5f9f9d4 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -382,7 +382,8 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
   struct thread_info *event_thr = get_lwp_thread (event_child);
   struct lwp_info *new_lwp;
 
-  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE))
+  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
+      || (event == PTRACE_EVENT_CLONE))
     {
       ptid_t ptid;
       unsigned long new_pid;
@@ -408,7 +409,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
-      if (event == PTRACE_EVENT_FORK)
+      if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
 	{
 	  struct process_info *parent_proc;
 	  struct process_info *child_proc;
@@ -453,8 +454,13 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 
 	  /* Save fork info in the parent thread.  */
 	  parent_lwp = get_thread_lwp (event_thr);
-	  parent_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  if (event == PTRACE_EVENT_FORK)
+	    parent_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  else if (event == PTRACE_EVENT_VFORK)
+	    parent_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+
 	  parent_lwp->waitstatus.value.related_pid = ptid;
+
 	  /* The status_pending field contains bits denoting the
 	     extended event, so when the pending event is handled,
 	     the handler will look at lwp->waitstatus.  */
@@ -497,6 +503,14 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
       /* Don't report the event.  */
       return 1;
     }
+  else if (event == PTRACE_EVENT_VFORK_DONE)
+    {
+      struct lwp_info *parent_lwp;
+
+      parent_lwp = get_thread_lwp (event_thr);
+      parent_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+      return 0;
+    }
 
   internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
@@ -1846,6 +1860,8 @@ linux_low_enable_events (pid_t pid, int attached)
 {
   if (!report_fork_events)
     linux_ptrace_clear_flags (PTRACE_O_TRACEFORK);
+  if (!report_vfork_events)
+    linux_ptrace_clear_flags (PTRACE_O_TRACEVFORK);
 
   linux_enable_event_reporting (pid, attached);
 }
@@ -2528,7 +2544,9 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
   if (waitstatus == NULL)
     return 0;
 
-  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
 }
 
 /* Wait for process, returns status.  */
@@ -6355,6 +6373,8 @@ initialize_low (void)
   initialize_low_arch ();
 
   /* Enable extended ptrace events.  */
-  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
+  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK
+					       | PTRACE_O_TRACEVFORK
+					       | PTRACE_O_TRACEVFORKDONE);
   linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 6191e58..56f4246 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1115,15 +1115,19 @@ prepare_resume_reply (char *buf, ptid_t ptid,
     {
     case TARGET_WAITKIND_STOPPED:
     case TARGET_WAITKIND_FORKED:
+    case TARGET_WAITKIND_VFORKED:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
 	struct regcache *regcache;
 
-	if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	if ((status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	     || (status->kind == TARGET_WAITKIND_VFORKED
+		 && report_vfork_events))
 	  {
 	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
-	    const char *event = "xfork";
+	    const char *event = (status->kind == TARGET_WAITKIND_FORKED
+				 ? "xfork" : "vfork");
 
 	    sprintf (buf, "T%02x%s:", signal, event);
 	    buf += strlen (buf);
@@ -1235,6 +1239,15 @@ prepare_resume_reply (char *buf, ptid_t ptid,
       else
 	sprintf (buf, "X%02x", status->value.sig);
       break;
+    case TARGET_WAITKIND_VFORK_DONE:
+      if (multi_process)
+	{
+	  enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	  const char *event = "vforkdone";
+
+	  sprintf (buf, "T%02x%s:;", signal, event);
+	}
+      break;
     default:
       error ("unhandled waitkind");
       break;
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index ce6ffb9..5284dac 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -85,6 +85,7 @@ extern int disable_packet_qfThreadInfo;
 extern int run_once;
 extern int multi_process;
 extern int report_fork_events;
+extern int report_vfork_events;
 extern int non_stop;
 
 extern int disable_randomization;
diff --git a/gdb/remote.c b/gdb/remote.c
index 3a6c6cb..13fd837 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4496,15 +4496,20 @@ remote_follow_fork (struct target_ops *ops, int follow_child,
 {
   struct remote_state *rs = get_remote_state ();
 
+  /* If fork events aren't supported, then return.  We know that if
+     fork events are supported, then so are vfork events, since they
+     were both introduced in the same version of the linux kernel.  */
   if (remote_fork_event_p (rs))
     {
       if (detach_fork && !follow_child)
 	{
 	  ptid_t parent_ptid;
 	  ptid_t child_ptid;
+	  enum target_waitkind kind;
 
-	  gdb_assert (inferior_thread ()->pending_follow.kind
-		      == TARGET_WAITKIND_FORKED);
+	  kind = inferior_thread ()->pending_follow.kind;
+	  gdb_assert (kind == TARGET_WAITKIND_FORKED
+		      || kind == TARGET_WAITKIND_VFORKED);
 
 	  /* remote_detach_1 detaches inferior_ptid, which is currently
 	     the ptid of the parent.  Switch inferior_ptid to the ptid
@@ -5667,6 +5672,23 @@ Packet: '%s'\n"),
 	      event->ws.value.related_pid = read_ptid (++p1, &p);
 	      event->ws.kind = TARGET_WAITKIND_FORKED;
 	    }
+	  else if (strncmp (p, "vfork", p1 - p) == 0)
+	    {
+	      event->ws.value.related_pid = read_ptid (++p1, &p);
+	      event->ws.kind = TARGET_WAITKIND_VFORKED;
+	    }
+	  else if (strncmp (p, "vforkdone", p1 - p) == 0)
+	    {
+	      char *p_temp;
+
+	      p1++;
+	      p_temp = p1;
+	      while (*p_temp && *p_temp != ';')
+		p_temp++;
+
+	      event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
+	      p = p_temp;
+	    }
 	  else
 	    {
 	      ULONGEST pnum;
