Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
sys/uvm: avoid deadlock in swapctl -d
To:
OpenBSD tech <tech@openbsd.org>
Date:
Mon, 13 Apr 2026 01:32:19 +0200

Download raw body.

Thread
  • Kirill A. Korinsky:

    sys/uvm: avoid deadlock in swapctl -d

tech@,

I had noticed a deadlock which is possible to triger via swapctl -d.

To reproduce it, add to /etc/fstab someting like that:

172.31.2.23:/volume1/octeon/swap.octeon none swap sw,nfsmntpt=/swap

And now just run swapctl -d /swap

For example with MP_LOCKDEBUG and WITNESS kernel I do have:

octeon$ doas sync; doas swapctl; doas swapctl -d /swap
Device      512-blocks     Used    Avail Capacity  Priority
/swap         33554432        0 33554432     0%    0
witness: lock order reversal:
 1st 0xffffffff816be9f8 amaplstlk (amaplstlk)
 2nd 0x9800000419227688 amaplk (&amap->am_lock)
lock order [1] amaplstlk (amaplstlk) -> [2] amaplk (&amap->am_lock)
#0  witness_checkorder+0x50c
#1  rw_do_enter_write+0x84
#2  rw_enter+0x78
#3  amap_swap_off+0xb8
#4  swap_off+0x120
#5  sys_swapctl+0x448
#6  itsa+0x118c
#7  trap+0x1f8
#8  u_general+0xd8
lock order [2] amaplk (&amap->am_lock) -> [1] amaplstlk (amaplstlk)
#0  witness_checkorder+0x50c
#1  rw_do_enter_write+0x84
#2  rw_enter_write+0x30
#3  amap_wipeout+0x98
#4  amap_unref+0x84
#5  uvm_unmap_detach+0xcc
#6  uvmspace_exec+0x218
#7  sys_execve+0x6cc
#8  start_init+0x338
#9  proc_trampoline+0x1c
Stopped at      db_enter+0x4:   jr      ra
db_enter+0x8:    nop
ddb{0}>

I had encountered crashes from swapctl -d a while ago:
https://marc.info/?l=openbsd-bugs&m=172195369905445&w=2
but I not sure that it is related.

Thouhgs? OK?

Index: sys/uvm/uvm_amap.c
===================================================================
RCS file: /home/cvs/src/sys/uvm/uvm_amap.c,v
diff -u -p -r1.98 uvm_amap.c
--- sys/uvm/uvm_amap.c	10 Dec 2025 08:38:18 -0000	1.98
+++ sys/uvm/uvm_amap.c	12 Apr 2026 23:16:06 -0000
@@ -478,8 +478,6 @@ amap_wipeout(struct vm_amap *amap)
 		return;
 	}
 
-	amap_list_remove(amap);
-
 	AMAP_CHUNK_FOREACH(chunk, amap) {
 		int i, refs, map = chunk->ac_usedmap;
 
@@ -1063,7 +1061,7 @@ amap_swap_off(int startslot, int endslot
 		struct vm_amap_chunk *chunk;
 
 		amap_lock(am, RW_WRITE);
-		if (am->am_nused == 0) {
+		if (am->am_ref == 0 || am->am_nused == 0) {
 			amap_unlock(am);
 			am_next = LIST_NEXT(am, am_list);
 			continue;
@@ -1096,6 +1094,9 @@ again:
 
 				am->am_flags &= ~AMAP_SWAPOFF;
 				if (amap_refs(am) == 0) {
+					amap_unlock(am);
+					amap_list_remove(am);
+					amap_lock(am, RW_WRITE);
 					amap_wipeout(am);
 					am = NULL;
 					goto nextamap;
@@ -1333,6 +1334,13 @@ amap_unref(struct vm_amap *amap, vaddr_t
 		 * If the last reference - wipeout and destroy the amap.
 		 */
 		amap->am_ref--;
+		if ((amap->am_flags & AMAP_SWAPOFF) != 0) {
+			amap_wipeout(amap);
+			return;
+		}
+		amap_unlock(amap);
+		amap_list_remove(amap);
+		amap_lock(amap, RW_WRITE);
 		amap_wipeout(amap);
 		return;
 	}

-- 
wbr, Kirill