NetBSD Problem Report #60030

From www@netbsd.org  Mon Feb 23 02:43:56 2026
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
	 client-signature RSA-PSS (2048 bits) client-digest SHA256)
	(Client CN "mail.netbsd.org", Issuer "R13" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id CFE661A9239
	for <gnats-bugs@gnats.NetBSD.org>; Mon, 23 Feb 2026 02:43:56 +0000 (UTC)
Message-Id: <20260223024354.3CC571A923C@mollari.NetBSD.org>
Date: Mon, 23 Feb 2026 02:43:54 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: ddb/crash: show all locks without LOCKDEBUG
X-Send-Pr-Version: www-1.0
X-From4GNATS: "campbell+netbsd@mumble.net via gnats" <gnats-admin@NetBSD.org>

>Number:         60030
>Category:       kern
>Synopsis:       ddb/crash: show all locks without LOCKDEBUG
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          needs-pullups
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Feb 23 02:45:00 +0000 2026
>Closed-Date:    
>Last-Modified:  Mon Aug 17 19:35:00 +0000 2026
>Originator:     Taylor R Campbell
>Release:        current, 11, 10, 9, ...
>Organization:
The LockBSD Foundebuggation, Inc.
>Environment:
>Description:

	We have had a number of deadlocks involving softints leading to
	heartbeat panics recently.  The heartbeat mechanism itself is
	not the problem -- it's just exposing other problems that might
	otherwise manifest as one CPU quietly getting stuck while
	dozens of others continue normally.

	Examples:

	PR kern/59963: Lock Order Reversal issue with PPP
	PR kern/60029: panic: cpu0: softints stuck for 16 seconds

	The obvious symptom is often the same -- panic: softints stuck
	for N seconds.  But the underlying problem is likely entirely
	unrelated.  We can track it down by following ps and ps/w and
	show all tstiles output to find which softint threads are
	waiting for which locks, and x/Lx on carefully chosen bits of
	what the wchan points to -- when it is applicable -- to find
	who holds the lock.  But sometimes it is hard to find, e.g. if
	the waiter is actually spinning because the lock holder is
	running on another CPU.

	In a kernel with LOCKDEBUG, detailed information about who is
	holding or waiting for which locks is recorded, but it's very
	expensive and never deployed in production (and for some
	workloads it just doesn't work, leading to kernel lock spinouts
	or similar, because its implications aren't exercised well
	enough).

	But we could easily add a bit of overhead just to the
	_contended_ paths of lock acquisition to record enough
	information for ddb or crash to tell us everything we need to
	diagnose these: just store a pointer to the lock in the
	existing struct lwp::l_ld_wanted member (currently used only
	under LOCKDEBUG) while acquiring a lock, saving whatever was
	there before on the stack.

	We can even just put this into the existing LOCKDEBUG_WANTLOCK
	and LOCKDEBUG_LOCKED macros, with a small tweak.

>How-To-Repeat:

	In ddb or crash:

	show all locks

>Fix:

	Two parts:

	1. Teach all the types of locks to record what they're waiting
	   for -- while _either_ spinning or sleeping -- by tweaking
	   the LOCKDEBUG_WANTLOCK and LOCKDEBUG_LOCKED macros (plus
	   tidying up a few cases where they weren't matched).

	2. Teach ddb/crash to show for each lwp waiting for a lock
	   which lock it's waiting for.

	XXX Not really fit for commit yet -- too much duplication to
	sync in the ddb and crash paths, need to disentangle the parts.
	Tricky because for the _kernel_ ddb build, it's
	compile-time-conditional on LOCKDEBUG, while for the _userland_
	crash(8) build, it's run-time-conditional on whether the kernel
	it's examining was built with LOCKDEBUG.

# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1771802723 0
#      Sun Feb 22 23:25:23 2026 +0000
# Branch trunk
# Node ID 6012a26811baaef76ae6093b3032098c2d60caca
# Parent  8103eaab019500f5780e323f8ad0056f7f973eef
# EXP-Topic riastradh-prNNNNN-showalllocks
WIP: Track what lock each lwp is waiting for without LOCKDEBUG.

This is reasonably cheap -- one extra pointer on the stack, couple
extra stores when _waiting_ (spinning or sleeping) for a _contended_
lock -- and doesn't change the kernel ABI.  Will enable us to get
diagnostics from crash dumps when, e.g., there's a softint deadlock
tripping a heartbeat panic.

diff -r 8103eaab0195 -r 6012a26811ba sys/external/bsd/drm2/linux/linux_rwsem.c
--- a/sys/external/bsd/drm2/linux/linux_rwsem.c	Sun Feb 22 11:40:19 2026 +0000
+++ b/sys/external/bsd/drm2/linux/linux_rwsem.c	Sun Feb 22 23:25:23 2026 +0000
@@ -42,15 +42,15 @@ __KERNEL_RCSID(0, "$NetBSD: linux_rwsem.

 #include <linux/rwsem.h>

-#define	RWSEM_WANTLOCK(RWSEM)						      \
+#define	RWSEM_WANTLOCK(RWSEM, OWANTEDP)					      \
 	LOCKDEBUG_WANTLOCK((RWSEM)->rws_debug, (RWSEM),			      \
-	    (uintptr_t)__builtin_return_address(0), 0)
-#define	RWSEM_LOCKED_EX(RWSEM)						      \
+	    (uintptr_t)__builtin_return_address(0), 0, OWANTEDP)
+#define	RWSEM_LOCKED_EX(RWSEM, OWANTEDP)				      \
 	LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL,		      \
-	    (uintptr_t)__builtin_return_address(0), 0)
-#define	RWSEM_LOCKED_SH(RWSEM)						      \
+	    (uintptr_t)__builtin_return_address(0), 0, OWANTEDP)
+#define	RWSEM_LOCKED_SH(RWSEM, OWANTEDP)				      \
 	LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL,		      \
-	    (uintptr_t)__builtin_return_address(0), 1)
+	    (uintptr_t)__builtin_return_address(0), 1, OWANTEDP)
 #define	RWSEM_UNLOCKED_EX(RWSEM)					      \
 	LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM),			      \
 	    (uintptr_t)__builtin_return_address(0), 0)
@@ -109,8 +109,9 @@ destroy_rwsem(struct rw_semaphore *rwsem
 void
 down_read(struct rw_semaphore *rwsem)
 {
+	volatile void *owanted;

-	RWSEM_WANTLOCK(rwsem);
+	RWSEM_WANTLOCK(rwsem, &owanted);

 	mutex_enter(&rwsem->rws_lock);
 	while (rwsem->rws_writer || rwsem->rws_writewanted)
@@ -119,7 +120,7 @@ down_read(struct rw_semaphore *rwsem)
 	rwsem->rws_readers++;
 	mutex_exit(&rwsem->rws_lock);

-	RWSEM_LOCKED_SH(rwsem);
+	RWSEM_LOCKED_SH(rwsem, &owanted);
 }

 bool
@@ -144,7 +145,8 @@ down_read_trylock(struct rw_semaphore *r
 	mutex_exit(&rwsem->rws_lock);

 	if (ret) {
-		RWSEM_LOCKED_SH(rwsem);
+		RWSEM_WANTLOCK(rwsem, NULL);
+		RWSEM_LOCKED_SH(rwsem, NULL);
 	}

 	return ret;
@@ -167,8 +169,9 @@ up_read(struct rw_semaphore *rwsem)
 void
 down_write(struct rw_semaphore *rwsem)
 {
+	volatile void *owanted;

-	RWSEM_WANTLOCK(rwsem);
+	RWSEM_WANTLOCK(rwsem, &owanted);

 	mutex_enter(&rwsem->rws_lock);

@@ -196,7 +199,7 @@ down_write(struct rw_semaphore *rwsem)

 	mutex_exit(&rwsem->rws_lock);

-	RWSEM_LOCKED_EX(rwsem);
+	RWSEM_LOCKED_EX(rwsem, &owanted);
 }

 void
@@ -227,5 +230,6 @@ downgrade_write(struct rw_semaphore *rws
 	cv_broadcast(&rwsem->rws_cv);
 	mutex_exit(&rwsem->rws_lock);

-	RWSEM_LOCKED_SH(rwsem);
+	RWSEM_WANTLOCK(rwsem, NULL);
+	RWSEM_LOCKED_SH(rwsem, NULL);
 }
diff -r 8103eaab0195 -r 6012a26811ba sys/external/bsd/drm2/linux/linux_ww_mutex.c
--- a/sys/external/bsd/drm2/linux/linux_ww_mutex.c	Sun Feb 22 11:40:19 2026 +0000
+++ b/sys/external/bsd/drm2/linux/linux_ww_mutex.c	Sun Feb 22 23:25:23 2026 +0000
@@ -43,12 +43,12 @@ __KERNEL_RCSID(0, "$NetBSD: linux_ww_mut
 #include <linux/ww_mutex.h>
 #include <linux/errno.h>

-#define	WW_WANTLOCK(WW)							      \
+#define	WW_WANTLOCK(WW, OWANTEDP)					      \
 	LOCKDEBUG_WANTLOCK((WW)->wwm_debug, (WW),			      \
-	    (uintptr_t)__builtin_return_address(0), 0)
-#define	WW_LOCKED(WW)							      \
+	    (uintptr_t)__builtin_return_address(0), 0, OWANTEDP)
+#define	WW_LOCKED(WW, OWANTEDP)						      \
 	LOCKDEBUG_LOCKED((WW)->wwm_debug, (WW), NULL,			      \
-	    (uintptr_t)__builtin_return_address(0), 0)
+	    (uintptr_t)__builtin_return_address(0), 0, OWANTEDP)
 #define	WW_UNLOCKED(WW)							      \
 	LOCKDEBUG_UNLOCKED((WW)->wwm_debug, (WW),			      \
 	    (uintptr_t)__builtin_return_address(0), 0)
@@ -458,7 +458,7 @@ ww_mutex_lock_wait_sig(struct ww_mutex *
 }

 /*
- * ww_mutex_lock_noctx(mutex)
+ * ww_mutex_lock_noctx(mutex, &owanted)
  *
  *	Acquire mutex without an acquire context.  Caller must not
  *	already hold the mutex.  Uninterruptible; never fails.
@@ -468,7 +468,7 @@ ww_mutex_lock_wait_sig(struct ww_mutex *
  *	Internal subroutine, implementing ww_mutex_lock(..., NULL).
  */
 static void
-ww_mutex_lock_noctx(struct ww_mutex *mutex)
+ww_mutex_lock_noctx(struct ww_mutex *mutex, volatile void **owantedp)
 {

 	mutex_enter(&mutex->wwm_lock);
@@ -497,12 +497,12 @@ retry:	switch (mutex->wwm_state) {
 	}
 	KASSERT(mutex->wwm_state == WW_OWNED);
 	KASSERT(mutex->wwm_u.owner == curlwp);
-	WW_LOCKED(mutex);
+	WW_LOCKED(mutex, owantedp);
 	mutex_exit(&mutex->wwm_lock);
 }

 /*
- * ww_mutex_lock_noctx_sig(mutex)
+ * ww_mutex_lock_noctx_sig(mutex, &owanted)
  *
  *	Acquire mutex without an acquire context and return 0, or fail
  *	and return -EINTR if interrupted by a signal.  Caller must not
@@ -514,7 +514,7 @@ retry:	switch (mutex->wwm_state) {
  *	ww_mutex_lock_interruptible(..., NULL).
  */
 static int
-ww_mutex_lock_noctx_sig(struct ww_mutex *mutex)
+ww_mutex_lock_noctx_sig(struct ww_mutex *mutex, volatile void **owantedp)
 {
 	int ret;

@@ -552,7 +552,7 @@ retry:	switch (mutex->wwm_state) {
 	}
 	KASSERT(mutex->wwm_state == WW_OWNED);
 	KASSERT(mutex->wwm_u.owner == curlwp);
-	WW_LOCKED(mutex);
+	WW_LOCKED(mutex, owantedp);
 	ret = 0;
 out:	mutex_exit(&mutex->wwm_lock);
 	KASSERTMSG((ret == 0 || ret == -EINTR), "ret=%d", ret);
@@ -577,6 +577,7 @@ out:	mutex_exit(&mutex->wwm_lock);
 int
 ww_mutex_lock(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
 {
+	volatile void *owanted;
 	int ret;

 	/*
@@ -587,8 +588,8 @@ ww_mutex_lock(struct ww_mutex *mutex, st
 	ASSERT_SLEEPABLE();

 	if (ctx == NULL) {
-		WW_WANTLOCK(mutex);
-		ww_mutex_lock_noctx(mutex);
+		WW_WANTLOCK(mutex, &owanted);
+		ww_mutex_lock_noctx(mutex, &owanted);
 		ret = 0;
 		goto out;
 	}
@@ -605,12 +606,12 @@ ww_mutex_lock(struct ww_mutex *mutex, st
 	ww_acquire_done_check(mutex, ctx);
 retry:	switch (mutex->wwm_state) {
 	case WW_UNLOCKED:
-		WW_WANTLOCK(mutex);
+		WW_WANTLOCK(mutex, &owanted);
 		mutex->wwm_state = WW_CTX;
 		mutex->wwm_u.ctx = ctx;
 		goto locked;
 	case WW_OWNED:
-		WW_WANTLOCK(mutex);
+		WW_WANTLOCK(mutex, &owanted);
 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
 		    "locking %p against myself: %p", mutex, curlwp);
 		ww_mutex_state_wait(mutex, WW_OWNED);
@@ -644,7 +645,7 @@ retry:	switch (mutex->wwm_state) {
 	 * We do not own it.  We can safely assert to LOCKDEBUG that we
 	 * want it.
 	 */
-	WW_WANTLOCK(mutex);
+	WW_WANTLOCK(mutex, &owanted);

 	if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
 		/*
@@ -667,7 +668,7 @@ retry:	switch (mutex->wwm_state) {
 locked:	KASSERT((mutex->wwm_state == WW_CTX) ||
 	    (mutex->wwm_state == WW_WANTOWN));
 	KASSERT(mutex->wwm_u.ctx == ctx);
-	WW_LOCKED(mutex);
+	WW_LOCKED(mutex, &owanted);
 	ctx->wwx_acquired++;
 	ret = 0;
 out_unlock:
@@ -697,6 +698,7 @@ out:	KASSERTMSG((ret == 0 || ret == -EAL
 int
 ww_mutex_lock_interruptible(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
 {
+	volatile void *owanted;
 	int ret;

 	/*
@@ -707,8 +709,8 @@ ww_mutex_lock_interruptible(struct ww_mu
 	ASSERT_SLEEPABLE();

 	if (ctx == NULL) {
-		WW_WANTLOCK(mutex);
-		ret = ww_mutex_lock_noctx_sig(mutex);
+		WW_WANTLOCK(mutex, &owanted);
+		ret = ww_mutex_lock_noctx_sig(mutex, &owanted);
 		KASSERTMSG((ret == 0 || ret == -EINTR), "ret=%d", ret);
 		goto out;
 	}
@@ -725,12 +727,12 @@ ww_mutex_lock_interruptible(struct ww_mu
 	ww_acquire_done_check(mutex, ctx);
 retry:	switch (mutex->wwm_state) {
 	case WW_UNLOCKED:
-		WW_WANTLOCK(mutex);
+		WW_WANTLOCK(mutex, &owanted);
 		mutex->wwm_state = WW_CTX;
 		mutex->wwm_u.ctx = ctx;
 		goto locked;
 	case WW_OWNED:
-		WW_WANTLOCK(mutex);
+		WW_WANTLOCK(mutex, &owanted);
 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
 		    "locking %p against myself: %p", mutex, curlwp);
 		ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
@@ -772,7 +774,7 @@ retry:	switch (mutex->wwm_state) {
 	 * We do not own it.  We can safely assert to LOCKDEBUG that we
 	 * want it.
 	 */
-	WW_WANTLOCK(mutex);
+	WW_WANTLOCK(mutex, &owanted);

 	if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
 		/*
@@ -799,7 +801,7 @@ retry:	switch (mutex->wwm_state) {
 locked:	KASSERT((mutex->wwm_state == WW_CTX) ||
 	    (mutex->wwm_state == WW_WANTOWN));
 	KASSERT(mutex->wwm_u.ctx == ctx);
-	WW_LOCKED(mutex);
+	WW_LOCKED(mutex, &owanted);
 	ctx->wwx_acquired++;
 	ret = 0;
 out_unlock:
@@ -824,13 +826,14 @@ out:	KASSERTMSG((ret == 0 || ret == -EAL
 void
 ww_mutex_lock_slow(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
 {
+	volatile void *owanted;

 	/* Caller must not try to lock against self here.  */
-	WW_WANTLOCK(mutex);
+	WW_WANTLOCK(mutex, &owanted);
 	ASSERT_SLEEPABLE();

 	if (ctx == NULL) {
-		ww_mutex_lock_noctx(mutex);
+		ww_mutex_lock_noctx(mutex, &owanted);
 		return;
 	}

@@ -881,7 +884,7 @@ retry:	switch (mutex->wwm_state) {
 locked:	KASSERT((mutex->wwm_state == WW_CTX) ||
 	    (mutex->wwm_state == WW_WANTOWN));
 	KASSERT(mutex->wwm_u.ctx == ctx);
-	WW_LOCKED(mutex);
+	WW_LOCKED(mutex, &owanted);
 	ctx->wwx_acquired++;
 	mutex_exit(&mutex->wwm_lock);
 }
@@ -901,13 +904,14 @@ int
 ww_mutex_lock_slow_interruptible(struct ww_mutex *mutex,
     struct ww_acquire_ctx *ctx)
 {
+	volatile void *owanted;
 	int ret;

-	WW_WANTLOCK(mutex);
+	WW_WANTLOCK(mutex, &owanted);
 	ASSERT_SLEEPABLE();

 	if (ctx == NULL) {
-		ret = ww_mutex_lock_noctx_sig(mutex);
+		ret = ww_mutex_lock_noctx_sig(mutex, &owanted);
 		KASSERTMSG((ret == 0 || ret == -EINTR), "ret=%d", ret);
 		goto out;
 	}
@@ -971,7 +975,7 @@ retry:	switch (mutex->wwm_state) {
 locked:	KASSERT((mutex->wwm_state == WW_CTX) ||
 	    (mutex->wwm_state == WW_WANTOWN));
 	KASSERT(mutex->wwm_u.ctx == ctx);
-	WW_LOCKED(mutex);
+	WW_LOCKED(mutex, &owanted);
 	ctx->wwx_acquired++;
 	ret = 0;
 out_unlock:
@@ -995,8 +999,8 @@ ww_mutex_trylock(struct ww_mutex *mutex)
 	if (mutex->wwm_state == WW_UNLOCKED) {
 		mutex->wwm_state = WW_OWNED;
 		mutex->wwm_u.owner = curlwp;
-		WW_WANTLOCK(mutex);
-		WW_LOCKED(mutex);
+		WW_WANTLOCK(mutex, NULL);
+		WW_LOCKED(mutex, NULL);
 		ret = 1;
 	} else {
 		/*
diff -r 8103eaab0195 -r 6012a26811ba sys/kern/kern_lock.c
--- a/sys/kern/kern_lock.c	Sun Feb 22 11:40:19 2026 +0000
+++ b/sys/kern/kern_lock.c	Sun Feb 22 23:25:23 2026 +0000
@@ -271,6 +271,7 @@ _kernel_lock(int nlocks)
 	u_int starttime;
 	int s;
 	struct lwp *l = curlwp;
+	volatile void *owanted;

 	_KERNEL_LOCK_ASSERT(nlocks > 0);

@@ -287,7 +288,7 @@ _kernel_lock(int nlocks)

 	_KERNEL_LOCK_ASSERT(l->l_blcnt == 0);
 	LOCKDEBUG_WANTLOCK(kernel_lock_dodebug, kernel_lock, RETURN_ADDRESS,
-	    0);
+	    0, &owanted);

 	if (__predict_true(__cpu_simple_lock_try(kernel_lock))) {
 		atomic_store_relaxed(&kernel_lock_holder, curcpu());
@@ -295,7 +296,7 @@ _kernel_lock(int nlocks)
 		ci->ci_biglock_count = nlocks;
 		l->l_blcnt = nlocks;
 		LOCKDEBUG_LOCKED(kernel_lock_dodebug, kernel_lock, NULL,
-		    RETURN_ADDRESS, 0);
+		    RETURN_ADDRESS, 0, &owanted);
 		splx(s);
 		return;
 	}
@@ -319,9 +320,6 @@ _kernel_lock(int nlocks)
 	membar_producer();
 	owant = ci->ci_biglock_wanted;
 	atomic_store_relaxed(&ci->ci_biglock_wanted, l);
-#if defined(DIAGNOSTIC) && !defined(LOCKDEBUG)
-	l->l_ld_wanted = __builtin_return_address(0);
-#endif

 	/*
 	 * Spin until we acquire the lock.  Once we have it, record the
@@ -351,7 +349,7 @@ _kernel_lock(int nlocks)
 	l->l_blcnt = nlocks;
 	LOCKSTAT_STOP_TIMER(lsflag, spintime);
 	LOCKDEBUG_LOCKED(kernel_lock_dodebug, kernel_lock, NULL,
-	    RETURN_ADDRESS, 0);
+	    RETURN_ADDRESS, 0, &owanted);
 	if (owant == NULL) {
 		LOCKSTAT_EVENT_RA(lsflag, kernel_lock,
 		    LB_KERNEL_LOCK | LB_SPIN, 1, spintime, RETURN_ADDRESS);
diff -r 8103eaab0195 -r 6012a26811ba sys/kern/kern_mutex.c
--- a/sys/kern/kern_mutex.c	Sun Feb 22 11:40:19 2026 +0000
+++ b/sys/kern/kern_mutex.c	Sun Feb 22 23:25:23 2026 +0000
@@ -77,15 +77,15 @@ __KERNEL_RCSID(0, "$NetBSD: kern_mutex.c
  * Debugging support.
  */

-#define	MUTEX_WANTLOCK(mtx)					\
+#define	MUTEX_WANTLOCK(mtx, owantedp)				\
     LOCKDEBUG_WANTLOCK(MUTEX_DEBUG_P(mtx), (mtx),		\
-        (uintptr_t)__builtin_return_address(0), 0)
+        (uintptr_t)__builtin_return_address(0), 0, owantedp)
 #define	MUTEX_TESTLOCK(mtx)					\
     LOCKDEBUG_WANTLOCK(MUTEX_DEBUG_P(mtx), (mtx),		\
-        (uintptr_t)__builtin_return_address(0), -1)
-#define	MUTEX_LOCKED(mtx)					\
+        (uintptr_t)__builtin_return_address(0), -1, NULL)
+#define	MUTEX_LOCKED(mtx, owantedp)				\
     LOCKDEBUG_LOCKED(MUTEX_DEBUG_P(mtx), (mtx), NULL,		\
-        (uintptr_t)__builtin_return_address(0), 0)
+        (uintptr_t)__builtin_return_address(0), 0, owantedp)
 #define	MUTEX_UNLOCKED(mtx)					\
     LOCKDEBUG_UNLOCKED(MUTEX_DEBUG_P(mtx), (mtx),		\
         (uintptr_t)__builtin_return_address(0), 0)
@@ -454,6 +454,7 @@ mutex_vector_enter(kmutex_t *mtx)
 #ifdef MULTIPROCESSOR
 	u_int count;
 #endif
+	volatile void *owanted;
 	LOCKSTAT_COUNTER(spincnt);
 	LOCKSTAT_COUNTER(slpcnt);
 	LOCKSTAT_TIMER(spintime);
@@ -471,10 +472,10 @@ mutex_vector_enter(kmutex_t *mtx)
 #endif
 		KPREEMPT_ENABLE(curlwp);
 		MUTEX_SPIN_SPLRAISE(mtx);
-		MUTEX_WANTLOCK(mtx);
+		MUTEX_WANTLOCK(mtx, &owanted);
 #ifdef FULL
 		if (MUTEX_SPINBIT_LOCK_TRY(mtx)) {
-			MUTEX_LOCKED(mtx);
+			MUTEX_LOCKED(mtx, &owanted);
 			return;
 		}
 #if !defined(MULTIPROCESSOR)
@@ -508,7 +509,7 @@ mutex_vector_enter(kmutex_t *mtx)
 		LOCKSTAT_EXIT(lsflag);
 #endif	/* !MULTIPROCESSOR */
 #endif	/* FULL */
-		MUTEX_LOCKED(mtx);
+		MUTEX_LOCKED(mtx, &owanted);
 		return;
 	}

@@ -517,7 +518,7 @@ mutex_vector_enter(kmutex_t *mtx)
 	MUTEX_DASSERT(mtx, MUTEX_ADAPTIVE_P(owner));
 	MUTEX_ASSERT(mtx, curthread != 0);
 	MUTEX_ASSERT(mtx, !cpu_intr_p());
-	MUTEX_WANTLOCK(mtx);
+	MUTEX_WANTLOCK(mtx, &owanted);

 	if (__predict_true(panicstr == NULL)) {
 		KDASSERT(pserialize_not_in_read_section());
@@ -704,7 +705,7 @@ mutex_vector_enter(kmutex_t *mtx)
 	LOCKSTAT_EXIT(lsflag);

 	MUTEX_DASSERT(mtx, MUTEX_OWNER(mtx->mtx_owner) == curthread);
-	MUTEX_LOCKED(mtx);
+	MUTEX_LOCKED(mtx, &owanted);
 }

 /*
@@ -881,22 +882,22 @@ mutex_tryenter(kmutex_t *mtx)
 		MUTEX_SPIN_SPLRAISE(mtx);
 #ifdef FULL
 		if (MUTEX_SPINBIT_LOCK_TRY(mtx)) {
-			MUTEX_WANTLOCK(mtx);
-			MUTEX_LOCKED(mtx);
+			MUTEX_WANTLOCK(mtx, NULL);
+			MUTEX_LOCKED(mtx, NULL);
 			return 1;
 		}
 		MUTEX_SPIN_SPLRESTORE(mtx);
 #else
-		MUTEX_WANTLOCK(mtx);
-		MUTEX_LOCKED(mtx);
+		MUTEX_WANTLOCK(mtx, NULL);
+		MUTEX_LOCKED(mtx, NULL);
 		return 1;
 #endif
 	} else {
 		curthread = (uintptr_t)curlwp;
 		MUTEX_ASSERT(mtx, curthread != 0);
 		if (MUTEX_ACQUIRE(mtx, curthread)) {
-			MUTEX_WANTLOCK(mtx);
-			MUTEX_LOCKED(mtx);
+			MUTEX_WANTLOCK(mtx, NULL);
+			MUTEX_LOCKED(mtx, NULL);
 			MUTEX_DASSERT(mtx,
 			    MUTEX_OWNER(mtx->mtx_owner) == curthread);
 			return 1;
@@ -923,8 +924,9 @@ mutex_spin_retry(kmutex_t *mtx)
 #ifdef LOCKDEBUG
 	u_int spins = 0;
 #endif	/* LOCKDEBUG */
+	volatile void *owanted;

-	MUTEX_WANTLOCK(mtx);
+	MUTEX_WANTLOCK(mtx, &owanted);

 	LOCKSTAT_ENTER(lsflag);
 	LOCKSTAT_START_TIMER(lsflag, spintime);
@@ -948,7 +950,7 @@ mutex_spin_retry(kmutex_t *mtx)
 	LOCKSTAT_EVENT(lsflag, mtx, LB_SPIN_MUTEX | LB_SPIN, 1, spintime);
 	LOCKSTAT_EXIT(lsflag);

-	MUTEX_LOCKED(mtx);
+	MUTEX_LOCKED(mtx, &owanted);
 #else	/* MULTIPROCESSOR */
 	MUTEX_ABORT(mtx, "locking against myself");
 #endif	/* MULTIPROCESSOR */
diff -r 8103eaab0195 -r 6012a26811ba sys/kern/kern_rwlock.c
--- a/sys/kern/kern_rwlock.c	Sun Feb 22 11:40:19 2026 +0000
+++ b/sys/kern/kern_rwlock.c	Sun Feb 22 23:25:23 2026 +0000
@@ -75,12 +75,12 @@ __KERNEL_RCSID(0, "$NetBSD: kern_rwlock.

 #define	RW_DEBUG_P(rw)		(((rw)->rw_owner & RW_NODEBUG) == 0)

-#define	RW_WANTLOCK(rw, op) \
+#define	RW_WANTLOCK(rw, op, owantedp) \
     LOCKDEBUG_WANTLOCK(RW_DEBUG_P(rw), (rw), \
-        (uintptr_t)__builtin_return_address(0), op == RW_READER);
-#define	RW_LOCKED(rw, op) \
+        (uintptr_t)__builtin_return_address(0), op == RW_READER, owantedp);
+#define	RW_LOCKED(rw, op, owantedp) \
     LOCKDEBUG_LOCKED(RW_DEBUG_P(rw), (rw), NULL, \
-        (uintptr_t)__builtin_return_address(0), op == RW_READER);
+        (uintptr_t)__builtin_return_address(0), op == RW_READER, owantedp);
 #define	RW_UNLOCKED(rw, op) \
     LOCKDEBUG_UNLOCKED(RW_DEBUG_P(rw), (rw), \
         (uintptr_t)__builtin_return_address(0), op == RW_READER);
@@ -286,6 +286,7 @@ rw_vector_enter(krwlock_t *rw, const krw
 	turnstile_t *ts;
 	int queue;
 	lwp_t *l;
+	volatile void *owanted;
 	LOCKSTAT_TIMER(slptime);
 	LOCKSTAT_TIMER(slpcnt);
 	LOCKSTAT_TIMER(spintime);
@@ -297,7 +298,7 @@ rw_vector_enter(krwlock_t *rw, const krw

 	RW_ASSERT(rw, !cpu_intr_p());
 	RW_ASSERT(rw, curthread != 0);
-	RW_WANTLOCK(rw, op);
+	RW_WANTLOCK(rw, op, &owanted);

 	if (__predict_true(panicstr == NULL)) {
 		KDASSERT(pserialize_not_in_read_section());
@@ -425,7 +426,7 @@ rw_vector_enter(krwlock_t *rw, const krw

 	RW_ASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) ||
 	    (op == RW_READER && RW_COUNT(rw) != 0));
-	RW_LOCKED(rw, op);
+	RW_LOCKED(rw, op, &owanted);
 }

 /*
@@ -576,8 +577,8 @@ rw_vector_tryenter(krwlock_t *rw, const 
 		}
 	}

-	RW_WANTLOCK(rw, op);
-	RW_LOCKED(rw, op);
+	RW_WANTLOCK(rw, op, NULL);
+	RW_LOCKED(rw, op, NULL);
 	RW_ASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) ||
 	    (op == RW_READER && RW_COUNT(rw) != 0));

@@ -617,7 +618,8 @@ rw_downgrade(krwlock_t *rw)
 			newown = (owner & RW_NODEBUG);
 			next = rw_cas(rw, owner, newown + RW_READ_INCR);
 			if (__predict_true(next == owner)) {
-				RW_LOCKED(rw, RW_READER);
+				RW_WANTLOCK(rw, RW_READER, NULL);
+				RW_LOCKED(rw, RW_READER, NULL);
 				RW_ASSERT(rw,
 				    (rw->rw_owner & RW_WRITE_LOCKED) == 0);
 				RW_ASSERT(rw, RW_COUNT(rw) != 0);
@@ -676,8 +678,8 @@ rw_downgrade(krwlock_t *rw)
 		}
 	}

-	RW_WANTLOCK(rw, RW_READER);
-	RW_LOCKED(rw, RW_READER);
+	RW_WANTLOCK(rw, RW_READER, NULL);
+	RW_LOCKED(rw, RW_READER, NULL);
 	RW_ASSERT(rw, (rw->rw_owner & RW_WRITE_LOCKED) == 0);
 	RW_ASSERT(rw, RW_COUNT(rw) != 0);
 }
@@ -714,8 +716,8 @@ rw_tryupgrade(krwlock_t *rw)
 	}

 	RW_UNLOCKED(rw, RW_READER);
-	RW_WANTLOCK(rw, RW_WRITER);
-	RW_LOCKED(rw, RW_WRITER);
+	RW_WANTLOCK(rw, RW_WRITER, NULL);
+	RW_LOCKED(rw, RW_WRITER, NULL);
 	RW_ASSERT(rw, rw->rw_owner & RW_WRITE_LOCKED);
 	RW_ASSERT(rw, RW_OWNER(rw) == curthread);

diff -r 8103eaab0195 -r 6012a26811ba sys/sys/lockdebug.h
--- a/sys/sys/lockdebug.h	Sun Feb 22 11:40:19 2026 +0000
+++ b/sys/sys/lockdebug.h	Sun Feb 22 23:25:23 2026 +0000
@@ -82,10 +82,10 @@ void	lockdebug_mem_check(const char *, s
     lockdebug_alloc(__func__, __LINE__, lock, ops, addr)
 #define	LOCKDEBUG_FREE(dodebug, lock) \
     if (dodebug) lockdebug_free(__func__, __LINE__, lock)
-#define	LOCKDEBUG_WANTLOCK(dodebug, lock, where, s) \
-    if (dodebug) lockdebug_wantlock(__func__, __LINE__, lock, where, s)
-#define	LOCKDEBUG_LOCKED(dodebug, lock, al, where, s) \
-    if (dodebug) lockdebug_locked(__func__, __LINE__, lock, al, where, s)
+#define	LOCKDEBUG_WANTLOCK(dodebug, lock, where, s, owantedp) \
+    if (dodebug) { lockdebug_wantlock(__func__, __LINE__, lock, where, s); __USE(owantedp); }
+#define	LOCKDEBUG_LOCKED(dodebug, lock, al, where, s, owantedp) \
+    if (dodebug) { lockdebug_locked(__func__, __LINE__, lock, al, where, s); __USE(owantedp); }
 #define	LOCKDEBUG_UNLOCKED(dodebug, lock, where, s) \
     if (dodebug) lockdebug_unlocked(__func__, __LINE__, lock, where, s)
 #define	LOCKDEBUG_BARRIER(lock, slp) \
@@ -96,12 +96,31 @@ void	lockdebug_mem_check(const char *, s
 #else	/* LOCKDEBUG */

 #define	LOCKDEBUG_ALLOC(lock, ops, addr)		false
-#define	LOCKDEBUG_FREE(dodebug, lock)			/* nothing */
-#define	LOCKDEBUG_WANTLOCK(dodebug, lock, where, s)	/* nothing */
-#define	LOCKDEBUG_LOCKED(dodebug, lock, al, where, s)	/* nothing */
-#define	LOCKDEBUG_UNLOCKED(dodebug, lock, where, s)	/* nothing */
-#define	LOCKDEBUG_BARRIER(lock, slp)			/* nothing */
-#define	LOCKDEBUG_MEM_CHECK(base, sz)			/* nothing */
+#define	LOCKDEBUG_FREE(dodebug, lock)			__nothing
+#if 0
+#define	LOCKDEBUG_WANTLOCK(dodebug, lock, where, s, owantedp)	__nothing
+#define	LOCKDEBUG_LOCKED(dodebug, lock, al, where, s, owantedp)	__nothing
+#else
+#define	LOCKDEBUG_WANTLOCK(dodebug, lock, where, s, owantedp) do	      \
+{									      \
+	if ((owantedp) == NULL)						      \
+		break;							      \
+	*((owantedp) == NULL ? (volatile void **)NULL : (owantedp)) =	      \
+	    curlwp->l_ld_wanted;					      \
+	curlwp->l_ld_wanted = (lock);					      \
+} while (0)
+#define	LOCKDEBUG_LOCKED(dodebug, lock, al, where, s, owantedp) do	      \
+{									      \
+	if ((owantedp) == NULL)						      \
+		break;							      \
+	curlwp->l_ld_wanted =						      \
+	    *((owantedp) == NULL ? (volatile void **)NULL : (owantedp));      \
+	*((owantedp) == NULL ? (volatile void **)NULL : (owantedp)) = NULL;   \
+} while (0)
+#endif
+#define	LOCKDEBUG_UNLOCKED(dodebug, lock, where, s)	__nothing
+#define	LOCKDEBUG_BARRIER(lock, slp)			__nothing
+#define	LOCKDEBUG_MEM_CHECK(base, sz)			__nothing

 #endif	/* LOCKDEBUG */

# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1771813534 0
#      Mon Feb 23 02:25:34 2026 +0000
# Branch trunk
# Node ID 0ed7b826070d61341f63757571d7406fc8466522
# Parent  6012a26811baaef76ae6093b3032098c2d60caca
# EXP-Topic riastradh-prNNNNN-showalllocks
WIP: Make `show all locks' work without LOCKDEBUG.

diff -r 6012a26811ba -r 0ed7b826070d sys/kern/subr_lockdebug.c
--- a/sys/kern/subr_lockdebug.c	Sun Feb 22 23:25:23 2026 +0000
+++ b/sys/kern/subr_lockdebug.c	Mon Feb 23 02:25:34 2026 +0000
@@ -60,6 +60,7 @@ __KERNEL_RCSID(0, "$NetBSD: subr_lockdeb
 #include <machine/db_machdep.h>
 #include <ddb/db_interface.h>
 #include <ddb/db_access.h>
+#include <ddb/db_proc.h>
 #include <ddb/db_sym.h>
 #endif

@@ -863,7 +864,17 @@ lockdebug_lock_print(void *addr,
 }

 #ifdef _KERNEL
+
+static void
+lockdebug_show_trace(const void *ptr,
+    void (*pr)(const char *, ...) __printflike(1, 2))
+{
+
+	db_stack_trace_print((db_expr_t)(intptr_t)ptr, true, 32, "a", pr);
+}
+
 #ifdef LOCKDEBUG
+
 static void
 lockdebug_show_one(lwp_t *l, lockdebug_t *ld, int i,
     void (*pr)(const char *, ...) __printflike(1, 2))
@@ -880,14 +891,6 @@ lockdebug_show_one(lwp_t *l, lockdebug_t
 }

 static void
-lockdebug_show_trace(const void *ptr,
-    void (*pr)(const char *, ...) __printflike(1, 2))
-{
-
-	db_stack_trace_print((db_expr_t)(intptr_t)ptr, true, 32, "a", pr);
-}
-
-static void
 lockdebug_show_all_locks_lwp(void (*pr)(const char *, ...) __printflike(1, 2),
     bool show_trace)
 {
@@ -957,15 +960,18 @@ lockdebug_show_all_locks_cpu(void (*pr)(
 		}
 	}
 }
-#endif /* _KERNEL */
+
 #endif	/* LOCKDEBUG */

+#endif /* _KERNEL */
+
 #ifdef _KERNEL
+/* XXX dedup with usr.sbin/crash.c lockdebug_show_all_locks */
 void
 lockdebug_show_all_locks(void (*pr)(const char *, ...) __printflike(1, 2),
     const char *modif)
 {
-#ifdef LOCKDEBUG
+#if defined LOCKDEBUG
 	bool show_trace = false;
 	if (modif[0] == 't')
 		show_trace = true;
@@ -977,6 +983,78 @@ lockdebug_show_all_locks(void (*pr)(cons
 	(*pr)("[Locks tracked through CPUs]\n");
 	lockdebug_show_all_locks_cpu(pr, show_trace);
 	(*pr)("\n");
+#elif defined DDB
+	struct proc *p;
+	bool show_trace = false;
+
+	if (modif[0] == 't')
+		show_trace = true;
+
+	(*pr)("%-5s %5s %3s %18s %18s %18s %4s\n",
+	    "PID", "LID", "CPU", "STRUCT LWP *", "LOCK", "OWNER", "BITS");
+	for (p = db_proc_first(); p != NULL; p = db_proc_next(p)) {
+		pid_t pid;
+		struct lwp *l;
+
+		db_read_bytes((db_addr_t)(uintptr_t)&p->p_pid, sizeof(pid),
+		    (char *)&pid);
+
+		for (db_read_bytes(
+			(db_addr_t)(uintptr_t) &LIST_FIRST(&p->p_lwps),
+			sizeof(l), (char *)&l);
+		    l != NULL;
+		    db_read_bytes(
+			(db_addr_t)(uintptr_t)&LIST_NEXT(l, l_sibling),
+			sizeof(l), (char *)&l)) {
+			lwpid_t lid;
+			int stat, pflag;
+			struct cpu_info *ci;
+			int cpuno = -1;
+			volatile void *ld_wanted;
+			uintptr_t ownerword;
+
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_ld_wanted,
+			    sizeof(ld_wanted), (char *)&ld_wanted);
+			if (ld_wanted == NULL)
+				continue;
+
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_lid,
+			    sizeof(lid), (char *)&lid);
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_stat,
+			    sizeof(stat), (char *)&stat);
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_pflag,
+			    sizeof(pflag), (char *)&pflag);
+			const bool run = (stat == LSONPROC ||
+			    (pflag & LP_RUNNING) != 0);
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_cpu,
+			    sizeof(ci), (char *)&ci);
+			if (ci != NULL) {
+				db_read_bytes(
+				    (db_addr_t)(uintptr_t)&ci->ci_index,
+				    sizeof(cpuno), (char *)&cpuno);
+			}
+			db_read_bytes((db_addr_t)(uintptr_t)ld_wanted,
+			    sizeof(ownerword), (char *)&ownerword);
+			(*pr)("%-5d%c%5d %3d %18lx %18lx %18lx %4x\n",
+			    pid, run ? '>' : ' ', lid, cpuno,
+			    (long)l, (long)ld_wanted,
+			    (long)(ownerword & ~(uintptr_t)ALIGNBYTES),
+			    (int)(ownerword & ALIGNBYTES));
+			if (show_trace) {
+				const struct lwp *owner =
+				    (const void *)(ownerword &
+					~(uintptr_t)ALIGNBYTES);
+
+				/*
+				 * XXX Skip this if the owner isn't a
+				 * pointer at all -- reader-held
+				 * rwlock, or spin mutex.
+				 */
+				if (owner != NULL)
+					lockdebug_show_trace(owner, pr);
+			}
+		}
+	}
 #else
 	(*pr)("Sorry, kernel not built with the LOCKDEBUG option.\n");
 #endif	/* LOCKDEBUG */
diff -r 6012a26811ba -r 0ed7b826070d usr.sbin/crash/crash.c
--- a/usr.sbin/crash/crash.c	Sun Feb 22 23:25:23 2026 +0000
+++ b/usr.sbin/crash/crash.c	Mon Feb 23 02:25:34 2026 +0000
@@ -85,13 +85,115 @@ static struct nlist nl[] = {
 };

 #ifdef LOCKDEBUG
+
 struct lockdebug;
 TAILQ_HEAD(, lockdebug) ld_all;
+static bool lockdebug_enabled;
+
+/* XXX dedup with sys/kern/subr_lockdebug.c lockdebug_show_trace */
+#include <sys/lockdebug.h>
+static void
+lockdebug_show_trace(const void *ptr,
+    void (*pr)(const char *, ...) __printflike(1, 2))
+{
+
+	db_stack_trace_print((db_expr_t)(intptr_t)ptr, true, 32, "a", pr);
+}
+
+/* XXX dedup with sys/kern/subr_lockdebug.c.c lockdebug_show_all_locks */
+#include <sys/cpu.h>
+#include <sys/lwp.h>
+#include <sys/proc.h>
+void
+lockdebug_show_all_locks(void (*pr)(const char *, ...) __printflike(1,2),
+    const char *modif)
+{
+	struct proc *p;
+	bool show_trace = false;
+
+	if (modif[0] == 't')
+		show_trace = true;
+
+	if (lockdebug_enabled) {
+		/* XXX implement me */
+		printf("This command only works in-kernel.\n");
+		return;
+	}
+
+	(*pr)("%-5s %5s %3s %18s %18s %18s %4s\n",
+	    "PID", "LID", "CPU", "STRUCT LWP *", "LOCK", "OWNER", "BITS");
+	for (p = db_proc_first(); p != NULL; p = db_proc_next(p)) {
+		pid_t pid;
+		struct lwp *l;
+
+		db_read_bytes((db_addr_t)(uintptr_t)&p->p_pid, sizeof(pid),
+		    (char *)&pid);
+
+		for (db_read_bytes(
+			(db_addr_t)(uintptr_t) &LIST_FIRST(&p->p_lwps),
+			sizeof(l), (char *)&l);
+		    l != NULL;
+		    db_read_bytes(
+			(db_addr_t)(uintptr_t)&LIST_NEXT(l, l_sibling),
+			sizeof(l), (char *)&l)) {
+			lwpid_t lid;
+			int stat, pflag;
+			struct cpu_info *ci;
+			int cpuno = -1;
+			volatile void *ld_wanted;
+			uintptr_t ownerword;
+
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_ld_wanted,
+			    sizeof(ld_wanted), (char *)&ld_wanted);
+			if (ld_wanted == NULL)
+				continue;
+
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_lid,
+			    sizeof(lid), (char *)&lid);
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_stat,
+			    sizeof(stat), (char *)&stat);
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_pflag,
+			    sizeof(pflag), (char *)&pflag);
+			const bool run = (stat == LSONPROC ||
+			    (pflag & LP_RUNNING) != 0);
+			db_read_bytes((db_addr_t)(uintptr_t)&l->l_cpu,
+			    sizeof(ci), (char *)&ci);
+			if (ci != NULL) {
+				db_read_bytes(
+				    (db_addr_t)(uintptr_t)&ci->ci_index,
+				    sizeof(cpuno), (char *)&cpuno);
+			}
+			db_read_bytes((db_addr_t)(uintptr_t)ld_wanted,
+			    sizeof(ownerword), (char *)&ownerword);
+			(*pr)("%-5d%c%5d %3d %18lx %18lx %18lx %4x\n",
+			    pid, run ? '>' : ' ', lid, cpuno,
+			    (long)l, (long)ld_wanted,
+			    (long)(ownerword & ~(uintptr_t)ALIGNBYTES),
+			    (int)(ownerword & ALIGNBYTES));
+			if (show_trace) {
+				const struct lwp *owner =
+				    (const void *)(ownerword &
+					~(uintptr_t)ALIGNBYTES);
+
+				/*
+				 * XXX Skip this if the owner isn't a
+				 * pointer at all -- reader-held
+				 * rwlock, or spin mutex.
+				 */
+				if (owner != NULL)
+					lockdebug_show_trace(owner, pr);
+			}
+		}
+	}
+}
+
 #else
+
 void lockdebug_lock_print(void);
 void lockdebug_lock_print(void) {
 	warnx("No lockdebug support compiled in");
 }
+
 #endif

 static void
@@ -439,8 +541,12 @@ main(int argc, char **argv)
 	}
 #ifdef LOCKDEBUG
 	if ((size_t)kvm_read(kd, nl[X_LOCKDEBUG].n_value, &ld_all,
-	    sizeof(ld_all)) != sizeof(ld_all))
+	    sizeof(ld_all)) == sizeof(ld_all)) {
+		lockdebug_enabled = true;
+	} else {
 		printf("Kernel compiled without options LOCKDEBUG.\n");
+		lockdebug_enabled = false;
+	}
 #endif

 	/*

>Release-Note:

>Audit-Trail:

State-Changed-From-To: open->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Sun, 16 Aug 2026 21:54:14 +0000
State-Changed-Why:
fixed in HEAD, let's pull this up after a few weeks of experience


From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60030 CVS commit: src/sys
Date: Sun, 16 Aug 2026 21:52:42 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Sun Aug 16 21:52:42 UTC 2026

 Modified Files:
 	src/sys/external/bsd/drm2/linux: linux_rwsem.c linux_ww_mutex.c
 	src/sys/kern: kern_lock.c kern_mutex.c kern_rwlock.c
 	src/sys/sys: lockdebug.h

 Log Message:
 kernel: Track what lock each lwp is waiting for without LOCKDEBUG.

 This is reasonably cheap -- one extra pointer on the stack, couple
 extra stores when _waiting_ (spinning or sleeping) for a _contended_
 lock -- and doesn't change the kernel ABI.  Will enable us to get
 diagnostics from crash dumps when, e.g., there's a softint deadlock
 tripping a heartbeat panic.

 PR kern/60030: ddb/crash: show all locks without LOCKDEBUG


 To generate a diff of this commit:
 cvs rdiff -u -r1.4 -r1.5 src/sys/external/bsd/drm2/linux/linux_rwsem.c
 cvs rdiff -u -r1.16 -r1.17 src/sys/external/bsd/drm2/linux/linux_ww_mutex.c
 cvs rdiff -u -r1.197 -r1.198 src/sys/kern/kern_lock.c
 cvs rdiff -u -r1.112 -r1.113 src/sys/kern/kern_mutex.c
 cvs rdiff -u -r1.76 -r1.77 src/sys/kern/kern_rwlock.c
 cvs rdiff -u -r1.22 -r1.23 src/sys/sys/lockdebug.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60030 CVS commit: src
Date: Sun, 16 Aug 2026 21:53:21 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Sun Aug 16 21:53:21 UTC 2026

 Modified Files:
 	src/sys/ddb: db_command.c db_extern.h
 	src/usr.sbin/crash: crash.c

 Log Message:
 ddb(4), crash(8): Make `show all locks' work without LOCKDEBUG.

 Any lwp currently waiting for a _contended_ lock will now record a
 pointer to that lock in struct lwp::l_ld_wanted, even without
 LOCKDEBUG.  We can't tell who holds shared locks or spin locks, but we
 can at least tell what lock a thread is waiting for when it's spinning
 for a spin lock, and generally present the graph of current lock
 dependencies reasonably well this way.

 PR kern/60030: ddb/crash: show all locks without LOCKDEBUG


 To generate a diff of this commit:
 cvs rdiff -u -r1.193 -r1.194 src/sys/ddb/db_command.c
 cvs rdiff -u -r1.17 -r1.18 src/sys/ddb/db_extern.h
 cvs rdiff -u -r1.17 -r1.18 src/usr.sbin/crash/crash.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60030 CVS commit: src/sys/sys
Date: Mon, 17 Aug 2026 01:52:09 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Mon Aug 17 01:52:09 UTC 2026

 Modified Files:
 	src/sys/sys: lockdebug.h

 Log Message:
 sys/lockdebug.h: Simplify conditional and explain why it's there.

 No functional change intended: just changes an always-false
 expression to another always-false expression in a conditional.

 (Well, in principle, it reduces the number of times we evaluate this
 argument, but in practice the argument should be side-effect-free
 anyway.)

 PR kern/60030: ddb/crash: show all locks without LOCKDEBUG


 To generate a diff of this commit:
 cvs rdiff -u -r1.23 -r1.24 src/sys/sys/lockdebug.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60030 CVS commit: src/sys/ddb
Date: Mon, 17 Aug 2026 19:31:50 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Mon Aug 17 19:31:50 UTC 2026

 Modified Files:
 	src/sys/ddb: db_command.c

 Log Message:
 ddb(4)/crash(8): Print lock symbols in `show all locks'.

 Reorder columns so the variable-width symbol comes last.

 PR kern/60030: ddb/crash: show all locks without LOCKDEBUG


 To generate a diff of this commit:
 cvs rdiff -u -r1.194 -r1.195 src/sys/ddb/db_command.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.