* Re: [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark
2026-11-10 22:44 [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark Joshua Hahn
@ 2026-11-11 17:39 ` Jose A. Perez de Azpillaga
2026-11-12 6:18 ` Gregory Price (Meta)
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Jose A. Perez de Azpillaga @ 2026-11-11 17:39 UTC (permalink / raw)
To: Joshua Hahn, Johannes Weiner, Gregory Price,
Lorenzo Stoakes (ARM)
Cc: Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Zi Yan, Andrew Morton, linux-mm, linux-kernel,
kernel-team
On 11/10/26 11:44 PM, Joshua Hahn wrote:
> Commit 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to
> NUMA restrictions") makes get_page_from_freelist() skip any node with an
> active kswapd on the first pass through the zonelist, so unrestricted
> allocations don't immediately consume the memory kswapd is freeing.
>
> But kswapd is also woken for high-order requests. With THP defrag set to
> defer, each THP fault that fails on a fragmented node wakes kswapd at
> PMD order. kswapd reclaims a batch, wakes kcompactd, and sleeps until
> the next failed fault, so it stays awake much of the time even on a node
> with plenty of free order-0 memory. This means those order-0 allocations
> get directed elsewhere.
>
> This was observed on some Meta production workloads running on 2-NUMA
> systems, where processes with a preference for node0 allocations
> continuously allocated order-0 pages on node1, despite node0 having
> hundreds of GB of free memory.
>
> Only skip a zone with an active kswapd if it is also below kswapd's
> target watermark.
>
> On a 2-NUMA VM with a 60G node0 and a 20G node1, with THP enabled=always
> and defrag=defer, I ran a process that prefers allocating on node0.
> With a fragmented node0:
> - Before the patch, 68% of pages were served from node0
> - After the patch, 100% of pages were served from node0
>
> One remaining gap is that since this uses kswapd's own threshold,
> unrestricted allocations can take what kswapd frees above the target
> watermark. The current code already lets such allocations drain from the
> high to the low watermark while kswapd sleeps.
>
> One potential solution, as hinted by Johannes in the discussion from the
> original fix [1] is to add some kind of fudge factor (for instance,
> wmark_pages + high_wmark_pages - low_wmark_pages).
>
> But in tests that I ran in a VM where the allocation demand outpaces
> kswapd, about 4% more pages landed on the busy node and kswapd reclaimed
> about 15% more, but kswapd still finished and slept. So let's hold off
> on the fudge factor until it becomes an observable issue in workloads.
>
> [1] https://lore.kernel.org/linux-mm/20251001164131.GB1597553@cmpxchg.org/
>
> Fixes: 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to NUMA restrictions")
> Suggested-by: Daniel Hodges <hodgesd@meta.com>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
LGTM:
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
--
cheers,
jose a. p-a
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark
2026-11-10 22:44 [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark Joshua Hahn
2026-11-11 17:39 ` Jose A. Perez de Azpillaga
@ 2026-11-12 6:18 ` Gregory Price (Meta)
2026-11-12 18:52 ` Joshua Hahn
2026-11-12 16:22 ` Zi Yan
` (2 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Gregory Price (Meta) @ 2026-11-12 6:18 UTC (permalink / raw)
To: Joshua Hahn
Cc: Johannes Weiner, Gregory Price, Lorenzo Stoakes (ARM),
Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Zi Yan, Andrew Morton, linux-mm, linux-kernel,
kernel-team
On 2026-11-10 14:44 -0800, Joshua Hahn wrote:
> Commit 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to
> NUMA restrictions") makes get_page_from_freelist() skip any node with an
> active kswapd on the first pass through the zonelist, so unrestricted
> allocations don't immediately consume the memory kswapd is freeing.
>
> But kswapd is also woken for high-order requests. With THP defrag set to
> defer, each THP fault that fails on a fragmented node wakes kswapd at
> PMD order. kswapd reclaims a batch, wakes kcompactd, and sleeps until
> the next failed fault, so it stays awake much of the time even on a node
> with plenty of free order-0 memory. This means those order-0 allocations
> get directed elsewhere.
>
> This was observed on some Meta production workloads running on 2-NUMA
> systems, where processes with a preference for node0 allocations
> continuously allocated order-0 pages on node1, despite node0 having
> hundreds of GB of free memory.
>
> Only skip a zone with an active kswapd if it is also below kswapd's
> target watermark.
>
I feel this commit message could have been simplified further.
Brevity is the soul of wit.
patch (details) skips any node with kswapd running - assuming that
this means memory is low, but kswapd is woken for other reasons (e.g.
order-N allocation failures).
This results in fallback allocations under X conditions, which leads
to xyz performance isues.
Only skip nodes with kswap running if the node is under the relevant
low-memory watermark.
(test details)
Otherwise this looks good to me. Looking forward to the watermark
cleanup afterwards - i agree the change here is a bit ugly, but
probably preferable for the backport.
Acked-by: Gregory Price (Meta) <gourry@gourry.net>
--
~Gregory
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark
2026-11-12 6:18 ` Gregory Price (Meta)
@ 2026-11-12 18:52 ` Joshua Hahn
0 siblings, 0 replies; 7+ messages in thread
From: Joshua Hahn @ 2026-11-12 18:52 UTC (permalink / raw)
To: Gregory Price (Meta)
Cc: Johannes Weiner, Lorenzo Stoakes (ARM), Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Zi Yan,
Andrew Morton, linux-mm, linux-kernel, kernel-team
On Thu, Nov 12, 2026 at 01:18:30AM -0500, Gregory Price (Meta) wrote:
> On 2026-11-10 14:44 -0800, Joshua Hahn wrote:
> > Commit 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to
> > NUMA restrictions") makes get_page_from_freelist() skip any node with an
> > active kswapd on the first pass through the zonelist, so unrestricted
> > allocations don't immediately consume the memory kswapd is freeing.
> >
> > But kswapd is also woken for high-order requests. With THP defrag set to
> > defer, each THP fault that fails on a fragmented node wakes kswapd at
> > PMD order. kswapd reclaims a batch, wakes kcompactd, and sleeps until
> > the next failed fault, so it stays awake much of the time even on a node
> > with plenty of free order-0 memory. This means those order-0 allocations
> > get directed elsewhere.
> >
> > This was observed on some Meta production workloads running on 2-NUMA
> > systems, where processes with a preference for node0 allocations
> > continuously allocated order-0 pages on node1, despite node0 having
> > hundreds of GB of free memory.
> >
> > Only skip a zone with an active kswapd if it is also below kswapd's
> > target watermark.
> >
>
> I feel this commit message could have been simplified further.
> Brevity is the soul of wit.
>
> patch (details) skips any node with kswapd running - assuming that
> this means memory is low, but kswapd is woken for other reasons (e.g.
> order-N allocation failures).
>
> This results in fallback allocations under X conditions, which leads
> to xyz performance isues.
>
> Only skip nodes with kswap running if the node is under the relevant
> low-memory watermark.
>
> (test details)
Thanks Gregory,
I was going to ask to have the commit message updated as a fixlet but
I think given the amount of change going in I will just send a new
version with a more concise commit message to make it less confusing.
Thanks again, have a great day!
Joshua
> Otherwise this looks good to me. Looking forward to the watermark
> cleanup afterwards - i agree the change here is a bit ugly, but
> probably preferable for the backport.
>
> Acked-by: Gregory Price (Meta) <gourry@gourry.net>
>
> --
> ~Gregory
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark
2026-11-10 22:44 [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark Joshua Hahn
2026-11-11 17:39 ` Jose A. Perez de Azpillaga
2026-11-12 6:18 ` Gregory Price (Meta)
@ 2026-11-12 16:22 ` Zi Yan
2026-11-12 16:37 ` Johannes Weiner
2026-11-12 19:55 ` Lorenzo Stoakes (ARM)
4 siblings, 0 replies; 7+ messages in thread
From: Zi Yan @ 2026-11-12 16:22 UTC (permalink / raw)
To: Joshua Hahn
Cc: Johannes Weiner, Gregory Price, Lorenzo Stoakes (ARM),
Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Andrew Morton, linux-mm, linux-kernel,
kernel-team
On 10 Nov 2026, at 17:44, Joshua Hahn wrote:
> Commit 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to
> NUMA restrictions") makes get_page_from_freelist() skip any node with an
> active kswapd on the first pass through the zonelist, so unrestricted
> allocations don't immediately consume the memory kswapd is freeing.
>
> But kswapd is also woken for high-order requests. With THP defrag set to
> defer, each THP fault that fails on a fragmented node wakes kswapd at
> PMD order. kswapd reclaims a batch, wakes kcompactd, and sleeps until
> the next failed fault, so it stays awake much of the time even on a node
> with plenty of free order-0 memory. This means those order-0 allocations
> get directed elsewhere.
>
> This was observed on some Meta production workloads running on 2-NUMA
> systems, where processes with a preference for node0 allocations
> continuously allocated order-0 pages on node1, despite node0 having
> hundreds of GB of free memory.
>
> Only skip a zone with an active kswapd if it is also below kswapd's
> target watermark.
>
> On a 2-NUMA VM with a 60G node0 and a 20G node1, with THP enabled=always
> and defrag=defer, I ran a process that prefers allocating on node0.
> With a fragmented node0:
> - Before the patch, 68% of pages were served from node0
> - After the patch, 100% of pages were served from node0
>
> One remaining gap is that since this uses kswapd's own threshold,
> unrestricted allocations can take what kswapd frees above the target
> watermark. The current code already lets such allocations drain from the
> high to the low watermark while kswapd sleeps.
>
> One potential solution, as hinted by Johannes in the discussion from the
> original fix [1] is to add some kind of fudge factor (for instance,
> wmark_pages + high_wmark_pages - low_wmark_pages).
>
> But in tests that I ran in a VM where the allocation demand outpaces
> kswapd, about 4% more pages landed on the busy node and kswapd reclaimed
> about 15% more, but kswapd still finished and slept. So let's hold off
> on the fudge factor until it becomes an observable issue in workloads.
>
> [1] https://lore.kernel.org/linux-mm/20251001164131.GB1597553@cmpxchg.org/
>
> Fixes: 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to NUMA restrictions")
> Suggested-by: Daniel Hodges <hodgesd@meta.com>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
> ---
> v1 --> v2:
> - Added a Fixes tag and separated this from the other cleanup patches
> - Check zone_watermark_fast with no flag to match the call in
> pgdat_balanced().
> - Calculates mark outside the zone_watermark_fast check to match the
> style for the other zone checks.
> - Wordsmithing commit message to explain the unaddressed gap.
> - Yes, this is ugly. I have another series to clean this code up but
> I wanted this to be a feature-minimal patch so that it could be
> easily backported. Things will be much prettier once those are landed.
>
> mm/page_alloc.c | 23 ++++++++++++++++-------
> 1 file changed, 16 insertions(+), 7 deletions(-)
>
Makes sense.
Acked-by: Zi Yan <ziy@nvidia.com>
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark
2026-11-10 22:44 [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark Joshua Hahn
` (2 preceding siblings ...)
2026-11-12 16:22 ` Zi Yan
@ 2026-11-12 16:37 ` Johannes Weiner
2026-11-12 19:55 ` Lorenzo Stoakes (ARM)
4 siblings, 0 replies; 7+ messages in thread
From: Johannes Weiner @ 2026-11-12 16:37 UTC (permalink / raw)
To: Joshua Hahn
Cc: Gregory Price, Lorenzo Stoakes (ARM), Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Zi Yan,
Andrew Morton, linux-mm, linux-kernel, kernel-team
On Tue, Nov 10, 2026 at 02:44:31PM -0800, Joshua Hahn wrote:
> Commit 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to
> NUMA restrictions") makes get_page_from_freelist() skip any node with an
> active kswapd on the first pass through the zonelist, so unrestricted
> allocations don't immediately consume the memory kswapd is freeing.
>
> But kswapd is also woken for high-order requests. With THP defrag set to
> defer, each THP fault that fails on a fragmented node wakes kswapd at
> PMD order. kswapd reclaims a batch, wakes kcompactd, and sleeps until
> the next failed fault, so it stays awake much of the time even on a node
> with plenty of free order-0 memory. This means those order-0 allocations
> get directed elsewhere.
>
> This was observed on some Meta production workloads running on 2-NUMA
> systems, where processes with a preference for node0 allocations
> continuously allocated order-0 pages on node1, despite node0 having
> hundreds of GB of free memory.
>
> Only skip a zone with an active kswapd if it is also below kswapd's
> target watermark.
>
> On a 2-NUMA VM with a 60G node0 and a 20G node1, with THP enabled=always
> and defrag=defer, I ran a process that prefers allocating on node0.
> With a fragmented node0:
> - Before the patch, 68% of pages were served from node0
> - After the patch, 100% of pages were served from node0
>
> One remaining gap is that since this uses kswapd's own threshold,
> unrestricted allocations can take what kswapd frees above the target
> watermark. The current code already lets such allocations drain from the
> high to the low watermark while kswapd sleeps.
>
> One potential solution, as hinted by Johannes in the discussion from the
> original fix [1] is to add some kind of fudge factor (for instance,
> wmark_pages + high_wmark_pages - low_wmark_pages).
>
> But in tests that I ran in a VM where the allocation demand outpaces
> kswapd, about 4% more pages landed on the busy node and kswapd reclaimed
> about 15% more, but kswapd still finished and slept. So let's hold off
> on the fudge factor until it becomes an observable issue in workloads.
>
> [1] https://lore.kernel.org/linux-mm/20251001164131.GB1597553@cmpxchg.org/
>
> Fixes: 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to NUMA restrictions")
> Suggested-by: Daniel Hodges <hodgesd@meta.com>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
This makes sense to me. I agree with Gregory on changelog brevity, but
the fix looks good.
Reviewed-by: Johannes Weiner <hannes@cmpxchg.org>
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark
2026-11-10 22:44 [PATCH v2] mm/page_alloc: only skip kswapd nodes below their kswapd watermark Joshua Hahn
` (3 preceding siblings ...)
2026-11-12 16:37 ` Johannes Weiner
@ 2026-11-12 19:55 ` Lorenzo Stoakes (ARM)
4 siblings, 0 replies; 7+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-11-12 19:55 UTC (permalink / raw)
To: Joshua Hahn
Cc: Johannes Weiner, Gregory Price, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Zi Yan,
Andrew Morton, linux-mm, linux-kernel, kernel-team
On Tue, Nov 10, 2026 at 02:44:31PM -0800, Joshua Hahn wrote:
> Commit 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to
> NUMA restrictions") makes get_page_from_freelist() skip any node with an
> active kswapd on the first pass through the zonelist, so unrestricted
> allocations don't immediately consume the memory kswapd is freeing.
>
> But kswapd is also woken for high-order requests. With THP defrag set to
> defer, each THP fault that fails on a fragmented node wakes kswapd at
> PMD order. kswapd reclaims a batch, wakes kcompactd, and sleeps until
> the next failed fault, so it stays awake much of the time even on a node
> with plenty of free order-0 memory. This means those order-0 allocations
> get directed elsewhere.
>
> This was observed on some Meta production workloads running on 2-NUMA
> systems, where processes with a preference for node0 allocations
> continuously allocated order-0 pages on node1, despite node0 having
> hundreds of GB of free memory.
>
> Only skip a zone with an active kswapd if it is also below kswapd's
> target watermark.
>
> On a 2-NUMA VM with a 60G node0 and a 20G node1, with THP enabled=always
> and defrag=defer, I ran a process that prefers allocating on node0.
> With a fragmented node0:
> - Before the patch, 68% of pages were served from node0
> - After the patch, 100% of pages were served from node0
>
> One remaining gap is that since this uses kswapd's own threshold,
> unrestricted allocations can take what kswapd frees above the target
> watermark. The current code already lets such allocations drain from the
> high to the low watermark while kswapd sleeps.
>
> One potential solution, as hinted by Johannes in the discussion from the
> original fix [1] is to add some kind of fudge factor (for instance,
> wmark_pages + high_wmark_pages - low_wmark_pages).
>
> But in tests that I ran in a VM where the allocation demand outpaces
> kswapd, about 4% more pages landed on the busy node and kswapd reclaimed
> about 15% more, but kswapd still finished and slept. So let's hold off
> on the fudge factor until it becomes an observable issue in workloads.
>
> [1] https://lore.kernel.org/linux-mm/20251001164131.GB1597553@cmpxchg.org/
>
> Fixes: 19c5fb83f2a4 ("mm: page_alloc: avoid kswapd thrashing due to NUMA restrictions")
> Suggested-by: Daniel Hodges <hodgesd@meta.com>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
LGTM and:
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> v1 --> v2:
> - Added a Fixes tag and separated this from the other cleanup patches
> - Check zone_watermark_fast with no flag to match the call in
> pgdat_balanced().
> - Calculates mark outside the zone_watermark_fast check to match the
> style for the other zone checks.
> - Wordsmithing commit message to explain the unaddressed gap.
> - Yes, this is ugly. I have another series to clean this code up but
> I wanted this to be a feature-minimal patch so that it could be
> easily backported. Things will be much prettier once those are landed.
>
> mm/page_alloc.c | 23 ++++++++++++++++-------
> 1 file changed, 16 insertions(+), 7 deletions(-)
>
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index 8532af052d74b..56121c3f9c4a7 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -45,6 +45,7 @@
> #include <linux/mmu_notifier.h>
> #include <linux/migrate.h>
> #include <linux/sched/mm.h>
> +#include <linux/sched/sysctl.h>
> #include <linux/page_owner.h>
> #include <linux/page_table_check.h>
> #include <linux/memcontrol.h>
> @@ -3799,6 +3800,9 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
> struct zoneref *z;
> struct zone *zone;
> struct pglist_data *last_pgdat = NULL;
> + enum zone_watermarks kswapd_watermark =
> + sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING ?
> + WMARK_PROMO : WMARK_HIGH;
> bool last_pgdat_dirty_ok = false;
> bool no_fallback;
> bool skip_kswapd_nodes = nr_online_nodes > 1;
> @@ -3866,16 +3870,21 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
> }
>
> /*
> - * If kswapd is already active on a node, keep looking
> - * for other nodes that might be idle. This can happen
> - * if another process has NUMA bindings and is causing
> - * kswapd wakeups on only some nodes. Avoid accidental
> - * "node_reclaim_mode"-like behavior in this case.
> + * If kswapd is already active on a node and the zone is below
> + * its kswapd watermark for this request, keep looking for
> + * other nodes that might be idle. This can happen if another
> + * process has NUMA bindings and is causing kswapd wakeups on
> + * only some nodes. Avoid accidental "node_reclaim_mode"-like
> + * behavior in this case.
> */
> if (skip_kswapd_nodes &&
> !waitqueue_active(&zone->zone_pgdat->kswapd_wait)) {
> - skipped_kswapd_nodes = true;
> - continue;
> + mark = wmark_pages(zone, kswapd_watermark);
> + if (!zone_watermark_fast(zone, order, mark,
> + ac->highest_zoneidx, 0, gfp_mask)) {
> + skipped_kswapd_nodes = true;
> + continue;
> + }
Much better thanks!
> }
>
> cond_accept_memory(zone, order, alloc_flags);
> --
> 2.53.0-Meta
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 7+ messages in thread