git: ecdc9cfea64c - stable/15 - pdwait(2): change handling of the exited processes

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sun, 16 Aug 2026 02:47:12 UTC
The branch stable/15 has been updated by kib:

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

commit ecdc9cfea64c987bfb1a2382b7e263016005cd54
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-07-28 00:31:51 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-08-16 02:41:35 +0000

    pdwait(2): change handling of the exited processes
    
    (cherry picked from commit 7da4edd300984abe67ff503828c1674a28e4b8b0)
---
 sys/kern/kern_exit.c    | 27 ++++++++++++++++++---------
 sys/kern/sys_procdesc.c | 29 +++++++++++++++++++++++++----
 sys/sys/procdesc.h      | 11 +++++++++--
 3 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 2e8e95e33d8c..b61fb7bcf2f1 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -1578,29 +1578,38 @@ kern_pdwait(struct thread *td, int fd, int *status,
 		    ("closed proc %p procdesc %p pd flags %#x",
 		    pd->pd_proc, pd, pd->pd_flags));
 
+		if ((pd->pd_flags & PDF_EXITED) != 0) {
+			if ((options & WEXITED) == 0) {
+				error = ESRCH;
+				goto exit_tree_locked;
+			}
+			procdesc_fill_winfo(pd, false);
+			*status = KW_EXITCODE(pd->pd_xexit, pd->pd_xsig);
+			if (wrusage != NULL) {
+				memcpy(wrusage, &pd->pd_wrusage,
+				    sizeof(*wrusage));
+			}
+			if (siginfo != NULL) {
+				memcpy(siginfo, &pd->pd_siginfo,
+				    sizeof(*siginfo));
+			}
+			goto exit_tree_locked;
+		}
 		p = pd->pd_proc;
 		if (p == NULL) {
 			error = ESRCH;
 			goto exit_tree_locked;
 		}
 		PROC_LOCK(p);
+		MPASS(p->p_state != PRS_ZOMBIE);
 
 		error = p_canwait(td, p);
 		if (error != 0)
 			break;
-		if ((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) {
-			error = ESRCH;
-			break;
-		}
 
 		wait_fill_siginfo(p, siginfo);
 		wait_fill_wrusage(p, wrusage);
 
-		if (p->p_state == PRS_ZOMBIE) {
-			proc_reap(td, p, status, options);
-			goto exit_unlocked;
-		}
-
 		if (wait6_check_alive(td, options, p, status, siginfo))
 			goto exit_unlocked;
 
diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c
index 608771b9f38c..a605633fbca5 100644
--- a/sys/kern/sys_procdesc.c
+++ b/sys/kern/sys_procdesc.c
@@ -295,7 +295,8 @@ procdesc_exit(struct proc *p)
 	KASSERT(pd->pd_fpcount > 0, ("%s: closed procdesc %p", __func__, pd));
 
 	pd->pd_flags |= PDF_EXITED;
-	pd->pd_xstat = KW_EXITCODE(p->p_xexit, p->p_xsig);
+	pd->pd_xexit = p->p_xexit;
+	pd->pd_xsig = p->p_xsig;
 
 	selwakeup(&pd->pd_selinfo);
 	KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT | NOTE_PDSIGCHLD);
@@ -337,6 +338,25 @@ procdesc_fork(struct proc *p, pid_t child_pid)
 	PROC_UNLOCK(p);
 }
 
+void
+procdesc_fill_winfo(struct procdesc *pd, bool proc_locked)
+{
+	struct proc *p;
+
+	sx_assert(&proctree_lock, SA_XLOCKED);
+
+	if ((pd->pd_flags & (PDF_EXITED | PDF_EXIT_INFO)) == PDF_EXITED) {
+		pd->pd_flags |= PDF_EXIT_INFO;
+		p = pd->pd_proc;
+		if (!proc_locked)
+			PROC_LOCK(p);
+		wait_fill_siginfo(p, &pd->pd_siginfo);
+		wait_fill_wrusage(p, &pd->pd_wrusage);
+		if (!proc_locked)
+			PROC_UNLOCK(p);
+	}
+}
+
 /*
  * When a process descriptor is reaped, perhaps as a result of close(), release
  * the process's reference on the process descriptor.
@@ -350,6 +370,7 @@ procdesc_reap(struct proc *p)
 	KASSERT(p->p_procdesc != NULL, ("procdesc_reap: p_procdesc == NULL"));
 
 	pd = p->p_procdesc;
+	procdesc_fill_winfo(pd, false);
 	pd->pd_proc = NULL;
 	p->p_procdesc = NULL;
 	procdesc_free(pd);
@@ -458,7 +479,7 @@ procdesc_poll(struct file *fp, int events, struct ucred *active_cred,
 	revents = 0;
 	pd = fp->f_data;
 	PROCDESC_LOCK(pd);
-	if (pd->pd_flags & PDF_EXITED)
+	if ((atomic_load_int(&pd->pd_flags) & PDF_EXITED) != 0)
 		revents |= POLLHUP;
 	else
 		selrecord(td, &pd->pd_selinfo);
@@ -491,7 +512,7 @@ procdesc_kqops_event(struct knote *kn, long hint)
 		 * pending.
 		 */
 		p = pd->pd_proc;
-		if ((pd->pd_flags & PDF_EXITED) != 0)
+		if ((atomic_load_int(&pd->pd_flags) & PDF_EXITED) != 0)
 			event = NOTE_EXIT | NOTE_PDSIGCHLD;
 		else if ((atomic_load_int(&p->p_flag) & (P_STOPPED_SIG |
 		    P_STOPPED_TRACE)) != 0)
@@ -509,7 +530,7 @@ procdesc_kqops_event(struct knote *kn, long hint)
 
 	/* Report exit status */
 	if ((kn->kn_fflags & NOTE_EXIT) != 0)
-		kn->kn_data = pd->pd_xstat;
+		kn->kn_data = KW_EXITCODE(pd->pd_xexit, pd->pd_xsig);
 
 	/* Process is gone, so flag the event as finished. */
 	if ((event & NOTE_REAP) != 0 ||
diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h
index bb486d9026ba..f912f8787fd9 100644
--- a/sys/sys/procdesc.h
+++ b/sys/sys/procdesc.h
@@ -71,10 +71,15 @@ struct procdesc {
 	/*
 	 * In-flight data and notification of events.
 	 */
-	int		 pd_flags;		/* (p) PD_ flags. */
-	u_short		 pd_xstat;		/* (p) Exit status. */
+	int		 pd_flags;		/* (t) PD_ flags. */
 	struct selinfo	 pd_selinfo;		/* (p) Event notification. */
 	struct mtx	 pd_lock;		/* Protect data + events. */
+
+	/* Exit status. */
+	u_int		 pd_xexit;
+	u_int		 pd_xsig;
+	struct __wrusage pd_wrusage;
+	siginfo_t	 pd_siginfo;
 };
 
 /*
@@ -89,6 +94,7 @@ struct procdesc {
 /*
  * Flags for the pd_flags field.
  */
+#define	PDF_EXIT_INFO	0x00000001	/* Exit info calculated. */
 #define	PDF_EXITED	0x00000004	/* Process exited. */
 
 /*
@@ -111,6 +117,7 @@ void	 procdesc_new(struct proc *, int);
 void	 procdesc_finit(struct procdesc *, struct file *);
 pid_t	 procdesc_pid(struct file *);
 void	 procdesc_reap(struct proc *);
+void	 procdesc_fill_winfo(struct procdesc *pd, bool proc_locked);
 
 int	 procdesc_falloc(struct thread *, struct file **, int *, int,
 	    struct filecaps *);