Download raw body.
uvm: gracefully handle transient pmap_enter failure with no swap
uvm: gracefully handle transient pmap_enter failure with no swap
On Tue, Jul 14, 2026 at 5:25 PM Nick Owens <mischief@offblast.org> wrote:
>
> 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);
> }
ping
uvm: gracefully handle transient pmap_enter failure with no swap
uvm: gracefully handle transient pmap_enter failure with no swap