[RFC PATCH 33/45] mm: page_alloc: refuse to taint clean SPBs for atomic NORETRY callers

Rik van Riel
2026-04-30T20:22:54Z

get_page_from_freelist's atomic-allocation retry logic progressively
relaxes ALLOC_NOFRAGMENT to give atomic allocs every chance: first add
ALLOC_NOFRAG_TAINTED_OK (allow steal from tainted), then drop
ALLOC_NOFRAGMENT entirely (allow tainting clean SPBs). The intent is
that atomic allocations have no slowpath escape and need extra room to
succeed.

For callers that pass __GFP_NORETRY, this tradeoff is wrong. The
NORETRY contract is "I have a fallback; don't go to extreme lengths."
Network skb_page_frag_refill, slab high-order allocations, and similar
hot-path callers all use NORETRY exactly so the allocator can return
NULL and let the caller's own fallback (smaller frag, lower-order
slab, etc.) take over. Tainting a clean superpageblock to satisfy
such a request is a lasting cost — the SPB stays tainted for the
remainder of the workload's lifetime, blocking 1 GiB hugepage
allocation from that region — that outlives the single allocation
that triggered it.

Skip the relaxation steps for NORETRY callers and return NULL
immediately. Their fallback path absorbs the failure cleanly.

Observed on a 247 GB devvm running the page-superblock
v18 series: an atomic order-3 alloc from
swapper context (PCP refill, gfp=0x152820 = __GFP_HIGH |
__GFP_KSWAPD_RECLAIM | __GFP_NOWARN | __GFP_NORETRY | __GFP_COMP |
__GFP_HARDWALL) tainted a fresh clean SPB at boot+~90 min despite
ALLOC_NOFRAGMENT being set, because the atomic-retry path stripped
the flag. The caller had a NORETRY-fallback ready; the taint was
gratuitous.

Signed-off-by: Rik van Riel <riel@surriel.com>
Assisted-by: Claude:claude-opus-4.7 syzkaller
---
mm/page_alloc.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ff7755ef2b79..e8d6d5b47f63 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5895,9 +5895,20 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
* first: allow steal/claim from tainted SPBs only. This avoids
* tainting clean SPBs while still finding pages in tainted ones.
* Only drop NOFRAGMENT entirely if that also fails.
+ *
+ * Exception: callers that explicitly opted into failure with
+ * __GFP_NORETRY have a fallback path of their own (a smaller
+ * order, a different cache, returning NULL from a best-effort
+ * cache refill, etc.). Tainting a clean superpageblock is a
+ * lasting cost that outlives this allocation; it is not justified
+ * to absorb it just to satisfy a caller that already has a
+ * cheaper escape hatch. Return NULL and let the caller's fallback
+ * run instead.
*/
if (no_fallback && !defrag_mode &&
!(gfp_mask & __GFP_DIRECT_RECLAIM)) {
+ if (gfp_mask & __GFP_NORETRY)
+ return NULL;
if (!(alloc_flags & ALLOC_NOFRAG_TAINTED_OK)) {
alloc_flags |= ALLOC_NOFRAG_TAINTED_OK;
goto retry;
--
2.52.0