Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 8 Mar 2012 12:54:26 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r232693 - in stable/9/sys: kern sys
Message-ID:  <201203081254.q28CsQQc045841@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Thu Mar  8 12:54:26 2012
New Revision: 232693
URL: http://svn.freebsd.org/changeset/base/232693

Log:
  MFC r232048:
  Allow the parent to gather the exit status of the children reparented
  to the debugger.  When reparenting for debugging, keep the child in
  the new orphan list of old parent.  When looping over the children in
  kern_wait(), iterate over both children list and orphan list to search
  for the process by pid.
  
  MFC r232104:
  Restore the return statement erronously removed in the r232048.
  
  In order to keep stable/9 KBI, the p_dbg_child member of struct proc
  was replaced with padding.

Modified:
  stable/9/sys/kern/kern_exit.c
  stable/9/sys/kern/kern_fork.c
  stable/9/sys/kern/sys_process.c
  stable/9/sys/sys/proc.h
Directory Properties:
  stable/9/sys/   (props changed)

Modified: stable/9/sys/kern/kern_exit.c
==============================================================================
--- stable/9/sys/kern/kern_exit.c	Thu Mar  8 12:49:08 2012	(r232692)
+++ stable/9/sys/kern/kern_exit.c	Thu Mar  8 12:54:26 2012	(r232693)
@@ -720,7 +720,6 @@ proc_reap(struct thread *td, struct proc
 	if (p->p_oppid && (t = pfind(p->p_oppid)) != NULL) {
 		PROC_LOCK(p);
 		proc_reparent(p, t);
-		p->p_pptr->p_dbg_child--;
 		p->p_oppid = 0;
 		PROC_UNLOCK(p);
 		pksignal(t, SIGCHLD, p->p_ksi);
@@ -739,6 +738,10 @@ proc_reap(struct thread *td, struct proc
 	LIST_REMOVE(p, p_list);	/* off zombproc */
 	sx_xunlock(&allproc_lock);
 	LIST_REMOVE(p, p_sibling);
+	if (p->p_flag & P_ORPHAN) {
+		LIST_REMOVE(p, p_orphan);
+		p->p_flag &= ~P_ORPHAN;
+	}
 	leavepgrp(p);
 #ifdef PROCDESC
 	if (p->p_procdesc != NULL)
@@ -803,12 +806,53 @@ proc_reap(struct thread *td, struct proc
 	sx_xunlock(&allproc_lock);
 }
 
+static int
+proc_to_reap(struct thread *td, struct proc *p, pid_t pid, int *status,
+    int options, struct rusage *rusage)
+{
+	struct proc *q;
+
+	q = td->td_proc;
+	PROC_LOCK(p);
+	if (pid != WAIT_ANY && p->p_pid != pid && p->p_pgid != -pid) {
+		PROC_UNLOCK(p);
+		return (0);
+	}
+	if (p_canwait(td, p)) {
+		PROC_UNLOCK(p);
+		return (0);
+	}
+
+	/*
+	 * This special case handles a kthread spawned by linux_clone
+	 * (see linux_misc.c).  The linux_wait4 and linux_waitpid
+	 * functions need to be able to distinguish between waiting
+	 * on a process and waiting on a thread.  It is a thread if
+	 * p_sigparent is not SIGCHLD, and the WLINUXCLONE option
+	 * signifies we want to wait for threads and not processes.
+	 */
+	if ((p->p_sigparent != SIGCHLD) ^
+	    ((options & WLINUXCLONE) != 0)) {
+		PROC_UNLOCK(p);
+		return (0);
+	}
+
+	PROC_SLOCK(p);
+	if (p->p_state == PRS_ZOMBIE) {
+		proc_reap(td, p, status, options, rusage);
+		return (-1);
+	}
+	PROC_SUNLOCK(p);
+	PROC_UNLOCK(p);
+	return (1);
+}
+
 int
 kern_wait(struct thread *td, pid_t pid, int *status, int options,
     struct rusage *rusage)
 {
 	struct proc *p, *q;
-	int error, nfound;
+	int error, nfound, ret;
 
 	AUDIT_ARG_PID(pid);
 	AUDIT_ARG_VALUE(options);
@@ -831,37 +875,16 @@ loop:
 	nfound = 0;
 	sx_xlock(&proctree_lock);
 	LIST_FOREACH(p, &q->p_children, p_sibling) {
-		PROC_LOCK(p);
-		if (pid != WAIT_ANY &&
-		    p->p_pid != pid && p->p_pgid != -pid) {
-			PROC_UNLOCK(p);
-			continue;
-		}
-		if (p_canwait(td, p)) {
-			PROC_UNLOCK(p);
+		ret = proc_to_reap(td, p, pid, status, options, rusage);
+		if (ret == 0)
 			continue;
-		}
-
-		/*
-		 * This special case handles a kthread spawned by linux_clone
-		 * (see linux_misc.c).  The linux_wait4 and linux_waitpid
-		 * functions need to be able to distinguish between waiting
-		 * on a process and waiting on a thread.  It is a thread if
-		 * p_sigparent is not SIGCHLD, and the WLINUXCLONE option
-		 * signifies we want to wait for threads and not processes.
-		 */
-		if ((p->p_sigparent != SIGCHLD) ^
-		    ((options & WLINUXCLONE) != 0)) {
-			PROC_UNLOCK(p);
-			continue;
-		}
+		else if (ret == 1)
+			nfound++;
+		else
+			return (0);
 
-		nfound++;
+		PROC_LOCK(p);
 		PROC_SLOCK(p);
-		if (p->p_state == PRS_ZOMBIE) {
-			proc_reap(td, p, status, options, rusage);
-			return (0);
-		}
 		if ((p->p_flag & P_STOPPED_SIG) &&
 		    (p->p_suspcount == p->p_numthreads) &&
 		    (p->p_flag & P_WAITED) == 0 &&
@@ -897,12 +920,31 @@ loop:
 		}
 		PROC_UNLOCK(p);
 	}
+
+	/*
+	 * Look in the orphans list too, to allow the parent to
+	 * collect it's child exit status even if child is being
+	 * debugged.
+	 *
+	 * Debugger detaches from the parent upon successful
+	 * switch-over from parent to child.  At this point due to
+	 * re-parenting the parent loses the child to debugger and a
+	 * wait4(2) call would report that it has no children to wait
+	 * for.  By maintaining a list of orphans we allow the parent
+	 * to successfully wait until the child becomes a zombie.
+	 */
+	LIST_FOREACH(p, &q->p_orphans, p_orphan) {
+		ret = proc_to_reap(td, p, pid, status, options, rusage);
+		if (ret == 0)
+			continue;
+		else if (ret == 1)
+			nfound++;
+		else
+			return (0);
+	}
 	if (nfound == 0) {
 		sx_xunlock(&proctree_lock);
-		if (td->td_proc->p_dbg_child)
-			return (0);
-		else
-			return (ECHILD);
+		return (ECHILD);
 	}
 	if (options & WNOHANG) {
 		sx_xunlock(&proctree_lock);
@@ -940,5 +982,15 @@ proc_reparent(struct proc *child, struct
 	PROC_UNLOCK(child->p_pptr);
 	LIST_REMOVE(child, p_sibling);
 	LIST_INSERT_HEAD(&parent->p_children, child, p_sibling);
+
+	if (child->p_flag & P_ORPHAN) {
+		LIST_REMOVE(child, p_orphan);
+		child->p_flag &= ~P_ORPHAN;
+	}
+	if (child->p_flag & P_TRACED) {
+		LIST_INSERT_HEAD(&child->p_pptr->p_orphans, child, p_orphan);
+		child->p_flag |= P_ORPHAN;
+	}
+
 	child->p_pptr = parent;
 }

Modified: stable/9/sys/kern/kern_fork.c
==============================================================================
--- stable/9/sys/kern/kern_fork.c	Thu Mar  8 12:49:08 2012	(r232692)
+++ stable/9/sys/kern/kern_fork.c	Thu Mar  8 12:54:26 2012	(r232693)
@@ -590,6 +590,7 @@ do_fork(struct thread *td, int flags, st
 	LIST_INSERT_AFTER(p1, p2, p_pglist);
 	PGRP_UNLOCK(p1->p_pgrp);
 	LIST_INIT(&p2->p_children);
+	LIST_INIT(&p2->p_orphans);
 
 	callout_init(&p2->p_itcallout, CALLOUT_MPSAFE);
 

Modified: stable/9/sys/kern/sys_process.c
==============================================================================
--- stable/9/sys/kern/sys_process.c	Thu Mar  8 12:49:08 2012	(r232692)
+++ stable/9/sys/kern/sys_process.c	Thu Mar  8 12:54:26 2012	(r232693)
@@ -841,8 +841,6 @@ kern_ptrace(struct thread *td, int req, 
 		p->p_flag |= P_TRACED;
 		p->p_oppid = p->p_pptr->p_pid;
 		if (p->p_pptr != td->td_proc) {
-			/* Remember that a child is being debugged(traced). */
-			p->p_pptr->p_dbg_child++;
 			proc_reparent(p, td->td_proc);
 		}
 		data = SIGSTOP;
@@ -931,7 +929,6 @@ kern_ptrace(struct thread *td, int req, 
 					PROC_UNLOCK(pp);
 				PROC_LOCK(p);
 				proc_reparent(p, pp);
-				p->p_pptr->p_dbg_child--;
 				if (pp == initproc)
 					p->p_sigparent = SIGCHLD;
 			}

Modified: stable/9/sys/sys/proc.h
==============================================================================
--- stable/9/sys/sys/proc.h	Thu Mar  8 12:49:08 2012	(r232692)
+++ stable/9/sys/sys/proc.h	Thu Mar  8 12:54:26 2012	(r232693)
@@ -506,8 +506,7 @@ struct proc {
 /* The following fields are all zeroed upon creation in fork. */
 #define	p_startzero	p_oppid
 	pid_t		p_oppid;	/* (c + e) Save ppid in ptrace. XXX */
-	int		p_dbg_child;	/* (c + e) # of debugged children in
-							ptrace. */
+	int		p_pad_dbg_child;
 	struct vmspace	*p_vmspace;	/* (b) Address space. */
 	u_int		p_swtick;	/* (c) Tick when swapped in or out. */
 	struct itimerval p_realtimer;	/* (c) Alarm timer. */
@@ -575,6 +574,14 @@ struct proc {
 					   after fork. */
 	uint64_t	p_prev_runtime;	/* (c) Resource usage accounting. */
 	struct racct	*p_racct;	/* (b) Resource accounting. */
+	/*
+	 * An orphan is the child that has beed re-parented to the
+	 * debugger as a result of attaching to it.  Need to keep
+	 * track of them for parent to be able to collect the exit
+	 * status of what used to be children.
+	 */
+	LIST_ENTRY(proc) p_orphan;	/* (e) List of orphan processes. */
+	LIST_HEAD(, proc) p_orphans;	/* (e) Pointer to list of orphans. */
 };
 
 #define	p_session	p_pgrp->pg_session
@@ -613,6 +620,7 @@ struct proc {
 #define	P_HWPMC		0x800000 /* Process is using HWPMCs */
 
 #define	P_JAILED	0x1000000 /* Process is in jail. */
+#define	P_ORPHAN	0x2000000 /* Orphaned. */
 #define	P_INEXEC	0x4000000 /* Process is in execve(). */
 #define	P_STATCHILD	0x8000000 /* Child process stopped or exited. */
 #define	P_INMEM		0x10000000 /* Loaded into memory. */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203081254.q28CsQQc045841>