git: 328a4f451f14 - stable/12 - jobc: rework detection of orphaned groups.

Konstantin Belousov kib at FreeBSD.org
Tue Feb 9 08:36:55 UTC 2021


The branch stable/12 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=328a4f451f14fb4d830c74c12445a4a80ee61b4c

commit 328a4f451f14fb4d830c74c12445a4a80ee61b4c
Author:     Konstantin Belousov <kib at FreeBSD.org>
AuthorDate: 2020-12-29 00:41:56 +0000
Commit:     Konstantin Belousov <kib at FreeBSD.org>
CommitDate: 2021-02-09 08:35:50 +0000

    jobc: rework detection of orphaned groups.
    
    Tested by:      pho
    
    For MFC, pg_jobc member is left in struct pgrp, but it is unused now.
    
    (cherry picked from commit 5844bd058aed6f3d0c8cbbddd6aa95993ece0189)
---
 lib/libkvm/kvm_proc.c |   2 +-
 sys/kern/kern_proc.c  | 209 ++++++++++++++------------------------------------
 sys/kern/kern_sig.c   |   6 +-
 sys/kern/tty.c        |   8 +-
 sys/sys/proc.h        |   3 +
 5 files changed, 68 insertions(+), 160 deletions(-)

diff --git a/lib/libkvm/kvm_proc.c b/lib/libkvm/kvm_proc.c
index c97a347decd7..d13aa762652b 100644
--- a/lib/libkvm/kvm_proc.c
+++ b/lib/libkvm/kvm_proc.c
@@ -272,7 +272,7 @@ kvm_proclist(kvm_t *kd, int what, int arg, struct proc *p,
 			return (-1);
 		}
 		kp->ki_pgid = pgrp.pg_id;
-		kp->ki_jobc = pgrp.pg_jobc;
+		kp->ki_jobc = -1;	/* Or calculate?  Arguably not. */
 		if (KREAD(kd, (u_long)pgrp.pg_session, &sess)) {
 			_kvm_err(kd, kd->program, "can't read session at %p",
 				pgrp.pg_session);
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index 9798abe96708..5b7a663f0d62 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -107,14 +107,12 @@ MALLOC_DEFINE(M_SESSION, "session", "session header");
 static MALLOC_DEFINE(M_PROC, "proc", "Proc structures");
 MALLOC_DEFINE(M_SUBPROC, "subproc", "Proc sub-structures");
 
-static void fixjobc_enterpgrp(struct proc *p, struct pgrp *pgrp);
 static void doenterpgrp(struct proc *, struct pgrp *);
 static void orphanpg(struct pgrp *pg);
 static void fill_kinfo_aggregate(struct proc *p, struct kinfo_proc *kp);
 static void fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp);
 static void fill_kinfo_thread(struct thread *td, struct kinfo_proc *kp,
     int preferthread);
-static void pgadjustjobc(struct pgrp *pgrp, bool entering);
 static void pgdelete(struct pgrp *);
 static int pgrp_init(void *mem, int size, int flags);
 static int proc_ctor(void *mem, int size, void *arg, int flags);
@@ -516,13 +514,13 @@ enterpgrp(struct proc *p, pid_t pgid, struct pgrp *pgrp, struct session *sess)
 	}
 	pgrp->pg_id = pgid;
 	LIST_INIT(&pgrp->pg_members);
+	pgrp->pg_flags = 0;
 
 	/*
 	 * As we have an exclusive lock of proctree_lock,
 	 * this should not deadlock.
 	 */
 	LIST_INSERT_HEAD(PGRPHASH(pgid), pgrp, pg_hash);
-	pgrp->pg_jobc = 0;
 	SLIST_INIT(&pgrp->pg_sigiolst);
 	PGRP_UNLOCK(pgrp);
 
@@ -562,6 +560,7 @@ static bool
 isjobproc(struct proc *q, struct pgrp *pgrp)
 {
 	sx_assert(&proctree_lock, SX_LOCKED);
+
 	return (q->p_pgrp != pgrp &&
 	    q->p_pgrp->pg_session == pgrp->pg_session);
 }
@@ -571,7 +570,7 @@ jobc_reaper(struct proc *p)
 {
 	struct proc *pp;
 
-	sx_assert(&proctree_lock, SX_LOCKED);
+	sx_assert(&proctree_lock, SA_LOCKED);
 
 	for (pp = p;;) {
 		pp = pp->p_reaper;
@@ -582,43 +581,40 @@ jobc_reaper(struct proc *p)
 }
 
 static struct proc *
-jobc_parent(struct proc *p)
+jobc_parent(struct proc *p, struct proc *p_exiting)
 {
 	struct proc *pp;
 
-	sx_assert(&proctree_lock, SX_LOCKED);
+	sx_assert(&proctree_lock, SA_LOCKED);
 
 	pp = proc_realparent(p);
-	if (pp->p_pptr == NULL ||
+	if (pp->p_pptr == NULL || pp == p_exiting ||
 	    (pp->p_treeflag & P_TREE_GRPEXITED) == 0)
 		return (pp);
 	return (jobc_reaper(pp));
 }
 
-#ifdef INVARIANTS
-static void
-check_pgrp_jobc(struct pgrp *pgrp)
+static int
+pgrp_calc_jobc(struct pgrp *pgrp)
 {
 	struct proc *q;
 	int cnt;
 
-	sx_assert(&proctree_lock, SX_LOCKED);
-	PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED);
+#ifdef INVARIANTS
+	if (!mtx_owned(&pgrp->pg_mtx))
+		sx_assert(&proctree_lock, SA_LOCKED);
+#endif
 
 	cnt = 0;
-	PGRP_LOCK(pgrp);
 	LIST_FOREACH(q, &pgrp->pg_members, p_pglist) {
 		if ((q->p_treeflag & P_TREE_GRPEXITED) != 0 ||
 		    q->p_pptr == NULL)
 			continue;
-		if (isjobproc(jobc_parent(q), pgrp))
+		if (isjobproc(jobc_parent(q, NULL), pgrp))
 			cnt++;
 	}
-	KASSERT(pgrp->pg_jobc == cnt, ("pgrp %d %p pg_jobc %d cnt %d",
-	    pgrp->pg_id, pgrp, pgrp->pg_jobc, cnt));
-	PGRP_UNLOCK(pgrp);
+	return (cnt);
 }
-#endif
 
 /*
  * Move p to a process group
@@ -627,6 +623,7 @@ static void
 doenterpgrp(struct proc *p, struct pgrp *pgrp)
 {
 	struct pgrp *savepgrp;
+	struct proc *pp;
 
 	sx_assert(&proctree_lock, SX_XLOCKED);
 	PROC_LOCK_ASSERT(p, MA_NOTOWNED);
@@ -635,24 +632,19 @@ doenterpgrp(struct proc *p, struct pgrp *pgrp)
 	SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED);
 
 	savepgrp = p->p_pgrp;
-
-#ifdef INVARIANTS
-	check_pgrp_jobc(pgrp);
-	check_pgrp_jobc(savepgrp);
-#endif
-
-	/*
-	 * Adjust eligibility of affected pgrps to participate in job control.
-	 */
-	fixjobc_enterpgrp(p, pgrp);
+	pp = jobc_parent(p, NULL);
 
 	PGRP_LOCK(pgrp);
 	PGRP_LOCK(savepgrp);
+	if (isjobproc(pp, savepgrp) && pgrp_calc_jobc(savepgrp) == 1)
+		orphanpg(savepgrp);
 	PROC_LOCK(p);
 	LIST_REMOVE(p, p_pglist);
 	p->p_pgrp = pgrp;
 	PROC_UNLOCK(p);
 	LIST_INSERT_HEAD(&pgrp->pg_members, p, p_pglist);
+	if (isjobproc(pp, pgrp))
+		pgrp->pg_flags &= ~PGRP_ORPHANED;
 	PGRP_UNLOCK(savepgrp);
 	PGRP_UNLOCK(pgrp);
 	if (LIST_EMPTY(&savepgrp->pg_members))
@@ -716,102 +708,6 @@ pgdelete(struct pgrp *pgrp)
 	sess_release(savesess);
 }
 
-static void
-pgadjustjobc(struct pgrp *pgrp, bool entering)
-{
-
-	PGRP_LOCK(pgrp);
-	if (entering) {
-		MPASS(pgrp->pg_jobc >= 0);
-		pgrp->pg_jobc++;
-	} else {
-		MPASS(pgrp->pg_jobc > 0);
-		--pgrp->pg_jobc;
-		if (pgrp->pg_jobc == 0)
-			orphanpg(pgrp);
-	}
-	PGRP_UNLOCK(pgrp);
-}
-
-static void
-fixjobc_enterpgrp_q(struct pgrp *pgrp, struct proc *p, struct proc *q, bool adj)
-{
-	struct pgrp *childpgrp;
-	bool future_jobc;
-
-	sx_assert(&proctree_lock, SX_LOCKED);
-
-	if ((q->p_treeflag & P_TREE_GRPEXITED) != 0)
-		return;
-	childpgrp = q->p_pgrp;
-	future_jobc = childpgrp != pgrp &&
-	    childpgrp->pg_session == pgrp->pg_session;
-
-	if ((adj && !isjobproc(p, childpgrp) && future_jobc) ||
-	    (!adj && isjobproc(p, childpgrp) && !future_jobc))
-		pgadjustjobc(childpgrp, adj);
-}
-
-/*
- * Adjust pgrp jobc counters when specified process changes process group.
- * We count the number of processes in each process group that "qualify"
- * the group for terminal job control (those with a parent in a different
- * process group of the same session).  If that count reaches zero, the
- * process group becomes orphaned.  Check both the specified process'
- * process group and that of its children.
- * We increment eligibility counts before decrementing, otherwise we
- * could reach 0 spuriously during the decrement.
- */
-static void
-fixjobc_enterpgrp(struct proc *p, struct pgrp *pgrp)
-{
-	struct proc *q;
-
-	sx_assert(&proctree_lock, SX_LOCKED);
-	PROC_LOCK_ASSERT(p, MA_NOTOWNED);
-	PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED);
-	SESS_LOCK_ASSERT(pgrp->pg_session, MA_NOTOWNED);
-
-	if (p->p_pgrp == pgrp)
-		return;
-
-	if (isjobproc(jobc_parent(p), pgrp))
-		pgadjustjobc(pgrp, true);
-	LIST_FOREACH(q, &p->p_children, p_sibling) {
-		if ((q->p_treeflag & P_TREE_ORPHANED) != 0)
-			continue;
-		fixjobc_enterpgrp_q(pgrp, p, q, true);
-	}
-	LIST_FOREACH(q, &p->p_orphans, p_orphan)
-		fixjobc_enterpgrp_q(pgrp, p, q, true);
-
-	if (isjobproc(jobc_parent(p), p->p_pgrp))
-		pgadjustjobc(p->p_pgrp, false);
-	LIST_FOREACH(q, &p->p_children, p_sibling) {
-		if ((q->p_treeflag & P_TREE_ORPHANED) != 0)
-			continue;
-		fixjobc_enterpgrp_q(pgrp, p, q, false);
-	}
-	LIST_FOREACH(q, &p->p_orphans, p_orphan)
-		fixjobc_enterpgrp_q(pgrp, p, q, false);
-}
-
-static void
-fixjobc_kill_q(struct proc *p, struct proc *q, bool adj)
-{
-	struct pgrp *childpgrp;
-
-	sx_assert(&proctree_lock, SX_LOCKED);
-
-	if ((q->p_treeflag & P_TREE_GRPEXITED) != 0)
-		return;
-	childpgrp = q->p_pgrp;
-
-	if ((adj && isjobproc(jobc_reaper(q), childpgrp) &&
-	    !isjobproc(p, childpgrp)) || (!adj && !isjobproc(jobc_reaper(q),
-	    childpgrp) && isjobproc(p, childpgrp)))
-		pgadjustjobc(childpgrp, adj);
-}
 
 static void
 fixjobc_kill(struct proc *p)
@@ -824,9 +720,6 @@ fixjobc_kill(struct proc *p)
 	pgrp = p->p_pgrp;
 	PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED);
 	SESS_LOCK_ASSERT(pgrp->pg_session, MA_NOTOWNED);
-#ifdef INVARIANTS
-	check_pgrp_jobc(pgrp);
-#endif
 
 	/*
 	 * p no longer affects process group orphanage for children.
@@ -837,35 +730,46 @@ fixjobc_kill(struct proc *p)
 	p->p_treeflag |= P_TREE_GRPEXITED;
 
 	/*
-	 * Check p's parent to see whether p qualifies its own process
-	 * group; if so, adjust count for p's process group.
+	 * Check if exiting p orphans its own group.
 	 */
-	if (isjobproc(jobc_parent(p), pgrp))
-		pgadjustjobc(pgrp, false);
+	pgrp = p->p_pgrp;
+	if (isjobproc(jobc_parent(p, NULL), pgrp)) {
+		PGRP_LOCK(pgrp);
+		if (pgrp_calc_jobc(pgrp) == 0)
+			orphanpg(pgrp);
+		PGRP_UNLOCK(pgrp);
+	}
 
 	/*
 	 * Check this process' children to see whether they qualify
-	 * their process groups after reparenting to reaper.  If so,
-	 * adjust counts for children's process groups.
+	 * their process groups after reparenting to reaper.
 	 */
 	LIST_FOREACH(q, &p->p_children, p_sibling) {
-		if ((q->p_treeflag & P_TREE_ORPHANED) != 0)
-			continue;
-		fixjobc_kill_q(p, q, true);
+		pgrp = q->p_pgrp;
+		PGRP_LOCK(pgrp);
+		if (pgrp_calc_jobc(pgrp) == 0) {
+			/*
+			 * We want to handle exactly the children that
+			 * has p as realparent.  Then, when calculating
+			 * jobc_parent for children, we should ignore
+			 * P_TREE_GRPEXITED flag already set on p.
+			 */
+			if (jobc_parent(q, p) == p && isjobproc(p, pgrp))
+				orphanpg(pgrp);
+		} else
+			pgrp->pg_flags &= ~PGRP_ORPHANED;
+		PGRP_UNLOCK(pgrp);
 	}
-	LIST_FOREACH(q, &p->p_orphans, p_orphan)
-		fixjobc_kill_q(p, q, true);
-	LIST_FOREACH(q, &p->p_children, p_sibling) {
-		if ((q->p_treeflag & P_TREE_ORPHANED) != 0)
-			continue;
-		fixjobc_kill_q(p, q, false);
+	LIST_FOREACH(q, &p->p_orphans, p_orphan) {
+		pgrp = q->p_pgrp;
+		PGRP_LOCK(pgrp);
+		if (pgrp_calc_jobc(pgrp) == 0) {
+			if (isjobproc(p, pgrp))
+				orphanpg(pgrp);
+		} else
+			pgrp->pg_flags &= ~PGRP_ORPHANED;
+		PGRP_UNLOCK(pgrp);
 	}
-	LIST_FOREACH(q, &p->p_orphans, p_orphan)
-		fixjobc_kill_q(p, q, false);
-
-#ifdef INVARIANTS
-	check_pgrp_jobc(pgrp);
-#endif
 }
 
 void
@@ -929,8 +833,8 @@ killjobc(void)
 }
 
 /*
- * A process group has become orphaned;
- * if there are any stopped processes in the group,
+ * A process group has become orphaned, mark it as such for signal
+ * delivery code.  If there are any stopped processes in the group,
  * hang-up all process in that group.
  */
 static void
@@ -940,6 +844,8 @@ orphanpg(struct pgrp *pg)
 
 	PGRP_LOCK_ASSERT(pg, MA_OWNED);
 
+	pg->pg_flags |= PGRP_ORPHANED;
+
 	LIST_FOREACH(p, &pg->pg_members, p_pglist) {
 		PROC_LOCK(p);
 		if (P_SHOULDSTOP(p) == P_STOPPED_SIG) {
@@ -1172,7 +1078,7 @@ fill_kinfo_proc_pgrp(struct proc *p, struct kinfo_proc *kp)
 		return;
 
 	kp->ki_pgid = pgrp->pg_id;
-	kp->ki_jobc = pgrp->pg_jobc;
+	kp->ki_jobc = pgrp_calc_jobc(pgrp);
 
 	sp = pgrp->pg_session;
 	tp = NULL;
@@ -1320,7 +1226,6 @@ fill_kinfo_thread(struct thread *td, struct kinfo_proc *kp, int preferthread)
 void
 fill_kinfo_proc(struct proc *p, struct kinfo_proc *kp)
 {
-
 	MPASS(FIRST_THREAD_IN_PROC(p) != NULL);
 
 	bzero(kp, sizeof(*kp));
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 9fbb6d86457a..07102f6de5a5 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2209,7 +2209,7 @@ tdsendsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
 		 * and don't clear any pending SIGCONT.
 		 */
 		if ((prop & SIGPROP_TTYSTOP) != 0 &&
-		    p->p_pgrp->pg_jobc == 0 &&
+		    (p->p_pgrp->pg_flags & PGRP_ORPHANED) != 0 &&
 		    action == SIG_DFL) {
 			if (ksi && (ksi->ksi_flags & KSI_INS))
 				ksiginfo_tryfree(ksi);
@@ -2952,8 +2952,8 @@ issignal(struct thread *td)
 			if (prop & SIGPROP_STOP) {
 				mtx_unlock(&ps->ps_mtx);
 				if ((p->p_flag & (P_TRACED | P_WEXIT |
-				    P_SINGLE_EXIT)) != 0 ||
-				    (p->p_pgrp->pg_jobc == 0 &&
+				    P_SINGLE_EXIT)) != 0 || ((p->p_pgrp->
+				    pg_flags & PGRP_ORPHANED) != 0 &&
 				    (prop & SIGPROP_TTYSTOP) != 0)) {
 					mtx_lock(&ps->ps_mtx);
 					break;	/* == ignore */
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index 693032908b3a..eea5d1b26ddd 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -461,7 +461,8 @@ tty_wait_background(struct tty *tp, struct thread *td, int sig)
 			return (sig == SIGTTOU ? 0 : EIO);
 		}
 
-		if ((p->p_flag & P_PPWAIT) != 0 || pg->pg_jobc == 0) {
+		if ((p->p_flag & P_PPWAIT) != 0 ||
+		    (pg->pg_flags & PGRP_ORPHANED) != 0) {
 			/* Don't allow the action to happen. */
 			PROC_UNLOCK(p);
 			PGRP_UNLOCK(pg);
@@ -2380,9 +2381,8 @@ DB_SHOW_COMMAND(tty, db_show_tty)
 	_db_show_hooks("\t", tp->t_hook);
 
 	/* Process info. */
-	db_printf("\tpgrp: %p gid %d jobc %d\n", tp->t_pgrp,
-	    tp->t_pgrp ? tp->t_pgrp->pg_id : 0,
-	    tp->t_pgrp ? tp->t_pgrp->pg_jobc : 0);
+	db_printf("\tpgrp: %p gid %d\n", tp->t_pgrp,
+	    tp->t_pgrp ? tp->t_pgrp->pg_id : 0);
 	db_printf("\tsession: %p", tp->t_session);
 	if (tp->t_session != NULL)
 	    db_printf(" count %u leader %p tty %p sid %d login %s",
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 9784d26a1215..005be45435d0 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -110,8 +110,11 @@ struct pgrp {
 	pid_t		pg_id;		/* (c) Process group id. */
 	int		pg_jobc;	/* (m) Job control process count. */
 	struct mtx	pg_mtx;		/* Mutex to protect members */
+	int		pg_flags;	/* (m) PGRP_ flags */
 };
 
+#define	PGRP_ORPHANED	0x00000001	/* Group is orphaned */
+
 /*
  * pargs, used to hold a copy of the command line, if it had a sane length.
  */


More information about the dev-commits-src-branches mailing list