NetBSD Problem Report #56764
From www@netbsd.org Mon Mar 21 09:34:45 2022
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))
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id 88FE61A921F
for <gnats-bugs@gnats.NetBSD.org>; Mon, 21 Mar 2022 09:34:45 +0000 (UTC)
Message-Id: <20220321093444.096081A9239@mollari.NetBSD.org>
Date: Mon, 21 Mar 2022 09:34:43 +0000 (UTC)
From: prlw1@cam.ac.uk
Reply-To: prlw1@cam.ac.uk
To: gnats-bugs@NetBSD.org
Subject: panic assertion "uvmexp.swpgonly > 0"
X-Send-Pr-Version: www-1.0
>Number: 56764
>Category: kern
>Synopsis: panic assertion "uvmexp.swpgonly > 0"
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Mar 21 09:35:00 +0000 2022
>Last-Modified: Fri Apr 03 12:25:01 +0000 2026
>Originator: Patrick Welche
>Release: NetBSD-9.99.95/amd64 20 Mar 2022
>Organization:
>Environment:
>Description:
During "shutdown -p now" of a pbulk run that was swapping heavily (has
something changed with libreoffice?), the following panic happened
(leaving disks needing fsck -y)
(gdb) print panicstr
$1 = 0xffffffff8151cc80 <scratchstr> "kernel diagnostic assertion \"uvmexp.swpgonly > 0\" failed: file \"../../../../uvm/uvm_swap.c\", line 1830 "
(gdb) bt
#0 0xffffffff802229c5 in cpu_reboot (howto=howto@entry=260,
bootstr=bootstr@entry=0x0) at ../../../../arch/amd64/amd64/machdep.c:720
#1 0xffffffff80aca704 in kern_reboot (howto=howto@entry=260,
bootstr=bootstr@entry=0x0) at ../../../../kern/kern_reboot.c:73
#2 0xffffffff80b0db72 in vpanic (
fmt=0xffffffff80fce6f0 "kernel %sassertion \"%s\" failed: file \"%s\", line %d ", ap=ap@entry=0xffffab04b0be4ad8) at ../../../../kern/subr_prf.c:290
#3 0xffffffff80c81c8f in kern_assert (
fmt=fmt@entry=0xffffffff80fce6f0 "kernel %sassertion \"%s\" failed: file \"%s\", line %d ") at ../../../../../../lib/libkern/kern_assert.c:51
#4 0xffffffff80a8793d in uvm_swap_get (page=<optimized out>,
page@entry=0xffffab0020000d00, swslot=<optimized out>, flags=flags@entry=2)
at ../../../../uvm/uvm_swap.c:1830
#5 0xffffffff80a6798e in uvmfault_anonget (ufi=ufi@entry=0x0,
amap=amap@entry=0xffff9665d0e40198, anon=anon@entry=0xffff9665db0cdad8)
at ../../../../uvm/uvm_fault.c:392
#6 0xffffffff80a61bb8 in uvm_anon_pagein (amap=amap@entry=0xffff9665d0e40198,
anon=0xffff9665db0cdad8) at ../../../../uvm/uvm_anon.c:287
#7 0xffffffff80a60ad2 in amap_swap_off (startslot=1, endslot=122095319)
at ../../../../uvm/uvm_amap.c:1402
#8 0xffffffff80a8513b in swap_off (l=l@entry=0xffff9665e3dde480,
sdp=0xffff9665d12ed100) at ../../../../uvm/uvm_swap.c:1082
#9 0xffffffff80a86af7 in sys_swapctl (l=0xffff9665e3dde480,
uap=0xffffab04b0be4f00, retval=<optimized out>)
at ../../../../uvm/uvm_swap.c:748
#10 0xffffffff803fd09e in sy_call (rval=0xffffab04b0be4eb0,
uap=0xffffab04b0be4f00, l=0xffff9665e3dde480,
sy=0xffffffff814639e8 <sysent+6504>) at ../../../../sys/syscallvar.h:65
#11 sy_invoke (code=271, rval=0xffffab04b0be4eb0, uap=0xffffab04b0be4f00,
l=0xffff9665e3dde480, sy=0xffffffff814639e8 <sysent+6504>)
at ../../../../sys/syscallvar.h:94
#12 syscall (frame=0xffffab04b0be4f00)
at ../../../../arch/x86/x86/syscall.c:138
#13 0xffffffff8020867d in handle_syscall ()
#14 0x0000000000000002 in ?? ()
#15 0x00007f7fffffd7f0 in ?? ()
#16 0x0000000000000000 in ?? ()
(gdb) print uvmexp
$5 = {pagesize = 4096, pagemask = 4095, pageshift = 12, npages = 16245534,
free = 5948528, paging = 0, wired = 4978, ncolors = 16, colormask = 15,
zeropages = 0, reserve_pagedaemon = 1, reserve_kernel = 80,
anonpages = 12304, filepages = 6062858, execpages = 16584, freemin = 4096,
freetarg = 5461, wiredmax = 5415178, nswapdev = 1, swpages = 122095317,
swpgavail = 0, swpginuse = 2, swpgonly = 0, nswget = 1903783,
faults = 1647202138, traps = 1648322817, intrs = 4570866, swtch = 73563035,
softs = 8174162, syscalls = -1962293868, pageins = 68644, _unused1 = 0,
_unused2 = 0, pgswapin = 0, pgswapout = 5382281, forks = 2573596,
forks_ppwait = 1630951, forks_sharevm = 1630951, pga_zerohit = 0,
pga_zeromiss = 0, zeroaborts = 0, colorhit = 1333019305,
colormiss = 139555180, cpuhit = 1156296487, cpumiss = 316277998,
fltnoram = 11842172, fltnoanon = 0, fltpgwait = 53, fltpgrele = 0,
fltrelck = 452245, fltrelckok = 452073, fltanget = 59468194,
fltanretry = 68735, fltamcopy = 157083835, fltnamap = 55460761,
fltnomap = 1168414334, fltlget = 326455861, fltget = 415713,
flt_anon = 35398387, flt_acow = 24037224, flt_obj = 245215384,
flt_prcopy = 81224287, flt_przero = 1245736078, pdwoke = 3499331,
pdrevs = 1612, _unused3 = 0, pdfreed = 31670364, pdscans = 62863648,
pdanscan = 229129, pdobscan = 31441586, pdreact = 35679, pdbusy = 0,
pdpageouts = 337035, pdpending = 5045166, pddeact = 66951433, pdreanon = 0,
pdrefile = 0, pdreexec = 0, bootpages = 507705}
>How-To-Repeat:
>Fix:
>Audit-Trail:
From: Thomas Klausner <wiz@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc: Chuck Silvers <chuq@chuq.com>
Subject: Re: kern/56764: panic assertion "uvmexp.swpgonly > 0"
Date: Mon, 25 Aug 2025 13:55:38 +0200
I haven't seen this particular panic yet, but I have problems with
this counter since at least 2017.
My latest one was
panic: kernel diagnostic assertion "uvmexp.swpgonly < uvmexp.swpginuse" failed: file "/usr/src/sys/uvm/uvm_pdaemon.c", line 795
The backtrace is
(gdb) bt
#0 0xffffffff80239b85 in cpu_reboot (howto=howto@entry=260, bootstr=bootstr@entry=0x0) at /usr/src/sys/arch/amd64/amd64/machdep.c:709
#1 0xffffffff80de5a95 in kern_reboot (howto=howto@entry=260, bootstr=bootstr@entry=0x0) at /usr/src/sys/kern/kern_reboot.c:91
#2 0xffffffff80e2ba63 in vpanic (fmt=0xffffffff81427c00 "kernel %sassertion \"%s\" failed: file \"%s\", line %d ", ap=ap@entry=0xffffc1a47baa2e98) at /usr/src/sys/kern/subr_prf.c:288
#3 0xffffffff80ffb6fe in kern_assert (fmt=fmt@entry=0xffffffff81427c00 "kernel %sassertion \"%s\" failed: file \"%s\", line %d ") at /usr/src/sys/lib/libkern/kern_assert.c:51
#4 0xffffffff80d9532c in uvmpd_scan_queue () at /usr/src/sys/uvm/uvm_pdaemon.c:795
#5 uvmpd_scan () at /usr/src/sys/uvm/uvm_pdaemon.c:917
#6 uvm_pageout (arg=<optimized out>) at /usr/src/sys/uvm/uvm_pdaemon.c:318
#7 0xffffffff80210327 in lwp_trampoline ()
#8 0x0000000000000000 in ?? ()
Chuck Silvers took a number of looks at this over the years, and his
best guess is that the counters are not maintained correctly, but we
haven't found the root cause of this.
Thomas
From: "Thomas Klausner" <wiz@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/56764 CVS commit: src/sys/uvm
Date: Sun, 4 Jan 2026 00:41:15 +0000
Module Name: src
Committed By: wiz
Date: Sun Jan 4 00:41:14 UTC 2026
Modified Files:
src/sys/uvm: uvm_pdaemon.c uvm_swap.c
Log Message:
Add more debugging.
To help understand PR 56764 better.
Ok riastradh@
To generate a diff of this commit:
cvs rdiff -u -r1.134 -r1.135 src/sys/uvm/uvm_pdaemon.c
cvs rdiff -u -r1.209 -r1.210 src/sys/uvm/uvm_swap.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: Thomas Klausner <wiz@netbsd.org>
To: NetBSD bugtracking <gnats-bugs@netbsd.org>
Cc:
Subject: Re: kern/56764
Date: Tue, 10 Feb 2026 07:51:00 +0100
The newly added assertions have been triggered today.
[ 56284.8616186] panic: kernel diagnostic assertion "uvmexp.swpgonly < uvmexp.swpginuse" failed: file "/usr/src/sys/uvm/uvm_pdaemon.c", line 795 swpgonly 18576318 swpginuse 18570395
Kernel backtrace:
(gdb) target kvm netbsd.25.core
0xffffffff80239eca in cpu_reboot (howto=howto@entry=260, bootstr=bootstr@entry=0x0) at /usr/src/sys/arch/amd64/amd64/machdep.c:709
709 dumpsys();
(gdb) bt
#0 0xffffffff80239eca in cpu_reboot (howto=howto@entry=260, bootstr=bootstr@entry=0x0) at /usr/src/sys/arch/amd64/amd64/machdep.c:709
#1 0xffffffff80e2be7f in kern_reboot (howto=howto@entry=260, bootstr=bootstr@entry=0x0) at /usr/src/sys/kern/kern_reboot.c:91
#2 0xffffffff80e77155 in vpanic (fmt=0xffffffff816ee020 "kernel %sassertion \"%s\" failed: file \"%s\", line %d swpgonly %d swpginuse %d", ap=ap@entry=0xffffad247baabe98) at /usr/src/sys/kern/subr_prf.c:288
#3 0xffffffff81051bbe in kern_assert (fmt=fmt@entry=0xffffffff816ee020 "kernel %sassertion \"%s\" failed: file \"%s\", line %d swpgonly %d swpginuse %d") at /usr/src/sys/lib/libkern/kern_assert.c:51
#4 0xffffffff80dd86a3 in uvmpd_scan_queue () at /usr/src/sys/uvm/uvm_pdaemon.c:795
#5 uvmpd_scan () at /usr/src/sys/uvm/uvm_pdaemon.c:919
#6 uvm_pageout (arg=<optimized out>) at /usr/src/sys/uvm/uvm_pdaemon.c:318
#7 0xffffffff80210327 in lwp_trampoline ()
#8 0x0000000000000000 in ?? ()
(gdb) fr 4
#4 0xffffffff80dd86a3 in uvmpd_scan_queue () at /usr/src/sys/uvm/uvm_pdaemon.c:795
795 KASSERTMSG(uvmexp.swpgonly < uvmexp.swpginuse,
(gdb) l
790 } else {
791 slot = uao_find_swslot(uobj, pageidx);
792 }
793 if (slot > 0) {
794 /* this page is now only in swap. */
795 KASSERTMSG(uvmexp.swpgonly < uvmexp.swpginuse,
796 "swpgonly %d swpginuse %d",
797 uvmexp.swpgonly, uvmexp.swpginuse);
798 atomic_inc_uint(&uvmexp.swpgonly);
799 }
(gdb) p slot
$1 = <optimized out>
(gdb) p uvmexp
$2 = {pagesize = 4096, pagemask = 4095, pageshift = 12, npages = 32315696, free = 792, paging = 0, wired = 4979, ncolors = 32, colormask = 31, zeropages = 0, reserve_pagedaemon = 1, reserve_kernel = 160,
anonpages = 22220503, filepages = 5515267, execpages = 96251, freemin = 8192, freetarg = 10922, wiredmax = 10771898, nswapdev = 3, swpages = 133993646, swpgavail = 133993646, swpginuse = 18418907,
swpgonly = 18576494, nswget = 839794, faults = 431457751, traps = 444959363, intrs = 123789873, swtch = 801648567, softs = 196734403, syscalls = 1279015017, pageins = 317835, _unused1 = 0, _unused2 = 0, pgswapin = 0,
pgswapout = 57374711, forks = 2716424, forks_ppwait = 1178253, forks_sharevm = 1178253, pga_zerohit = 0, pga_zeromiss = 0, zeroaborts = 0, colorhit = -1069245636, colormiss = 155300626, cpuhit = -2103165643,
cpumiss = 1189220633, fltnoram = 125291247, fltnoanon = 0, fltpgwait = 2919, fltpgrele = 0, fltrelck = 1752063358, fltrelckok = 1752006885, fltanget = 157777663, fltanretry = 386737, fltamcopy = 481707478,
fltnamap = 144737577, fltnomap = 978014010, fltlget = -2138418117, fltget = 1751675474, flt_anon = 94785099, flt_acow = 62991413, flt_obj = 1814723619, flt_prcopy = 298511667, flt_przero = -1979758914,
pdwoke = 30586934, pdrevs = 1259, _unused3 = 0, pdfreed = 91987649, pdscans = 120124040, pdanscan = 690052, pdobscan = 91298403, pdreact = 431879, pdbusy = 0, pdpageouts = 3586174, pdpending = 53788537,
pddeact = 224044232, pdreanon = 0, pdrefile = 0, pdreexec = 0, bootpages = 1009928}
(gdb) p uvmexp.swpgonly
$3 = 18576494
(gdb) p uvmexp.swpginuse
$4 = 18418907
I don't understand why the numbers here are both different than in the
panic message itself.
Also, some (unrelated?) values are negative, like:
colorhit = -1069245636
which doesn't make sense to me either.
Core dump is available on request.
Cheers,
Thomas
From: Thomas Klausner <wiz@netbsd.org>
To: NetBSD bugtracking <gnats-bugs@netbsd.org>
Cc:
Subject: Re: kern/56764
Date: Sat, 21 Feb 2026 14:19:35 +0100
And again today:
panic: kernel diagnostic assertion "uvmexp.swpgonly < uvmexp.swpginuse" failed: file "/usr/src/sys/uvm/uvm_pdaemon.c", line 795 swpgonly 26000749 swpginuse 25888760
(gdb) bt
#0 0xffffffff80239eca in cpu_reboot (howto=howto@entry=260, bootstr=bootstr@entry=0x0) at /usr/src/sys/arch/amd64/amd64/machdep.c:709
#1 0xffffffff80e2bfbf in kern_reboot (howto=howto@entry=260, bootstr=bootstr@entry=0x0) at /usr/src/sys/kern/kern_reboot.c:91
#2 0xffffffff80e77295 in vpanic (fmt=0xffffffff816ee020 "kernel %sassertion \"%s\" failed: file \"%s\", line %d swpgonly %d swpginuse %d", ap=ap@entry=0xffffde247baa4e98)
at /usr/src/sys/kern/subr_prf.c:288
#3 0xffffffff81051cfe in kern_assert (fmt=fmt@entry=0xffffffff816ee020 "kernel %sassertion \"%s\" failed: file \"%s\", line %d swpgonly %d swpginuse %d")
at /usr/src/sys/lib/libkern/kern_assert.c:51
#4 0xffffffff80dd86a3 in uvmpd_scan_queue () at /usr/src/sys/uvm/uvm_pdaemon.c:795
#5 uvmpd_scan () at /usr/src/sys/uvm/uvm_pdaemon.c:919
#6 uvm_pageout (arg=<optimized out>) at /usr/src/sys/uvm/uvm_pdaemon.c:318
#7 0xffffffff80210327 in lwp_trampoline ()
#8 0x0000000000000000 in ?? ()
(gdb) p uvmexp
$1 = {pagesize = 4096, pagemask = 4095, pageshift = 12, npages = 32315697, free = 8039, paging = 16, wired = 4913, ncolors = 32, colormask = 31, zeropages = 0,
reserve_pagedaemon = 1, reserve_kernel = 160, anonpages = 21282242, filepages = 4459464, execpages = 959813, freemin = 8192, freetarg = 10922, wiredmax = 10771899,
nswapdev = 3, swpages = 133993646, swpgavail = 133993646, swpginuse = 25789384, swpgonly = 25706596, nswget = 29166374, faults = 2039508928, traps = -2132403363,
intrs = 502227833, swtch = -13256194, softs = 754970585, syscalls = -364678381, pageins = 2547602, _unused1 = 0, _unused2 = 0, pgswapin = 0, pgswapout = 529198543,
forks = 11607662, forks_ppwait = 3708387, forks_sharevm = 3708387, pga_zerohit = 0, pga_zeromiss = 0, zeroaborts = 0, colorhit = 578435610, colormiss = 694700272,
cpuhit = 686404484, cpumiss = 586731398, fltnoram = 849845748, fltnoanon = 0, fltpgwait = 119329, fltpgrele = 0, fltrelck = -1500510270, fltrelckok = -1500820650,
fltanget = 1101511225, fltanretry = 2987802, fltamcopy = 1912608152, fltnamap = 969584305, fltnomap = 252048530, fltlget = 31900090, fltget = -1503503167,
flt_anon = 689900560, flt_acow = 411604461, flt_obj = -1188934567, flt_prcopy = 964935297, flt_przero = 303214857, pdwoke = 173361105, pdrevs = 37988, _unused3 = 0,
pdfreed = 775174807, pdscans = 986789583, pdanscan = 4021869, pdobscan = 771157076, pdreact = 3977394, pdbusy = 32, pdpageouts = 33088753, pdpending = 496109791,
pddeact = 1571316217, pdreanon = 0, pdrefile = 0, pdreexec = 0, bootpages = 1009928}
again the numbers are different than in the assert, to wit:
assert: swpgonly 26000749 swpginuse 25888760
struct uvmpexp: swpgonly 25706596 swpginuse 25789384
so they are fine in the struct but not in the assert.
But the assert is for exactly these:
795 KASSERTMSG(uvmexp.swpgonly < uvmexp.swpginuse,
796 "swpgonly %d swpginuse %d",
797 uvmexp.swpgonly, uvmexp.swpginuse);
so is something else modifying the values without a proper lock?
Thomas
From: Thomas Klausner <wiz@gatalith.at>
To: NetBSD bugtracking <gnats-bugs@netbsd.org>
Cc:
Subject: Re: kern/56764
Date: Sat, 21 Feb 2026 16:49:59 +0100
--ykk54ve36zcfxrfd
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hi!
I'm trying to find causes why I get
kernel diagnostic assertion "uvmexp.swpgonly < uvmexp.swpginuse" failed
quite regularly.
void uvm_aio_aiodone_pages(struct vm_page **pgs, int npages, bool
write, int error) in uvm_pager.c v1.131 ends with this:
if (!swap) {
uvm_page_unbusy(pgs, npages);
rw_exit(slock);
} else {
#if defined(VMSWAP)
KASSERT(write);
/* these pages are now only in swap. */
if (error != ENOMEM) {
atomic_add_int(&uvmexp.swpgonly, npages);
}
if (error) {
if (error != ENOMEM)
uvm_swap_markbad(swslot, npages);
else
uvm_swap_free(swslot, npages);
}
atomic_dec_uint(&uvmexp.pdpending);
#endif /* defined(VMSWAP) */
}
In GENERIC on my system, VMSWAP is 1.
So, if 'swap != 0' and 'error != ENOMEM', uvmexp.swpgonly is increased
by npages. In case error is not 0 and not ENOMEM, it is then decreased
again in uvm_swap_markbad().
In uvm_swap_markbad() the change is protected by
mutex_enter(&uvm_swap_data_lock), but here in uvm_aio_aiodone_pages()
it isn't.
1. Should this be protected by mutex_enter(&uvm_swap_data_lock) in
uvm_aio_aiodone_pages()?
2. How is it ensured that uvmexp.swpgonly < uvmexp.swpginuse here when
error = 0? uvm_aio_aiodone_pages() does not increase swpginuse.
I see a couple other places where changing swpgonly is not protected
by the mutex.
The other suspect are the two places where swpginuse is reduced,
neither of them compares against swpgonly.
I'll try the attached diff (I've had to make the lock available in
uvm_swap.h for the three places outside of uvm_swap.c that modify
swpgonly.)
Thomas
--ykk54ve36zcfxrfd
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="uvm.diff"
Index: uvm_km.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_km.c,v
retrieving revision 1.166
diff -u -r1.166 uvm_km.c
--- uvm_km.c 7 Dec 2024 23:19:07 -0000 1.166
+++ uvm_km.c 21 Feb 2026 15:32:36 -0000
@@ -491,8 +491,10 @@
rw_exit(uobj->vmobjlock);
if (swpgonlydelta > 0) {
+ mutex_enter(&uvm_swap_data_lock);
KASSERT(uvmexp.swpgonly >= swpgonlydelta);
atomic_add_int(&uvmexp.swpgonly, -swpgonlydelta);
+ mutex_exit(&uvm_swap_data_lock);
}
}
Index: uvm_pager.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_pager.c,v
retrieving revision 1.131
diff -u -r1.131 uvm_pager.c
--- uvm_pager.c 15 Mar 2024 07:09:37 -0000 1.131
+++ uvm_pager.c 21 Feb 2026 15:32:36 -0000
@@ -483,7 +483,12 @@
/* these pages are now only in swap. */
if (error != ENOMEM) {
+ mutex_enter(&uvm_swap_data_lock);
+ KASSERTMSG(uvmexp.swpginuse >= uvmexp.swpgonly + npages,
+ "swpginuse %d swpgonly %d npages %d",
+ uvmexp.swpginuse, uvmexp.swpgonly, npages);
atomic_add_int(&uvmexp.swpgonly, npages);
+ mutex_enter(&uvm_swap_data_lock);
}
if (error) {
if (error != ENOMEM)
Index: uvm_pdaemon.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_pdaemon.c,v
retrieving revision 1.135
diff -u -r1.135 uvm_pdaemon.c
--- uvm_pdaemon.c 4 Jan 2026 00:41:14 -0000 1.135
+++ uvm_pdaemon.c 21 Feb 2026 15:32:37 -0000
@@ -792,10 +792,12 @@
}
if (slot > 0) {
/* this page is now only in swap. */
+ mutex_enter(&uvm_swap_data_lock);
KASSERTMSG(uvmexp.swpgonly < uvmexp.swpginuse,
"swpgonly %d swpginuse %d",
uvmexp.swpgonly, uvmexp.swpginuse);
atomic_inc_uint(&uvmexp.swpgonly);
+ mutex_exit(&uvm_swap_data_lock);
}
rw_exit(slock);
continue;
Index: uvm_swap.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_swap.c,v
retrieving revision 1.215
diff -u -r1.215 uvm_swap.c
--- uvm_swap.c 13 Feb 2026 19:16:41 -0000 1.215
+++ uvm_swap.c 21 Feb 2026 15:32:37 -0000
@@ -201,7 +201,7 @@
static struct swap_priority swap_priority;
/* locks */
-static kmutex_t uvm_swap_data_lock __cacheline_aligned;
+kmutex_t uvm_swap_data_lock __cacheline_aligned;
static krwlock_t swap_syscall_lock;
bool uvm_swap_init_done = false;
@@ -1246,6 +1246,9 @@
KASSERTMSG(uvmexp.swpginuse >= sdp->swd_npgbad,
"swpginuse %d sdp->swd_npgbad %d",
uvmexp.swpginuse, sdp->swd_npgbad);
+ KASSERTMSG(uvmexp.swpginuse >= uvmexp.swpgonly + sdp->swd_npgbad,
+ "swpginuse %d swpgonly %d sdp->swd_npgbad %d",
+ uvmexp.swpginuse, uvmexp.swpgonly, sdp->swd_npgbad);
uvmexp.swpginuse -= sdp->swd_npgbad;
if (swaplist_find(sdp->swd_vp, true) == NULL)
@@ -1932,6 +1935,9 @@
sdp->swd_npginuse -= nslots;
KASSERTMSG(uvmexp.swpginuse >= nslots, "swpginuse %d nslots %d",
uvmexp.swpginuse, nslots);
+ KASSERTMSG(uvmexp.swpginuse >= uvmexp.swpgonly + nslots,
+ "swpginuse %d swpgonly %d nslots %d",
+ uvmexp.swpginuse, uvmexp.swpgonly, nslots);
uvmexp.swpginuse -= nslots;
mutex_exit(&uvm_swap_data_lock);
}
@@ -1977,8 +1983,10 @@
* this page is no longer only in swap.
*/
+ mutex_enter(&uvm_swap_data_lock);
KASSERT(uvmexp.swpgonly > 0);
atomic_dec_uint(&uvmexp.swpgonly);
+ mutex_exit(&uvm_swap_data_lock);
}
return error;
}
Index: uvm_swap.h
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_swap.h,v
retrieving revision 1.29
diff -u -r1.29 uvm_swap.h
--- uvm_swap.h 15 Mar 2024 22:15:21 -0000 1.29
+++ uvm_swap.h 21 Feb 2026 15:32:37 -0000
@@ -56,6 +56,7 @@
int uvm_swap_stats(char *, int,
void (*)(void *, const struct swapent *), size_t, register_t *);
+extern kmutex_t uvm_swap_data_lock;
#else /* defined(VMSWAP) */
#define uvm_swapisfull() true
--ykk54ve36zcfxrfd--
From: Thomas Klausner <wiz@netbsd.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/56764
Date: Sat, 21 Feb 2026 17:23:25 +0100
--blvg5djcc6d5falj
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
The first diff causes a 'locking against myself' error in
uvm_aio_aiodone_pages, so I removed the lock access in uvm_pager.c and
am trying this version now.
Thomas
--blvg5djcc6d5falj
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="uvm.2.diff"
Index: uvm_km.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_km.c,v
retrieving revision 1.166
diff -u -r1.166 uvm_km.c
--- uvm_km.c 7 Dec 2024 23:19:07 -0000 1.166
+++ uvm_km.c 21 Feb 2026 16:21:45 -0000
@@ -491,8 +491,10 @@
rw_exit(uobj->vmobjlock);
if (swpgonlydelta > 0) {
+ mutex_enter(&uvm_swap_data_lock);
KASSERT(uvmexp.swpgonly >= swpgonlydelta);
atomic_add_int(&uvmexp.swpgonly, -swpgonlydelta);
+ mutex_exit(&uvm_swap_data_lock);
}
}
Index: uvm_pager.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_pager.c,v
retrieving revision 1.131
diff -u -r1.131 uvm_pager.c
--- uvm_pager.c 15 Mar 2024 07:09:37 -0000 1.131
+++ uvm_pager.c 21 Feb 2026 16:21:45 -0000
@@ -483,6 +483,9 @@
/* these pages are now only in swap. */
if (error != ENOMEM) {
+ KASSERTMSG(uvmexp.swpginuse >= uvmexp.swpgonly + npages,
+ "swpginuse %d swpgonly %d npages %d",
+ uvmexp.swpginuse, uvmexp.swpgonly, npages);
atomic_add_int(&uvmexp.swpgonly, npages);
}
if (error) {
Index: uvm_pdaemon.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_pdaemon.c,v
retrieving revision 1.135
diff -u -r1.135 uvm_pdaemon.c
--- uvm_pdaemon.c 4 Jan 2026 00:41:14 -0000 1.135
+++ uvm_pdaemon.c 21 Feb 2026 16:21:45 -0000
@@ -792,10 +792,12 @@
}
if (slot > 0) {
/* this page is now only in swap. */
+ mutex_enter(&uvm_swap_data_lock);
KASSERTMSG(uvmexp.swpgonly < uvmexp.swpginuse,
"swpgonly %d swpginuse %d",
uvmexp.swpgonly, uvmexp.swpginuse);
atomic_inc_uint(&uvmexp.swpgonly);
+ mutex_exit(&uvm_swap_data_lock);
}
rw_exit(slock);
continue;
Index: uvm_swap.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_swap.c,v
retrieving revision 1.215
diff -u -r1.215 uvm_swap.c
--- uvm_swap.c 13 Feb 2026 19:16:41 -0000 1.215
+++ uvm_swap.c 21 Feb 2026 16:21:45 -0000
@@ -201,7 +201,7 @@
static struct swap_priority swap_priority;
/* locks */
-static kmutex_t uvm_swap_data_lock __cacheline_aligned;
+kmutex_t uvm_swap_data_lock __cacheline_aligned;
static krwlock_t swap_syscall_lock;
bool uvm_swap_init_done = false;
@@ -1246,6 +1246,9 @@
KASSERTMSG(uvmexp.swpginuse >= sdp->swd_npgbad,
"swpginuse %d sdp->swd_npgbad %d",
uvmexp.swpginuse, sdp->swd_npgbad);
+ KASSERTMSG(uvmexp.swpginuse >= uvmexp.swpgonly + sdp->swd_npgbad,
+ "swpginuse %d swpgonly %d sdp->swd_npgbad %d",
+ uvmexp.swpginuse, uvmexp.swpgonly, sdp->swd_npgbad);
uvmexp.swpginuse -= sdp->swd_npgbad;
if (swaplist_find(sdp->swd_vp, true) == NULL)
@@ -1932,6 +1935,9 @@
sdp->swd_npginuse -= nslots;
KASSERTMSG(uvmexp.swpginuse >= nslots, "swpginuse %d nslots %d",
uvmexp.swpginuse, nslots);
+ KASSERTMSG(uvmexp.swpginuse >= uvmexp.swpgonly + nslots,
+ "swpginuse %d swpgonly %d nslots %d",
+ uvmexp.swpginuse, uvmexp.swpgonly, nslots);
uvmexp.swpginuse -= nslots;
mutex_exit(&uvm_swap_data_lock);
}
@@ -1977,8 +1983,10 @@
* this page is no longer only in swap.
*/
+ mutex_enter(&uvm_swap_data_lock);
KASSERT(uvmexp.swpgonly > 0);
atomic_dec_uint(&uvmexp.swpgonly);
+ mutex_exit(&uvm_swap_data_lock);
}
return error;
}
Index: uvm_swap.h
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_swap.h,v
retrieving revision 1.29
diff -u -r1.29 uvm_swap.h
--- uvm_swap.h 15 Mar 2024 22:15:21 -0000 1.29
+++ uvm_swap.h 21 Feb 2026 16:21:45 -0000
@@ -56,6 +56,7 @@
int uvm_swap_stats(char *, int,
void (*)(void *, const struct swapent *), size_t, register_t *);
+extern kmutex_t uvm_swap_data_lock;
#else /* defined(VMSWAP) */
#define uvm_swapisfull() true
--blvg5djcc6d5falj--
From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org, netbsd-bugs@netbsd.org, wiz@netbsd.org
Cc:
Subject: Re: kern/56764
Date: Sun, 22 Feb 2026 00:10:53 +0700
Date: Sat, 21 Feb 2026 13:20:02 +0000 (UTC)
From: "Thomas Klausner via gnats" <gnats-admin@NetBSD.org>
Message-ID: <20260221132002.315021A923D@mollari.NetBSD.org>
| so is something else modifying the values without a proper lock?
I know nothing about the UVM system, so nothing I say should be treated
as being correct, but will perhaps inspire someone who knows more to
correct me.
But I just took a look, and I can see no evidence at all that anything in
uvmexp expects to take any kind of lock before modifications.
Some values (like uvmexp.swpgonly) are only ever modified via atomic
ops (increment/decrement/add) so in theory should be safe, but it isn't
listed as being volatile, so accesses to it could be cached for who
knows how long (in a local register) while the actual value is being
modified by some other thread. Others (like uvmexp.swpginuse) are
simply modified by regular C += and -= operations.
All of this looks very unsafe to me, for as much as the uvmexp values are
just FYIs, it would all be OK, and many of the values are, though just
being int (for almost all of them) many are very likely to overflow on
a busy system, and I see that the many of the values in your gdb dump of
the struct are negative which almost certainly means they overflowed).
But some of the values (like these two) aren't quite the same, they're
actually tracking the state of the swap system. But the way things are
done, I don't know that it makes sense to ever compare them, despite the
logic of their uses implying that should be possible (one would expect that
the number of swap pages allocated to pages that are only resident on the
swap device (/dev/drum) must be less than the total number of swap pages
allocated, or at least not greater, as there would often be some pages
resident in both ram and on the swap device, which should make a difference).
WRT your newer message:
gnats-admin@NetBSD.org (wiz@ really) said:
| In uvm_swap_markbad() the change is protected by
| mutex_enter(&uvm_swap_data_lock),
Not really, that lock is held, but its purpose is to protect the swapdev
lists, it prevents manipulations of those lists while held, it isn't
intended to apply to uvmexp, which as I said above, I don't think anything
is.
I think I'd just remove the KASSERT that is firing for you, and look for
some other way to try and understand the original problem from the PR.
kre
ps: once again, I really have no idea what I am typing about!
From: Thomas Klausner <wiz@netbsd.org>
To: Robert Elz <kre@munnari.oz.au>
Cc: gnats-bugs@netbsd.org
Subject: Re: kern/56764
Date: Sat, 21 Feb 2026 18:30:55 +0100
Hi!
Thanks for looking at this.
On Sun, Feb 22, 2026 at 12:10:53AM +0100, Robert Elz wrote:
> But I just took a look, and I can see no evidence at all that anything in
> uvmexp expects to take any kind of lock before modifications.
I thought this was the case because of functions like uvm_swapisfull, in full:
bool
uvm_swapisfull(void)
{
int swpgonly;
bool rv;
if (uvmexp.swpages == 0) {
return true;
}
mutex_enter(&uvm_swap_data_lock);
KASSERT(uvmexp.swpgonly <= uvmexp.swpages);
swpgonly = (int)((uint64_t)uvmexp.swpgonly * 100 /
uvm_swapisfull_factor);
rv = (swpgonly >= uvmexp.swpgavail);
mutex_exit(&uvm_swap_data_lock);
return (rv);
}
It only accesses uvmexp members and still grabs the lock.
> Some values (like uvmexp.swpgonly) are only ever modified via atomic
> ops (increment/decrement/add) so in theory should be safe, but it isn't
> listed as being volatile, so accesses to it could be cached for who
> knows how long (in a local register) while the actual value is being
> modified by some other thread. Others (like uvmexp.swpginuse) are
> simply modified by regular C += and -= operations.
If you look closely you'll find that swpginuse is only modified when
uvm_swap_data_lock is held.
That was the other reason why I thought the same should apply to
swpgonly.
> I think I'd just remove the KASSERT that is firing for you, and look for
> some other way to try and understand the original problem from the PR.
I've hijacked this PR a bit - it was about
kernel diagnostic assertion "uvmexp.swpgonly > 0" failed
while I was hitting
kernel diagnostic assertion "uvmexp.swpgonly < uvmexp.swpginuse" failed
the swpgonly > 0 definitely sounds sane while the other one should
(according to Chuck) always be true too.
Thomas
From: Thomas Klausner <wiz@netbsd.org>
To: NetBSD bugtracking <gnats-bugs@netbsd.org>
Cc:
Subject: Re: kern/56764
Date: Sat, 21 Feb 2026 18:55:17 +0100
My second diff just showed where the assumption is violated:
panic: kernel diagnostic assertion "uvmexp.swpginuse >= uvmexp.swpgonly + nslots" failed: file "/usr/src/sys/uvm/uvm_swap.c", line 1938 swpginuse 18158179 swpgonly 18158179 nslots 1
i.e. swpginuse is reduced before swpgonly is reduced.
I think I'll just remove the assertion that swpginuse > swpgonly
locally - it's just not consistently true.
Thomas
From: Robert Elz <kre@munnari.OZ.AU>
To: Thomas Klausner <wiz@netbsd.org>
Cc: gnats-bugs@netbsd.org
Subject: Re: kern/56764
Date: Sun, 22 Feb 2026 01:14:26 +0700
Date: Sat, 21 Feb 2026 18:30:55 +0100
From: Thomas Klausner <wiz@netbsd.org>
Message-ID: <aZnqBQcezBYRM5zw@exadelic.gatalith.at>
| It only accesses uvmexp members and still grabs the lock.
OK, like I said, I am guessing a lot, but if
| If you look closely you'll find that swpginuse is only modified when
| uvm_swap_data_lock is held.
that's true, then good.
| I've hijacked this PR a bit - it was about
| kernel diagnostic assertion "uvmexp.swpgonly > 0" failed
Yes. I saw.
| while I was hitting
|
| kernel diagnostic assertion "uvmexp.swpgonly < uvmexp.swpginuse" failed
But that's an assertion you just added in Jan, right?
| the swpgonly > 0 definitely sounds sane while the other one should
| (according to Chuck) always be true too.
Logically, I agree, but with new compilers and the current C rules,
I'm not sure the code can guarantee that.
Since the addr of uvmexp.swpgonly is passed to the atomic functions,
the compiler must assume that it might be being modified, which of
course it is. But that means it needs to be read from the struct
on any reference after one of those operations (there's just the one
in uvmpd_scan_queue() which is the KASSERT() in question).
But that's not true of uvmexp.swpginuse ... uvmpd_scan_queue() is just one
big loop, runs until it has nothing left to do, over and over. The compiler
might read uvmexp.swpginuse, stick in a register, and as nothing in that
func is touching it, nor passing its addr to any other funcs, and it isn't
volatile, the compiler is entitled (by modern C rules) to assume that nothing
is changing it. Maybe that doesn't apply to extern structs, beats me.
But I also see this code in uvmpd_scan_queue() (not that I expect this
is related to your issue):
if ((p->flags & PG_SWAPBACKED) == 0) {
KASSERT(uobj != NULL);
(void) (uobj->pgops->pgo_put)(uobj, p->offset,
p->offset + PAGE_SIZE, PGO_CLEANIT|PGO_FREE);
continue;
}
That's going around the big loop again - but I see no attempt to
rw_exit(slock);
slock is a local var in uvmpd_scan_queue() so whatever the function is
(uobj->pgops->pgo_put) it cannot be exiting the locked region.
Every other "continue;" in that section of code (before and after that one
- from after the slock is obtained) has that rw_exit(slock) just before the
continue, so there may well be lots of locked stuff hanging around, if
that particular piece of code ever runs. Very unlikely to be related to
your issue(s) but it looks broken to me.
kre
From: Thomas Klausner <wiz@netbsd.org>
To: Robert Elz <kre@munnari.oz.au>
Cc: gnats-bugs@netbsd.org
Subject: Re: kern/56764
Date: Sat, 21 Feb 2026 20:44:32 +0100
On Sun, Feb 22, 2026 at 01:14:26AM +0100, Robert Elz wrote:
> | while I was hitting
> |
> | kernel diagnostic assertion "uvmexp.swpgonly < uvmexp.swpginuse" failed
>
> But that's an assertion you just added in Jan, right?
No - I just added details for the values, the assertion has been there
(and hitting me) for a long time.
Thomas
From: "Thomas Klausner" <wiz@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/56764 CVS commit: src/sys/uvm
Date: Sun, 22 Mar 2026 12:00:08 +0000
Module Name: src
Committed By: wiz
Date: Sun Mar 22 12:00:08 UTC 2026
Modified Files:
src/sys/uvm: uvm_pdaemon.c
Log Message:
Disable a kassertmsg.
This triggers for me quite reliably over years now, and has been
tracked in PR 56764, with no resolution.
It seems the asserted inequality just is not correct.
To generate a diff of this commit:
cvs rdiff -u -r1.136 -r1.137 src/sys/uvm/uvm_pdaemon.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "YAMAMOTO Takashi" <yamt@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/56764 CVS commit: src/sys/uvm
Date: Mon, 23 Mar 2026 12:44:50 +0000
Module Name: src
Committed By: yamt
Date: Mon Mar 23 12:44:50 UTC 2026
Modified Files:
src/sys/uvm: uvm_pdaemon.c
Log Message:
uvmpd_scan_queue: remove ENABLE_UNRELIABLE_CHECK_PR_56764 block
while this condition is true in most of times, we can't
assert it here because these counters are not always
updated in-sync.
for example, consider a removal of a large tmpfs file which is
mostly swapped out. because uao_dropswap_range() batches swpgonly
updates, swpgonly can be temporarily larger than swpginuse.
the original symptom reported in PR/56764 ("uvmexp.swpgonly > 0")
looks like a different issue though.
https://gnats.netbsd.org/56764
To generate a diff of this commit:
cvs rdiff -u -r1.137 -r1.138 src/sys/uvm/uvm_pdaemon.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/56764 CVS commit: [netbsd-11] src/sys/uvm
Date: Fri, 3 Apr 2026 12:22:23 +0000
Module Name: src
Committed By: martin
Date: Fri Apr 3 12:22:23 UTC 2026
Modified Files:
src/sys/uvm [netbsd-11]: uvm_pdaemon.c uvm_swap.c
Log Message:
Pull up following revision(s) (requested by yamt in ticket #242):
sys/uvm/uvm_pdaemon.c: revision 1.135
sys/uvm/uvm_pdaemon.c: revision 1.137
sys/uvm/uvm_pdaemon.c: revision 1.138
sys/uvm/uvm_swap.c: revision 1.210
Add more debugging.
To help understand PR 56764 better.
Ok riastradh@
Disable a kassertmsg.
This triggers for me quite reliably over years now, and has been
tracked in PR 56764, with no resolution.
It seems the asserted inequality just is not correct.
uvmpd_scan_queue: remove ENABLE_UNRELIABLE_CHECK_PR_56764 block
while this condition is true in most of times, we can't
assert it here because these counters are not always
updated in-sync.
for example, consider a removal of a large tmpfs file which is
mostly swapped out. because uao_dropswap_range() batches swpgonly
updates, swpgonly can be temporarily larger than swpginuse.
the original symptom reported in PR/56764 ("uvmexp.swpgonly > 0")
looks like a different issue though.
To generate a diff of this commit:
cvs rdiff -u -r1.134 -r1.134.8.1 src/sys/uvm/uvm_pdaemon.c
cvs rdiff -u -r1.209 -r1.209.2.1 src/sys/uvm/uvm_swap.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
(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.