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 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