1
0
mirror of https://github.com/openbsd/src.git synced 2026-04-15 09:44:36 +00:00

When the pagedaemon is triggered to create free memory, there may be

sleeping pmemrange allocations with multi-page alignment requirements
which can't be satisfied by the simplistic freeing of (solo) pages
which the pagedaemon performs.  As we near starvation, fragmentation
is the main problem.  Our free list could be large enough that the
pagedaemon sees no reason to do more work, but also too fragmented to
satisfy a pending allocation request with complex requirements
(imagine asking for 512K of physically linear memory which is DMA
reachable).  When the requirement isn't satisfied, the pagedaemon is
told to try again, but again doesn't mean harder because it has no
mechanism to try harder.  It's tracking variables do not show the
fragmentation problem.  It spins a lot.  Often this becomes a
deadlock.
Time to change strategy: Overshoot creation of (both) inactive and
free pages each time through the loop. After inspecting existing
variables, we generate minumum 128 inactive pages (which may be
dynamically drawn down asyncronously by accesses), and then try to
convert minumum 128 inactives into free pages (different pages
get freed different ways, including via swapcluster which has been
improved in previous uvm_swap.c commit to absorb more pressure and
indicate when it is full).
As we mow through the freelist, this will eventually create some
(physical address space) defragmention and satisfy these complex
requirements.  Maybe not on the first round, but it will keep trying.
Before this change, it was not trying at all.
ok kettenis kirill beck
This commit is contained in:
deraadt
2026-04-11 01:57:22 +00:00
parent 9bac450dd5
commit f7e671ebc3

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: uvm_pdaemon.c,v 1.155 2026/04/11 01:36:23 deraadt Exp $ */
/* $OpenBSD: uvm_pdaemon.c,v 1.156 2026/04/11 01:57:22 deraadt Exp $ */
/* $NetBSD: uvm_pdaemon.c,v 1.23 2000/08/20 10:24:14 bjh21 Exp $ */
/*
@@ -202,6 +202,14 @@ struct uvm_pmalloc nowait_pma;
/*
* uvm_pageout: the main loop for the pagedaemon
*
* Sleeping pmemrange allocations may have multi-page alignment
* requirements which can't be satisfied by the simplistic freeing of
* pages. Our free list could be large enough that we don't need to
* free more, but too fragmented to satisfy a pending allocation. So
* we overshoot creation of inactive and free pages each time through
* the loop, which will eventually create some defragmention and
* satisfy the complex requirement.
*/
void
uvm_pageout(void *arg)
@@ -240,6 +248,8 @@ uvm_pageout(void *arg)
} else {
constraint = no_constraint;
}
size = MAX(size, 128);
/* How many pages do we need to free during this round? */
shortage = uvmexp.freetarg - atomic_load_sint(&uvmexp.free) +
BUFPAGES_DEFICIT;
@@ -263,8 +273,6 @@ uvm_pageout(void *arg)
/* Reclaim pages from the buffer cache if possible. */
if (shortage > 0)
size += shortage;
if (size == 0)
size = 16; /* XXX */
shortage -= bufbackoff(&constraint, size * 2);
#if NDRM > 0
@@ -274,16 +282,11 @@ uvm_pageout(void *arg)
if (shortage > 0)
shortage -= uvm_pmr_cache_drain();
/* XXX remove shortage as parameter below */
if (shortage < 0)
shortage = 0;
inactive_shortage = MAX(inactive_shortage, size * 2);
shortage = MAX(shortage, size);
/*
* scan if needed
*/
uvm_lock_pageq();
if (pma || shortage > 0 || inactive_shortage > 0)
uvmpd_scan(&constraint, shortage, inactive_shortage);
uvmpd_scan(&constraint, shortage, inactive_shortage);
/*
* if there's any free memory to be had,