Download raw body.
uvm: gracefully handle transient pmap_enter failure with no swap
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);
}
uvm: gracefully handle transient pmap_enter failure with no swap