[RFC PATCH 36/40] mm: page_alloc: set ALLOC_NOFRAGMENT on alloc_frozen_pages_nolock_noprof
From: Rik van Riel
Date: Wed May 20 2026 - 11:21:01 EST
The no-lock atomic page allocator (alloc_frozen_pages_nolock) bypasses
__alloc_pages_slowpath() entirely and calls get_page_from_freelist()
directly. The slowpath is where ALLOC_NOFRAGMENT is normally set for
non-movable allocations, so the nolock path runs without the "skip
clean SPBs" guard.
Add ALLOC_NOFRAGMENT to the alloc_flags. The function is best-effort
and callers already handle NULL. The flag steers the alloc toward
tainted-SPB sub-pageblock space first; for order > 0 the existing
auto-refuse in the get_page_from_freelist relax sequence returns NULL
when the tainted pool can serve a smaller order, and for order = 0
the alloc falls back to claiming a clean SPB only if the tainted
pool is exhausted of suitable fragments.
ALLOC_NOFRAGMENT is purely a pageblock-category steering hint; it
does not depend on __GFP_DIRECT_RECLAIM and does not introduce any
sleeping locks, so it composes cleanly with ALLOC_TRYLOCK in NMI,
hardirq, and PREEMPT_RT contexts.
Signed-off-by: Rik van Riel <riel@xxxxxxxxxxx>
Assisted-by: Claude:claude-opus-4.7 syzkaller
---
mm/page_alloc.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 2791a52b61da..4e45fac14622 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -11525,7 +11525,23 @@ struct page *alloc_frozen_pages_nolock_noprof(gfp_t gfp_flags, int nid, unsigned
*/
gfp_t alloc_gfp = __GFP_NOWARN | __GFP_ZERO | __GFP_NOMEMALLOC | __GFP_COMP
| gfp_flags;
- unsigned int alloc_flags = ALLOC_TRYLOCK;
+ /*
+ * ALLOC_NOFRAGMENT steers this best-effort no-lock allocation
+ * toward tainted-SPB sub-pageblock space before fragmenting any
+ * clean superpageblock. Without it, atomic kmalloc_nolock callers
+ * (e.g. BPF storage from sched_process_exec tracepoints) hit the
+ * fallback path on every PCP miss and convert a movable pageblock
+ * in a clean SPB to UNMOVABLE -- tainting a 1 GiB hugepage
+ * candidate for an order-0 atomic alloc. With NOFRAGMENT set:
+ * - order > 0: the get_page_from_freelist relax sequence
+ * auto-refuses to drop NOFRAGMENT when the tainted pool can
+ * serve a smaller order, returning NULL to the caller.
+ * - order = 0: prefers tainted-SPB fragments first; only falls
+ * back to claiming a clean-SPB pageblock if the tainted pool
+ * is exhausted of suitable fragments.
+ * Callers of alloc_pages_nolock() already handle NULL.
+ */
+ unsigned int alloc_flags = ALLOC_TRYLOCK | ALLOC_NOFRAGMENT;
struct alloc_context ac = { };
struct page *page;
--
2.54.0