[PATCH] mm/page_alloc: fix defrag_mode for non-reclaimable allocations

Dmitry Ilvokhin posted 1 patch 6 days, 11 hours ago
There is a newer version of this series
mm/page_alloc.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
[PATCH] mm/page_alloc: fix defrag_mode for non-reclaimable allocations
Posted by Dmitry Ilvokhin 6 days, 11 hours ago
When defrag_mode is enabled, ALLOC_NOFRAGMENT is enforced to prevent
migratetype fallbacks and keep pageblocks clean. The allocator relies on
reclaim and compaction to free pages of the correct type before allowing
fallback as a last resort.

However, non-reclaimable allocations such as GFP_ATOMIC cannot invoke
direct reclaim or compaction. With defrag_mode=1, these allocations hit
the !can_direct_reclaim bailout in __alloc_pages_slowpath() with
ALLOC_NOFRAGMENT still set, and fail without ever attempting a fallback.

This causes a large number of SLUB allocation failures for
skbuff_head_cache under network-heavy workloads, despite free memory
being available in other migratetype freelists.

Clear ALLOC_NOFRAGMENT and retry before giving up on allocations that
cannot reclaim, following the same pattern used after reclaim/compaction
exhaustion later in the slowpath.

Fixes: e3aa7df331bc ("mm: page_alloc: defrag_mode")

Signed-off-by: Dmitry Ilvokhin <d@ilvokhin.com>
---
 mm/page_alloc.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 227d58dc3de6..749422b8396f 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4811,8 +4811,18 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
 	}
 
 	/* Caller is not willing to reclaim, we can't balance anything */
-	if (!can_direct_reclaim)
+	if (!can_direct_reclaim) {
+		/*
+		 * Reclaim/compaction cannot run, so defrag_mode's strategy
+		 * of enforcing ALLOC_NOFRAGMENT cannot be fulfilled. Allow
+		 * fallbacks rather than failing the allocation outright.
+		 */
+		if (defrag_mode && (alloc_flags & ALLOC_NOFRAGMENT)) {
+			alloc_flags &= ~ALLOC_NOFRAGMENT;
+			goto retry;
+		}
 		goto nopage;
+	}
 
 	/* Avoid recursion of direct reclaim */
 	if (current->flags & PF_MEMALLOC)
-- 
2.53.0-Meta
Re: [PATCH] mm/page_alloc: fix defrag_mode for non-reclaimable allocations
Posted by Andrew Morton 6 days, 8 hours ago
On Mon, 18 May 2026 16:37:36 +0000 Dmitry Ilvokhin <d@ilvokhin.com> wrote:

> When defrag_mode is enabled, ALLOC_NOFRAGMENT is enforced to prevent
> migratetype fallbacks and keep pageblocks clean. The allocator relies on
> reclaim and compaction to free pages of the correct type before allowing
> fallback as a last resort.
> 
> However, non-reclaimable allocations such as GFP_ATOMIC cannot invoke
> direct reclaim or compaction. With defrag_mode=1, these allocations hit
> the !can_direct_reclaim bailout in __alloc_pages_slowpath() with
> ALLOC_NOFRAGMENT still set, and fail without ever attempting a fallback.
> 
> This causes a large number of SLUB allocation failures for
> skbuff_head_cache under network-heavy workloads, despite free memory
> being available in other migratetype freelists.
> 
> Clear ALLOC_NOFRAGMENT and retry before giving up on allocations that
> cannot reclaim, following the same pattern used after reclaim/compaction
> exhaustion later in the slowpath.

Thanks.  Sashiko asked a couple of things:

	https://sashiko.dev/#/patchset/20260518163736.173910-1-d@ilvokhin.com

I'm not sure what to make of the first one - we aren't holding any locks
in there which prevent concurrent cpuset or zonelist alterations
anyway (?).

But your change might violate the later comment `No "goto retry;" can be
placed above this check * unless it can execute just once'?
Re: [PATCH] mm/page_alloc: fix defrag_mode for non-reclaimable allocations
Posted by Dmitry Ilvokhin 5 days, 14 hours ago
On Mon, May 18, 2026 at 01:24:22PM -0700, Andrew Morton wrote:
> On Mon, 18 May 2026 16:37:36 +0000 Dmitry Ilvokhin <d@ilvokhin.com> wrote:
> 
> > When defrag_mode is enabled, ALLOC_NOFRAGMENT is enforced to prevent
> > migratetype fallbacks and keep pageblocks clean. The allocator relies on
> > reclaim and compaction to free pages of the correct type before allowing
> > fallback as a last resort.
> > 
> > However, non-reclaimable allocations such as GFP_ATOMIC cannot invoke
> > direct reclaim or compaction. With defrag_mode=1, these allocations hit
> > the !can_direct_reclaim bailout in __alloc_pages_slowpath() with
> > ALLOC_NOFRAGMENT still set, and fail without ever attempting a fallback.
> > 
> > This causes a large number of SLUB allocation failures for
> > skbuff_head_cache under network-heavy workloads, despite free memory
> > being available in other migratetype freelists.
> > 
> > Clear ALLOC_NOFRAGMENT and retry before giving up on allocations that
> > cannot reclaim, following the same pattern used after reclaim/compaction
> > exhaustion later in the slowpath.
> 
> Thanks.  Sashiko asked a couple of things:
> 
> 	https://sashiko.dev/#/patchset/20260518163736.173910-1-d@ilvokhin.com
> 
> I'm not sure what to make of the first one - we aren't holding any locks
> in there which prevent concurrent cpuset or zonelist alterations
> anyway (?).
> 
> But your change might violate the later comment `No "goto retry;" can be
> placed above this check * unless it can execute just once'?

Thanks for taking a look, Andrew.

Goto retry can execute at most once, since ALLOC_NOFRAGMENT is cleared
before the jump, so on the next iteration the condition is false and we
fall through to goto nopage. This is the similar to the existing
can_retry_reserves path.

Just for the sake of keeping everything in one place. Another point
Sashiko raised.

"Will allocations hitting this PF_MEMALLOC check, or the __GFP_NORETRY check
further down in the function, still fail prematurely under defrag_mode=1?
Because these terminal error paths also jump directly to the nopage label,
they skip the normal ALLOC_NOFRAGMENT clearing at the bottom of the slowpath.
Should we also clear ALLOC_NOFRAGMENT and retry for these paths so they are
allowed to fall back rather than failing outright?"

I think by the time we reach the PF_MEMALLOC check, ALLOC_NOFRAGMENT has
already been cleared, since we set only ALLOC_NO_WATERMARKS and
ALLOC_KSWAPD in reserve_flags, when PF_MEMALLOC is set.

For GFP_NORETRY, we can do direct reclaim (compared to GFP_ATOMIC case),
so we either succeed or not, we don't need another round.
Re: [PATCH] mm/page_alloc: fix defrag_mode for non-reclaimable allocations
Posted by Johannes Weiner 5 days, 13 hours ago
On Tue, May 19, 2026 at 01:47:52PM +0000, Dmitry Ilvokhin wrote:
> On Mon, May 18, 2026 at 01:24:22PM -0700, Andrew Morton wrote:
> > On Mon, 18 May 2026 16:37:36 +0000 Dmitry Ilvokhin <d@ilvokhin.com> wrote:
> > 
> > > When defrag_mode is enabled, ALLOC_NOFRAGMENT is enforced to prevent
> > > migratetype fallbacks and keep pageblocks clean. The allocator relies on
> > > reclaim and compaction to free pages of the correct type before allowing
> > > fallback as a last resort.
> > > 
> > > However, non-reclaimable allocations such as GFP_ATOMIC cannot invoke
> > > direct reclaim or compaction. With defrag_mode=1, these allocations hit
> > > the !can_direct_reclaim bailout in __alloc_pages_slowpath() with
> > > ALLOC_NOFRAGMENT still set, and fail without ever attempting a fallback.
> > > 
> > > This causes a large number of SLUB allocation failures for
> > > skbuff_head_cache under network-heavy workloads, despite free memory
> > > being available in other migratetype freelists.
> > > 
> > > Clear ALLOC_NOFRAGMENT and retry before giving up on allocations that
> > > cannot reclaim, following the same pattern used after reclaim/compaction
> > > exhaustion later in the slowpath.
> > 
> > Thanks.  Sashiko asked a couple of things:
> > 
> > 	https://sashiko.dev/#/patchset/20260518163736.173910-1-d@ilvokhin.com
> > 
> > I'm not sure what to make of the first one - we aren't holding any locks
> > in there which prevent concurrent cpuset or zonelist alterations
> > anyway (?).
> > 
> > But your change might violate the later comment `No "goto retry;" can be
> > placed above this check * unless it can execute just once'?
> 
> Thanks for taking a look, Andrew.
> 
> Goto retry can execute at most once, since ALLOC_NOFRAGMENT is cleared
> before the jump, so on the next iteration the condition is false and we
> fall through to goto nopage. This is the similar to the existing
> can_retry_reserves path.

Yes, it's just a one-off retry with relaxed fragmentation rules, no
need to re-evaluate the cpuset. So this looks fine to me.

> Just for the sake of keeping everything in one place. Another point
> Sashiko raised.
> 
> "Will allocations hitting this PF_MEMALLOC check, or the __GFP_NORETRY check
> further down in the function, still fail prematurely under defrag_mode=1?
> Because these terminal error paths also jump directly to the nopage label,
> they skip the normal ALLOC_NOFRAGMENT clearing at the bottom of the slowpath.
> Should we also clear ALLOC_NOFRAGMENT and retry for these paths so they are
> allowed to fall back rather than failing outright?"
> 
> I think by the time we reach the PF_MEMALLOC check, ALLOC_NOFRAGMENT has
> already been cleared, since we set only ALLOC_NO_WATERMARKS and
> ALLOC_KSWAPD in reserve_flags, when PF_MEMALLOC is set.

Yes, that's correct. alloc_flags gets overwritten, losing NOFRAGMENT,
for privileged requests. And then we retry with that already.

> For GFP_NORETRY, we can do direct reclaim (compared to GFP_ATOMIC case),
> so we either succeed or not, we don't need another round.

This is an interesting question.

GFP_NORETRY can reclaim and compact once, yes, but ALLOC_NOFRAGMENT is
still a higher bar, increasing the likelihood of failure.

However, unlike GFP_ATOMIC, GFP_NORETRY are usually speculative
allocations with reasonable fallback options (like slub's optimistic
higher order requests).

The idea behind defrag_mode is to not fragment until the alternative
is OOM. For GFP_ATOMIC, failing is an OOM-like event. For the other
nopage cases, it's more about "my favorite thing isn't available".

So I'd say let's fix GFP_ATOMIC and leave the other cases alone unless
somebody specifically brings it up as an issue.

However, there is one catch: GFP_ATOMIC is not its own flag. You're
gating on can_direct_reclaim which is also true for optimistic things
like mTHP allocations (GFP_TRANSHUGE_LIGHT e.g.). We don't want to
fragment for those, either.

So I think you'd want to check at least if __GFP_KSWAPD_RECLAIM is set
(which it is for GFP_ATOMIC) to decide between fragmenting and
failing. If the caller doesn't even set that, it's a good indicator
that they're purely speculative, and failing is the better option.

With that,

Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Re: [PATCH] mm/page_alloc: fix defrag_mode for non-reclaimable allocations
Posted by Dmitry Ilvokhin 4 days, 16 hours ago
On Tue, May 19, 2026 at 11:28:39AM -0400, Johannes Weiner wrote:
> On Tue, May 19, 2026 at 01:47:52PM +0000, Dmitry Ilvokhin wrote:
> > On Mon, May 18, 2026 at 01:24:22PM -0700, Andrew Morton wrote:
> > > On Mon, 18 May 2026 16:37:36 +0000 Dmitry Ilvokhin <d@ilvokhin.com> wrote:
> > > 
> > > > When defrag_mode is enabled, ALLOC_NOFRAGMENT is enforced to prevent
> > > > migratetype fallbacks and keep pageblocks clean. The allocator relies on
> > > > reclaim and compaction to free pages of the correct type before allowing
> > > > fallback as a last resort.
> > > > 
> > > > However, non-reclaimable allocations such as GFP_ATOMIC cannot invoke
> > > > direct reclaim or compaction. With defrag_mode=1, these allocations hit
> > > > the !can_direct_reclaim bailout in __alloc_pages_slowpath() with
> > > > ALLOC_NOFRAGMENT still set, and fail without ever attempting a fallback.
> > > > 
> > > > This causes a large number of SLUB allocation failures for
> > > > skbuff_head_cache under network-heavy workloads, despite free memory
> > > > being available in other migratetype freelists.
> > > > 
> > > > Clear ALLOC_NOFRAGMENT and retry before giving up on allocations that
> > > > cannot reclaim, following the same pattern used after reclaim/compaction
> > > > exhaustion later in the slowpath.
> > > 
> > > Thanks.  Sashiko asked a couple of things:
> > > 
> > > 	https://sashiko.dev/#/patchset/20260518163736.173910-1-d@ilvokhin.com
> > > 
> > > I'm not sure what to make of the first one - we aren't holding any locks
> > > in there which prevent concurrent cpuset or zonelist alterations
> > > anyway (?).
> > > 
> > > But your change might violate the later comment `No "goto retry;" can be
> > > placed above this check * unless it can execute just once'?
> > 
> > Thanks for taking a look, Andrew.
> > 
> > Goto retry can execute at most once, since ALLOC_NOFRAGMENT is cleared
> > before the jump, so on the next iteration the condition is false and we
> > fall through to goto nopage. This is the similar to the existing
> > can_retry_reserves path.
> 
> Yes, it's just a one-off retry with relaxed fragmentation rules, no
> need to re-evaluate the cpuset. So this looks fine to me.
> 
> > Just for the sake of keeping everything in one place. Another point
> > Sashiko raised.
> > 
> > "Will allocations hitting this PF_MEMALLOC check, or the __GFP_NORETRY check
> > further down in the function, still fail prematurely under defrag_mode=1?
> > Because these terminal error paths also jump directly to the nopage label,
> > they skip the normal ALLOC_NOFRAGMENT clearing at the bottom of the slowpath.
> > Should we also clear ALLOC_NOFRAGMENT and retry for these paths so they are
> > allowed to fall back rather than failing outright?"
> > 
> > I think by the time we reach the PF_MEMALLOC check, ALLOC_NOFRAGMENT has
> > already been cleared, since we set only ALLOC_NO_WATERMARKS and
> > ALLOC_KSWAPD in reserve_flags, when PF_MEMALLOC is set.
> 
> Yes, that's correct. alloc_flags gets overwritten, losing NOFRAGMENT,
> for privileged requests. And then we retry with that already.
> 
> > For GFP_NORETRY, we can do direct reclaim (compared to GFP_ATOMIC case),
> > so we either succeed or not, we don't need another round.
> 
> This is an interesting question.
> 
> GFP_NORETRY can reclaim and compact once, yes, but ALLOC_NOFRAGMENT is
> still a higher bar, increasing the likelihood of failure.
> 
> However, unlike GFP_ATOMIC, GFP_NORETRY are usually speculative
> allocations with reasonable fallback options (like slub's optimistic
> higher order requests).
> 
> The idea behind defrag_mode is to not fragment until the alternative
> is OOM. For GFP_ATOMIC, failing is an OOM-like event. For the other
> nopage cases, it's more about "my favorite thing isn't available".
> 
> So I'd say let's fix GFP_ATOMIC and leave the other cases alone unless
> somebody specifically brings it up as an issue.
> 
> However, there is one catch: GFP_ATOMIC is not its own flag. You're
> gating on can_direct_reclaim which is also true for optimistic things
> like mTHP allocations (GFP_TRANSHUGE_LIGHT e.g.). We don't want to
> fragment for those, either.
> 
> So I think you'd want to check at least if __GFP_KSWAPD_RECLAIM is set
> (which it is for GFP_ATOMIC) to decide between fragmenting and
> failing. If the caller doesn't even set that, it's a good indicator
> that they're purely speculative, and failing is the better option.

Thanks for taking a look, Johannes.

Makes sense to me. I'll add a check for __GFP_KSWAPD_RECLAIM.

> 
> With that,
> 
> Acked-by: Johannes Weiner <hannes@cmpxchg.org>