From: Nick Owens Subject: uvm: gracefully handle transient pmap_enter failure with no swap To: tech@openbsd.org Date: Tue, 14 Jul 2026 17:25:03 -0700 hi, since the pmap_vp_pool structures on arm64/riscv64 are 8K, the allocations have to go through trylock on the kernel map. during fault handling, pmap pool allocations may fail due to kernel map lock contention with PR_NOWAIT set. on a machine with no swap, uvm_swapisfull() will always be true and then the fault handler will return ENOMEM, leading the trap handler to simply SIGKILL the process, even if there are free pages. this changes the kill condition to check uvmexp.free to see if pages are actually scarce, so that we can fall into the recovery path instead of slaying processes. i've run into this issue running -current inside KVM VMs on both arm64 and riscv64, and one other person mentioned they had this happen on physical arm64 hardware (radxa zero 3e). this is most visible during boot time, where there will be 'Killed' messages interleaved with normal rc output, and daemons will be dead, library_aslr incomplete, etc. this can be reproduced by installing 7.9 or a snapshot on a VM on arm64 or riscv64 with a single / partition and no swap, and 2 vcpus. is this the right approach, or would it be better to make the pmap vp pool allocations fit in single pages to avoid hitting the kernel map lock, instead of or in addition to this? diff --git a/sys/uvm/uvm_fault.c b/sys/uvm/uvm_fault.c index 866b378199e..4486b170791 100644 --- a/sys/uvm/uvm_fault.c +++ b/sys/uvm/uvm_fault.c @@ -1139,7 +1139,13 @@ retry: * as the map may change while we're asleep. */ uvmfault_unlockall(ufi, amap, NULL); - if (uvm_swapisfull()) { + + /* + * uvm_swapisfull() will always be true on swapless systems, so + * also check if pages are genuinely scarce before giving up + */ + if (uvm_swapisfull() && + atomic_load_sint(&uvmexp.free) < uvmexp.freemin) { /* XXX instrumentation */ return ENOMEM; } @@ -1517,7 +1523,13 @@ uvm_fault_lower(struct uvm_faultinfo *ufi, struct uvm_faultctx *flt, atomic_clearbits_int(&pg->pg_flags, PG_BUSY|PG_FAKE|PG_WANTED); UVM_PAGE_OWN(pg, NULL); uvmfault_unlockall(ufi, amap, uobj); - if (uvm_swapisfull()) { + + /* + * uvm_swapisfull() will always be true on swapless systems, so + * also check if pages are genuinely scarce before giving up + */ + if (uvm_swapisfull() && + atomic_load_sint(&uvmexp.free) < uvmexp.freemin) { /* XXX instrumentation */ return (ENOMEM); }