Download raw body.
sys/uvm: avoid deadlock in swapctl -d
On Sat, May 23, 2026 at 11:35:04PM +0200, Kirill A. Korinsky wrote:
> From: "Kirill A. Korinsky" <kirill@korins.ky>
> To: tech@openbsd.org
> Subject: Re: sys/uvm: avoid deadlock in swapctl -d
> Date: Sat, 23 May 2026 23:35:04 +0200
> User-Agent: Wanderlust
>
> On Mon, 13 Apr 2026 01:32:19 +0200,
> Kirill A. Korinsky <kirill@korins.ky> wrote:
> >
> > 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?
> >
I reproduced this on amd64 with a -current GENERIC.MP + WITNESS kernel.
With the diff applied, swapctl -d no longer produces the reversal, on the
same kernel and the same trigger.
So OK renaud@
I was unable to reproduce a crash though.
>
> Anyone?
>
> 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
>
sys/uvm: avoid deadlock in swapctl -d