Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 29 Jul 2024 01:49:56 GMT
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: e24a65528388 - main - thread: Remove kernel stack swapping support, part 4
Message-ID:  <202407290149.46T1nuhb091780@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by markj:

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

commit e24a65528388f4debfb12e936f314f85ba6ac407
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2024-07-29 01:40:22 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2024-07-29 01:40:22 +0000

    thread: Remove kernel stack swapping support, part 4
    
    - Remove the IS_SWAPPED thread inhibitor state.
    - Remove all uses of TD_IS_SWAPPED() in the kernel.
    - Remove the TDF_CANSWAP flag.
    - Remove the P_SWAPPINGOUT and P_SWAPPINGIN flags.
    
    Tested by:      pho
    Reviewed by:    alc, imp, kib
    Differential Revision:  https://reviews.freebsd.org/D46115
---
 sys/arm/arm/stack_machdep.c         |  2 --
 sys/arm64/arm64/stack_machdep.c     |  2 --
 sys/ddb/db_ps.c                     |  8 --------
 sys/gdb/gdb_main.c                  |  2 --
 sys/kern/kern_proc.c                | 10 +++-------
 sys/kern/kern_sig.c                 | 13 ++-----------
 sys/kern/kern_synch.c               | 25 +++++--------------------
 sys/kern/sched_4bsd.c               |  3 ---
 sys/kern/sched_ule.c                |  3 ---
 sys/kern/tty_info.c                 |  8 ++------
 sys/powerpc/powerpc/stack_machdep.c |  2 --
 sys/riscv/riscv/stack_machdep.c     |  2 --
 sys/sys/proc.h                      | 19 +++++--------------
 sys/sys/user.h                      |  2 +-
 sys/vm/vm_meter.c                   |  4 +---
 sys/vm/vm_pageout.c                 |  3 +--
 sys/x86/x86/stack_machdep.c         |  2 --
 17 files changed, 20 insertions(+), 90 deletions(-)

diff --git a/sys/arm/arm/stack_machdep.c b/sys/arm/arm/stack_machdep.c
index 3fc42d4524ae..7dcf5583d866 100644
--- a/sys/arm/arm/stack_machdep.c
+++ b/sys/arm/arm/stack_machdep.c
@@ -69,8 +69,6 @@ stack_save_td(struct stack *st, struct thread *td)
 	struct unwind_state state;
 
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
-	KASSERT(!TD_IS_SWAPPED(td),
-	    ("stack_save_td: thread %p is swapped", td));
 
 	if (TD_IS_RUNNING(td))
 		return (EOPNOTSUPP);
diff --git a/sys/arm64/arm64/stack_machdep.c b/sys/arm64/arm64/stack_machdep.c
index e5e105aeb955..fde975ffc7d2 100644
--- a/sys/arm64/arm64/stack_machdep.c
+++ b/sys/arm64/arm64/stack_machdep.c
@@ -59,8 +59,6 @@ stack_save_td(struct stack *st, struct thread *td)
 	struct unwind_state frame;
 
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
-	KASSERT(!TD_IS_SWAPPED(td),
-	    ("stack_save_td: thread %p is swapped", td));
 
 	if (TD_IS_RUNNING(td))
 		return (EOPNOTSUPP);
diff --git a/sys/ddb/db_ps.c b/sys/ddb/db_ps.c
index cc2eded87c77..9bccb46f989c 100644
--- a/sys/ddb/db_ps.c
+++ b/sys/ddb/db_ps.c
@@ -290,8 +290,6 @@ dumpthread(volatile struct proc *p, volatile struct thread *td, int all)
 				else
 					strlcat(state, "D", sizeof(state));
 			}
-			if (TD_IS_SWAPPED(td))
-				strlcat(state, "W", sizeof(state));
 			if (TD_AWAITING_INTR(td))
 				strlcat(state, "I", sizeof(state));
 			if (TD_IS_SUSPENDED(td))
@@ -393,12 +391,6 @@ DB_SHOW_COMMAND(thread, db_show_thread)
 			db_printf("SUSPENDED");
 			comma = true;
 		}
-		if (TD_IS_SWAPPED(td)) {
-			if (comma)
-				db_printf(", ");
-			db_printf("SWAPPED");
-			comma = true;
-		}
 		if (TD_ON_LOCK(td)) {
 			if (comma)
 				db_printf(", ");
diff --git a/sys/gdb/gdb_main.c b/sys/gdb/gdb_main.c
index 3e8ada42adac..8ba8b14fd4b1 100644
--- a/sys/gdb/gdb_main.c
+++ b/sys/gdb/gdb_main.c
@@ -522,8 +522,6 @@ do_qXfer_threads_read(void)
 				sbuf_cat(&ctx.qXfer.sb, "Blocked");
 			else if (TD_IS_SLEEPING(ctx.iter))
 				sbuf_cat(&ctx.qXfer.sb, "Sleeping");
-			else if (TD_IS_SWAPPED(ctx.iter))
-				sbuf_cat(&ctx.qXfer.sb, "Swapped");
 			else if (TD_AWAITING_INTR(ctx.iter))
 				sbuf_cat(&ctx.qXfer.sb, "IthreadWait");
 			else if (TD_IS_SUSPENDED(ctx.iter))
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index 52b361832218..280ad3facd3a 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -1140,10 +1140,8 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp)
 
 		kp->ki_size = vm->vm_map.size;
 		kp->ki_rssize = vmspace_resident_count(vm); /*XXX*/
-		FOREACH_THREAD_IN_PROC(p, td0) {
-			if (!TD_IS_SWAPPED(td0))
-				kp->ki_rssize += td0->td_kstack_pages;
-		}
+		FOREACH_THREAD_IN_PROC(p, td0)
+			kp->ki_rssize += td0->td_kstack_pages;
 		kp->ki_swrss = vm->vm_swrss;
 		kp->ki_tsize = vm->vm_tsize;
 		kp->ki_dsize = vm->vm_dsize;
@@ -2869,9 +2867,7 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS)
 		    sizeof(kkstp->kkst_trace), SBUF_FIXEDLEN);
 		thread_lock(td);
 		kkstp->kkst_tid = td->td_tid;
-		if (TD_IS_SWAPPED(td))
-			kkstp->kkst_state = KKST_STATE_SWAPPED;
-		else if (stack_save_td(st, td) == 0)
+		if (stack_save_td(st, td) == 0)
 			kkstp->kkst_state = KKST_STATE_STACKOK;
 		else
 			kkstp->kkst_state = KKST_STATE_RUNNING;
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 7ac9dcb8cb40..46f7b29837e4 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2791,8 +2791,7 @@ sig_suspend_threads(struct thread *td, struct proc *p)
 	FOREACH_THREAD_IN_PROC(p, td2) {
 		thread_lock(td2);
 		ast_sched_locked(td2, TDA_SUSPEND);
-		if ((TD_IS_SLEEPING(td2) || TD_IS_SWAPPED(td2)) &&
-		    (td2->td_flags & TDF_SINTR)) {
+		if (TD_IS_SLEEPING(td2) && (td2->td_flags & TDF_SINTR) != 0) {
 			if (td2->td_flags & TDF_SBDRY) {
 				/*
 				 * Once a thread is asleep with
@@ -3579,16 +3578,8 @@ proc_wkilled(struct proc *p)
 {
 
 	PROC_LOCK_ASSERT(p, MA_OWNED);
-	if ((p->p_flag & P_WKILLED) == 0) {
+	if ((p->p_flag & P_WKILLED) == 0)
 		p->p_flag |= P_WKILLED;
-		/*
-		 * Notify swapper that there is a process to swap in.
-		 * The notification is racy, at worst it would take 10
-		 * seconds for the swapper process to notice.
-		 */
-		if ((p->p_flag & (P_INMEM | P_SWAPPINGIN)) == 0)
-			wakeup(&proc0);
-	}
 }
 
 /*
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index c7258b3cffa5..25bca094b400 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -558,25 +558,23 @@ mi_switch(int flags)
 }
 
 /*
- * Change thread state to be runnable, placing it on the run queue if
- * it is in memory.  If it is swapped out, return true so our caller
- * will know to awaken the swapper.
+ * Change thread state to be runnable, placing it on the run queue.
  *
  * Requires the thread lock on entry, drops on exit.
  */
 int
 setrunnable(struct thread *td, int srqflags)
 {
-	int swapin;
-
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
 	KASSERT(td->td_proc->p_state != PRS_ZOMBIE,
 	    ("setrunnable: pid %d is a zombie", td->td_proc->p_pid));
 
-	swapin = 0;
 	switch (TD_GET_STATE(td)) {
 	case TDS_RUNNING:
 	case TDS_RUNQ:
+	case TDS_INHIBITED:
+		if ((srqflags & (SRQ_HOLD | SRQ_HOLDTD)) == 0)
+			thread_unlock(td);
 		break;
 	case TDS_CAN_RUN:
 		KASSERT((td->td_flags & TDF_INMEM) != 0,
@@ -584,25 +582,12 @@ setrunnable(struct thread *td, int srqflags)
 		    td, td->td_flags, td->td_inhibitors));
 		/* unlocks thread lock according to flags */
 		sched_wakeup(td, srqflags);
-		return (0);
-	case TDS_INHIBITED:
-		/*
-		 * If we are only inhibited because we are swapped out
-		 * arrange to swap in this process.
-		 */
-		if (td->td_inhibitors == TDI_SWAPPED &&
-		    (td->td_flags & TDF_SWAPINREQ) == 0) {
-			td->td_flags |= TDF_SWAPINREQ;
-			swapin = 1;
-		}
 		break;
 	default:
 		panic("setrunnable: state 0x%x", TD_GET_STATE(td));
 	}
-	if ((srqflags & (SRQ_HOLD | SRQ_HOLDTD)) == 0)
-		thread_unlock(td);
 
-	return (swapin);
+	return (0);
 }
 
 /*
diff --git a/sys/kern/sched_4bsd.c b/sys/kern/sched_4bsd.c
index ff1e57746404..6d94cc7f8ed1 100644
--- a/sys/kern/sched_4bsd.c
+++ b/sys/kern/sched_4bsd.c
@@ -998,8 +998,6 @@ sched_sleep(struct thread *td, int pri)
 	td_get_sched(td)->ts_slptime = 0;
 	if (pri != 0 && PRI_BASE(td->td_pri_class) == PRI_TIMESHARE)
 		sched_prio(td, pri);
-	if (TD_IS_SUSPENDED(td) || pri >= PSOCK)
-		td->td_flags |= TDF_CANSWAP;
 }
 
 void
@@ -1137,7 +1135,6 @@ sched_wakeup(struct thread *td, int srqflags)
 
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
 	ts = td_get_sched(td);
-	td->td_flags &= ~TDF_CANSWAP;
 	if (ts->ts_slptime > 1) {
 		updatepri(td);
 		resetpriority(td);
diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c
index 502802047cd4..39cb648c2216 100644
--- a/sys/kern/sched_ule.c
+++ b/sys/kern/sched_ule.c
@@ -2334,8 +2334,6 @@ sched_sleep(struct thread *td, int prio)
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
 
 	td->td_slptick = ticks;
-	if (TD_IS_SUSPENDED(td) || prio >= PSOCK)
-		td->td_flags |= TDF_CANSWAP;
 	if (PRI_BASE(td->td_pri_class) != PRI_TIMESHARE)
 		return;
 	if (static_boost == 1 && prio)
@@ -2358,7 +2356,6 @@ sched_wakeup(struct thread *td, int srqflags)
 
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
 	ts = td_get_sched(td);
-	td->td_flags &= ~TDF_CANSWAP;
 
 	/*
 	 * If we slept for more than a tick update our interactivity and
diff --git a/sys/kern/tty_info.c b/sys/kern/tty_info.c
index f54fc3a30f5e..638180292f67 100644
--- a/sys/kern/tty_info.c
+++ b/sys/kern/tty_info.c
@@ -369,12 +369,8 @@ tty_info(struct tty *tp)
 	kstacks_val = atomic_load_int(&tty_info_kstacks);
 	print_kstacks = (kstacks_val != STACK_SBUF_FMT_NONE);
 
-	if (print_kstacks) {
-		if (TD_IS_SWAPPED(td))
-			sterr = ENOENT;
-		else
-			sterr = stack_save_td(&stack, td);
-	}
+	if (print_kstacks)
+		sterr = stack_save_td(&stack, td);
 #endif
 	thread_unlock(td);
 	if (p->p_state == PRS_NEW || p->p_state == PRS_ZOMBIE)
diff --git a/sys/powerpc/powerpc/stack_machdep.c b/sys/powerpc/powerpc/stack_machdep.c
index 2d1a45c71c5a..b34d97958cd9 100644
--- a/sys/powerpc/powerpc/stack_machdep.c
+++ b/sys/powerpc/powerpc/stack_machdep.c
@@ -91,8 +91,6 @@ stack_save_td(struct stack *st, struct thread *td)
 	vm_offset_t frame;
 
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
-	KASSERT(!TD_IS_SWAPPED(td),
-	    ("stack_save_td: thread %p is swapped", td));
 
 	if (TD_IS_RUNNING(td))
 		return (EOPNOTSUPP);
diff --git a/sys/riscv/riscv/stack_machdep.c b/sys/riscv/riscv/stack_machdep.c
index 25ddf6ddfa0b..92d82220a9be 100644
--- a/sys/riscv/riscv/stack_machdep.c
+++ b/sys/riscv/riscv/stack_machdep.c
@@ -65,8 +65,6 @@ stack_save_td(struct stack *st, struct thread *td)
 	struct unwind_state frame;
 
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
-	KASSERT(!TD_IS_SWAPPED(td),
-	    ("stack_save_td: thread %p is swapped", td));
 
 	if (TD_IS_RUNNING(td))
 		return (EOPNOTSUPP);
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index c492cd10e712..167ac275c920 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -452,7 +452,7 @@ do {									\
 #define	TDF_SINTR	0x00000008 /* Sleep is interruptible. */
 #define	TDF_TIMEOUT	0x00000010 /* Timing out during sleep. */
 #define	TDF_IDLETD	0x00000020 /* This is a per-CPU idle thread. */
-#define	TDF_CANSWAP	0x00000040 /* Thread can be swapped. */
+#define	TDF_UNUSED11	0x00000040 /* Available */
 #define	TDF_SIGWAIT	0x00000080 /* Ignore ignored signals */
 #define	TDF_KTH_SUSP	0x00000100 /* kthread is suspended */
 #define	TDF_ALLPROCSUSP	0x00000200 /* suspended by SINGLE_ALLPROC */
@@ -468,7 +468,7 @@ do {									\
 #define	TDF_SERESTART	0x00080000 /* ERESTART on stop attempts. */
 #define	TDF_THRWAKEUP	0x00100000 /* Libthr thread must not suspend itself. */
 #define	TDF_SEINTR	0x00200000 /* EINTR on stop attempts. */
-#define	TDF_SWAPINREQ	0x00400000 /* Swapin request due to wakeup. */
+#define	TDF_UNUSED12	0x00400000 /* Available */
 #define	TDF_UNUSED6	0x00800000 /* Available */
 #define	TDF_SCHED0	0x01000000 /* Reserved for scheduler private use */
 #define	TDF_SCHED1	0x02000000 /* Reserved for scheduler private use */
@@ -574,14 +574,12 @@ enum {
  */
 #define	TDI_SUSPENDED	0x0001	/* On suspension queue. */
 #define	TDI_SLEEPING	0x0002	/* Actually asleep! (tricky). */
-#define	TDI_SWAPPED	0x0004	/* Stack not in mem.  Bad juju if run. */
 #define	TDI_LOCK	0x0008	/* Stopped on a lock. */
 #define	TDI_IWAIT	0x0010	/* Awaiting interrupt. */
 
 #define	TD_IS_SLEEPING(td)	((td)->td_inhibitors & TDI_SLEEPING)
 #define	TD_ON_SLEEPQ(td)	((td)->td_wchan != NULL)
 #define	TD_IS_SUSPENDED(td)	((td)->td_inhibitors & TDI_SUSPENDED)
-#define	TD_IS_SWAPPED(td)	((td)->td_inhibitors & TDI_SWAPPED)
 #define	TD_ON_LOCK(td)		((td)->td_inhibitors & TDI_LOCK)
 #define	TD_AWAITING_INTR(td)	((td)->td_inhibitors & TDI_IWAIT)
 #ifdef _KERNEL
@@ -602,7 +600,6 @@ enum {
 #define	KTDSTATE(td)							\
 	(((td)->td_inhibitors & TDI_SLEEPING) != 0 ? "sleep"  :		\
 	((td)->td_inhibitors & TDI_SUSPENDED) != 0 ? "suspended" :	\
-	((td)->td_inhibitors & TDI_SWAPPED) != 0 ? "swapped" :		\
 	((td)->td_inhibitors & TDI_LOCK) != 0 ? "blocked" :		\
 	((td)->td_inhibitors & TDI_IWAIT) != 0 ? "iwait" : "yielding")
 
@@ -618,14 +615,12 @@ enum {
 } while (0)
 
 #define	TD_SET_SLEEPING(td)	TD_SET_INHIB((td), TDI_SLEEPING)
-#define	TD_SET_SWAPPED(td)	TD_SET_INHIB((td), TDI_SWAPPED)
 #define	TD_SET_LOCK(td)		TD_SET_INHIB((td), TDI_LOCK)
 #define	TD_SET_SUSPENDED(td)	TD_SET_INHIB((td), TDI_SUSPENDED)
 #define	TD_SET_IWAIT(td)	TD_SET_INHIB((td), TDI_IWAIT)
 #define	TD_SET_EXITING(td)	TD_SET_INHIB((td), TDI_EXITING)
 
 #define	TD_CLR_SLEEPING(td)	TD_CLR_INHIB((td), TDI_SLEEPING)
-#define	TD_CLR_SWAPPED(td)	TD_CLR_INHIB((td), TDI_SWAPPED)
 #define	TD_CLR_LOCK(td)		TD_CLR_INHIB((td), TDI_LOCK)
 #define	TD_CLR_SUSPENDED(td)	TD_CLR_INHIB((td), TDI_SUSPENDED)
 #define	TD_CLR_IWAIT(td)	TD_CLR_INHIB((td), TDI_IWAIT)
@@ -816,8 +811,7 @@ struct proc {
 					   shortcuts) */
 #define	P_SUGID		0x00000100	/* Had set id privileges since last
 					   exec. */
-#define	P_SYSTEM	0x00000200	/* System proc: no sigs, stats or
-					   swapping. */
+#define	P_SYSTEM	0x00000200	/* System proc: no sigs or stats. */
 #define	P_SINGLE_EXIT	0x00000400	/* Threads suspending should exit,
 					   not wait. */
 #define	P_TRACED	0x00000800	/* Debugged process being traced. */
@@ -842,8 +836,8 @@ struct proc {
 #define	P_INEXEC	0x04000000	/* Process is in execve(). */
 #define	P_STATCHILD	0x08000000	/* Child process stopped or exited. */
 #define	P_INMEM		0x10000000	/* Loaded into memory. */
-#define	P_SWAPPINGOUT	0x20000000	/* Process is being swapped out. */
-#define	P_SWAPPINGIN	0x40000000	/* Process is being swapped in. */
+#define	P_UNUSED1	0x20000000	/* --available-- */
+#define	P_UNUSED2	0x40000000	/* --available-- */
 #define	P_PPTRACE	0x80000000	/* PT_TRACEME by vforked child. */
 
 #define	P_STOPPED	(P_STOPPED_SIG|P_STOPPED_SINGLE|P_STOPPED_TRACE)
@@ -1038,9 +1032,6 @@ extern pid_t pid_max;
 	_p->p_cowgen - _td->td_cowgen;					\
 })
 
-/* Check whether a thread is safe to be swapped out. */
-#define	thread_safetoswapout(td)	((td)->td_flags & TDF_CANSWAP)
-
 /* Control whether or not it is safe for curthread to sleep. */
 #define	THREAD_NO_SLEEPING()		do {				\
 	curthread->td_no_sleeping++;					\
diff --git a/sys/sys/user.h b/sys/sys/user.h
index e76b2a66ae94..96f17bffff8c 100644
--- a/sys/sys/user.h
+++ b/sys/sys/user.h
@@ -613,7 +613,7 @@ struct kinfo_vmobject {
 #define	KKST_MAXLEN	1024
 
 #define	KKST_STATE_STACKOK	0		/* Stack is valid. */
-#define	KKST_STATE_SWAPPED	1		/* Stack swapped out. */
+#define	KKST_STATE_SWAPPED	1		/* Stack swapped out, obsolete. */
 #define	KKST_STATE_RUNNING	2		/* Stack ephemeral. */
 
 #if defined(__amd64__) || defined(__i386__)
diff --git a/sys/vm/vm_meter.c b/sys/vm/vm_meter.c
index d255f8e8f358..7348577fc3cb 100644
--- a/sys/vm/vm_meter.c
+++ b/sys/vm/vm_meter.c
@@ -189,9 +189,7 @@ vmtotal(SYSCTL_HANDLER_ARGS)
 				thread_lock(td);
 				switch (TD_GET_STATE(td)) {
 				case TDS_INHIBITED:
-					if (TD_IS_SWAPPED(td))
-						total.t_sw++;
-					else if (TD_IS_SLEEPING(td)) {
+					if (TD_IS_SLEEPING(td)) {
 						if (td->td_priority <= PZERO)
 							total.t_dw++;
 						else
diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c
index e32d27f2300a..e848d68739ca 100644
--- a/sys/vm/vm_pageout.c
+++ b/sys/vm/vm_pageout.c
@@ -1960,8 +1960,7 @@ vm_pageout_oom(int shortage)
 			if (!TD_ON_RUNQ(td) &&
 			    !TD_IS_RUNNING(td) &&
 			    !TD_IS_SLEEPING(td) &&
-			    !TD_IS_SUSPENDED(td) &&
-			    !TD_IS_SWAPPED(td)) {
+			    !TD_IS_SUSPENDED(td)) {
 				thread_unlock(td);
 				breakout = true;
 				break;
diff --git a/sys/x86/x86/stack_machdep.c b/sys/x86/x86/stack_machdep.c
index 746c560a094b..f1084a1d3b0c 100644
--- a/sys/x86/x86/stack_machdep.c
+++ b/sys/x86/x86/stack_machdep.c
@@ -109,8 +109,6 @@ stack_save_td(struct stack *st, struct thread *td)
 	bool done;
 
 	THREAD_LOCK_ASSERT(td, MA_OWNED);
-	KASSERT(!TD_IS_SWAPPED(td),
-	    ("stack_save_td: thread %p is swapped", td));
 	if (TD_IS_RUNNING(td) && td != curthread)
 		PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
 



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