Re: uvm: gracefully handle transient pmap_enter failure with no swap

Nick Owens <[email protected]> Fri, 24 Jul 2026 00:50:34 -0700
Newsgroups gmane.os.openbsd.tech
Message-ID <CAH_zEu4ktLczboYagCMQDPrJB+0KFkcyXt35EeeFoRXA382wcw@mail.gmail.com>
On Tue, Jul 14, 2026 at 5:25 PM Nick Owens <[email protected]> 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