On 6/29/26 10:11 PM, Brendan Jackman wrote: > Currently the core allocator code is controlled by ALLOC_NOLOCK, but the > main entry point function is significantly different from the normal > __alloc_frozen_pages_nolock(), this is tiring when reading the code. > > Plumb the ALLOC_NOLOCK control one layer up in the call stack: create > an alloc_flags argument to __alloc_frozen_pages_nolock() (which is only > exposed to mm/) and then turn the nolock variant into a thin wrapper > that just sets that flag (as well as handling NUMA_NO_NODE, similar to > how some of the wrappers in gfp.h do). > > Rationale that this doesn't change anything: > > 1. Simple bits: A bunch of the nolock-specific handling is just moved to > the new alloc_order_allowed(), alloc_trylock_allowed() and > gfp_trylock. Right. > 2. __alloc_frozen_pages_noprof() has some extra logic that wasn't > previously in the nolock variant: > > a. Application of gfp_allowed_mask; this only affects early boot, and > only flags that affect the slowpath get changed here. gfp_allowed_mask clears __GFP_RECLAIM, and that means now allocations with GFP_KERNEL during early boot would see gfpflags_allow_spinning() = false. The helper is not used in in the page allocator, but used in memcg/stackdepot/page_owner. > b. Application of current_gfp_context() - also only affects the > slowpath PF_MEMALLOC_PIN affects the fast path, but ALLOC_NOLOCK users won't be affected. What about alloc_flags_nofragment/nonblocking()? > 3. The slowpath itself: this is now just explicitly skipped under > !ALLOC_TRYLOCK. Right. > Ulterior motive: adding an alloc_flags arg to the allocator's > mm-internal entrypoint can later be used to do more allocation > customisation without needing to create new GFP flags. > > While adding this flag to a bunch of places, create ALLOC_DEFAULT to > avoid a mysterious literal 0 in most places. > > alloc_frozen_pages_noprof() is defined above the alloc flags The function is defined below the alloc flags, no? > so just leave that as a slightly messy > exception instead of trying to fully reorder mm/internal.h for that one > case. > > No functional change intended. > > Signed-off-by: Brendan Jackman > --- > mm/hugetlb.c | 3 +- > mm/mempolicy.c | 10 ++-- > mm/page_alloc.c | 178 +++++++++++++++++++++++++++++--------------------------- > mm/page_alloc.h | 6 +- > mm/slub.c | 6 +- > 5 files changed, 108 insertions(+), 95 deletions(-) > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > index a3ba63c7f9199..8d409d075e3e9 100644 > --- a/mm/page_alloc.c > +++ b/mm/page_alloc.c > @@ -5271,24 +5271,98 @@ void free_pages_bulk(struct page **page_array, unsigned long nr_pages) > } > } > > +static inline bool alloc_trylock_allowed(void) > +{ > + /* > + * In PREEMPT_RT spin_trylock() will call raw_spin_lock() which is > + * unsafe in NMI. If spin_trylock() is called from hard IRQ the current > + * task may be waiting for one rt_spin_lock, but rt_spin_trylock() will > + * mark the task as the owner of another rt_spin_lock which will > + * confuse PI logic, so return immediately if called from hard IRQ or > + * NMI. > + * > + * Note, irqs_disabled() case is ok. This function can be called > + * from raw_spin_lock_irqsave region. > + */ > + if (IS_ENABLED(CONFIG_PREEMPT_RT) && (in_nmi() || in_hardirq())) > + return false; > + > + /* On UP, spin_trylock() always succeeds even when it is locked */ > + if (!IS_ENABLED(CONFIG_SMP) && in_nmi()) > + return false; Except for deferred_pages_enabled(), it's not specific to the page allocator. SLUB has /* * See the comment for the same check in * alloc_frozen_pages_nolock_noprof() */ ... and repeats the same thing as above. Perhaps let's factor it out into a helper rather than trying not to forget to update the other place? > + /* Bailout, since _deferred_grow_zone() needs to take a lock */ > + if (deferred_pages_enabled()) > + return false; > + > + return true; > +} -- Cheers, Harry / Hyeonggon