* [PATCH 1/3] mm/page_alloc: drop unused gfp_mask from zone_watermark_fast()
2026-11-13 23:52 [PATCH 0/3] mm/page_alloc, vmscan: get_page_from_freelist cleanups Joshua Hahn
@ 2026-11-13 23:52 ` Joshua Hahn
2026-11-14 1:17 ` SJ Park
2026-11-13 23:52 ` [PATCH 2/3] mm/page_alloc: move the kswapd node skip check into a helper Joshua Hahn
2026-11-13 23:52 ` [PATCH 3/3] mm/vmscan: consolidate promo vs. high watermark checks Joshua Hahn
2 siblings, 1 reply; 7+ messages in thread
From: Joshua Hahn @ 2026-11-13 23:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R. Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, Qi Zheng, Shakeel Butt, linux-mm,
linux-kernel, kernel-team
zone_watermark_fast() has not used its gfp_mask argument since commit
2973d8229b78 ("mm: discard __GFP_ATOMIC") switched the watermark boost
check from __GFP_ATOMIC to ALLOC_MIN_RESERVE.
Remove the argument, no functional changes intended.
Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
---
mm/page_alloc.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 9e1261521b6a3..4d829e5eac49d 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -3675,7 +3675,7 @@ bool zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark,
static inline bool zone_watermark_fast(struct zone *z, unsigned int order,
unsigned long mark, int highest_zoneidx,
- unsigned int alloc_flags, gfp_t gfp_mask)
+ unsigned int alloc_flags)
{
long free_pages;
@@ -3877,7 +3877,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
!waitqueue_active(&zone->zone_pgdat->kswapd_wait)) {
mark = wmark_pages(zone, kswapd_watermark);
if (!zone_watermark_fast(zone, order, mark,
- ac->highest_zoneidx, 0, gfp_mask)) {
+ ac->highest_zoneidx, 0)) {
skipped_kswapd_nodes = true;
continue;
}
@@ -3897,8 +3897,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
mark = high_wmark_pages(zone);
if (zone_watermark_fast(zone, order, mark,
- ac->highest_zoneidx, alloc_flags,
- gfp_mask))
+ ac->highest_zoneidx, alloc_flags))
goto try_this_zone;
else
set_bit(ZONE_BELOW_HIGH, &zone->flags);
@@ -3906,8 +3905,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
check_alloc_wmark:
mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK);
if (!zone_watermark_fast(zone, order, mark,
- ac->highest_zoneidx, alloc_flags,
- gfp_mask)) {
+ ac->highest_zoneidx, alloc_flags)) {
if (cond_accept_memory(zone, order, alloc_flags))
goto try_this_zone;
@@ -5245,7 +5243,7 @@ unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid,
mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK) + nr_pages - nr_populated;
if (zone_watermark_fast(zone, 0, mark,
zonelist_zone_idx(ac.preferred_zoneref),
- alloc_flags, gfp)) {
+ alloc_flags)) {
break;
}
--
2.53.1-Meta
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 1/3] mm/page_alloc: drop unused gfp_mask from zone_watermark_fast()
2026-11-13 23:52 ` [PATCH 1/3] mm/page_alloc: drop unused gfp_mask from zone_watermark_fast() Joshua Hahn
@ 2026-11-14 1:17 ` SJ Park
0 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-11-14 1:17 UTC (permalink / raw)
To: Joshua Hahn
Cc: SJ Park, Andrew Morton, Vlastimil Babka, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, Qi Zheng, Shakeel Butt, linux-mm,
linux-kernel, kernel-team
On Fri, 13 Nov 2026 15:52:34 -0800 Joshua Hahn <joshua.hahnjy@gmail.com> wrote:
> zone_watermark_fast() has not used its gfp_mask argument since commit
> 2973d8229b78 ("mm: discard __GFP_ATOMIC") switched the watermark boost
> check from __GFP_ATOMIC to ALLOC_MIN_RESERVE.
>
> Remove the argument, no functional changes intended.
Looks clean to me.
>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Reviewed-by: SJ Park <sj@kernel.org>
Thanks,
SJ
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/3] mm/page_alloc: move the kswapd node skip check into a helper
2026-11-13 23:52 [PATCH 0/3] mm/page_alloc, vmscan: get_page_from_freelist cleanups Joshua Hahn
2026-11-13 23:52 ` [PATCH 1/3] mm/page_alloc: drop unused gfp_mask from zone_watermark_fast() Joshua Hahn
@ 2026-11-13 23:52 ` Joshua Hahn
2026-11-14 1:45 ` SJ Park
2026-11-13 23:52 ` [PATCH 3/3] mm/vmscan: consolidate promo vs. high watermark checks Joshua Hahn
2 siblings, 1 reply; 7+ messages in thread
From: Joshua Hahn @ 2026-11-13 23:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R. Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, Qi Zheng, Shakeel Butt, linux-mm,
linux-kernel, kernel-team
get_page_from_freelist() checks many conditions for each zone, often
with a big comment block explaining the conditions.
Move the kswapd check and its comment into should_skip_kswapd_zone() to
make the zone iteration cleaner to read.
While moving this, also resolve a checkpatch warning which complains of
a waitqueue_active() call with no corresponding comment explaining why
the lockless use is OK in this case.
No functional change intended.
Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
---
mm/page_alloc.c | 44 ++++++++++++++++++++++++++------------------
1 file changed, 26 insertions(+), 18 deletions(-)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 4d829e5eac49d..2fe49f3c0de13 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -3785,6 +3785,29 @@ static inline unsigned int alloc_flags_cma(gfp_t gfp_mask)
return ALLOC_DEFAULT;
}
+/*
+ * 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.
+ */
+static bool should_skip_kswapd_zone(struct zone *zone, unsigned int order,
+ const struct alloc_context *ac)
+{
+ unsigned long mark;
+
+ /* Lockless check, a stale result only affects zone selection */
+ if (waitqueue_active(&zone->zone_pgdat->kswapd_wait))
+ return false;
+
+ if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING)
+ mark = promo_wmark_pages(zone);
+ else
+ mark = high_wmark_pages(zone);
+ return !zone_watermark_fast(zone, order, mark, ac->highest_zoneidx, 0);
+}
+
/*
* get_page_from_freelist goes through the zonelist trying to allocate
* a page.
@@ -3796,9 +3819,6 @@ 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;
@@ -3865,22 +3885,10 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
}
}
- /*
- * 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)) {
- mark = wmark_pages(zone, kswapd_watermark);
- if (!zone_watermark_fast(zone, order, mark,
- ac->highest_zoneidx, 0)) {
- skipped_kswapd_nodes = true;
- continue;
- }
+ should_skip_kswapd_zone(zone, order, ac)) {
+ skipped_kswapd_nodes = true;
+ continue;
}
cond_accept_memory(zone, order, alloc_flags);
--
2.53.1-Meta
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 2/3] mm/page_alloc: move the kswapd node skip check into a helper
2026-11-13 23:52 ` [PATCH 2/3] mm/page_alloc: move the kswapd node skip check into a helper Joshua Hahn
@ 2026-11-14 1:45 ` SJ Park
0 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-11-14 1:45 UTC (permalink / raw)
To: Joshua Hahn
Cc: SJ Park, Andrew Morton, Vlastimil Babka, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, Qi Zheng, Shakeel Butt, linux-mm,
linux-kernel, kernel-team
On Fri, 13 Nov 2026 15:52:35 -0800 Joshua Hahn <joshua.hahnjy@gmail.com> wrote:
> get_page_from_freelist() checks many conditions for each zone, often
> with a big comment block explaining the conditions.
>
> Move the kswapd check and its comment into should_skip_kswapd_zone() to
> make the zone iteration cleaner to read.
>
> While moving this, also resolve a checkpatch warning which complains of
> a waitqueue_active() call with no corresponding comment explaining why
> the lockless use is OK in this case.
>
> No functional change intended.
Looks correct to me.
>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Reviewed-by: SJ Park <sj@kernel.org>
[...]
> @@ -3865,22 +3885,10 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
> }
> }
>
> - /*
> - * 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)) {
> - mark = wmark_pages(zone, kswapd_watermark);
> - if (!zone_watermark_fast(zone, order, mark,
> - ac->highest_zoneidx, 0)) {
> - skipped_kswapd_nodes = true;
> - continue;
> - }
> + should_skip_kswapd_zone(zone, order, ac)) {
> + skipped_kswapd_nodes = true;
> + continue;
Just thinking loud. While reviewing I realized this patch is not based on
mm-new but apparently mm/page_alloc.git for-next. I should also update some of
my setups.
Thanks,
SJ
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/3] mm/vmscan: consolidate promo vs. high watermark checks
2026-11-13 23:52 [PATCH 0/3] mm/page_alloc, vmscan: get_page_from_freelist cleanups Joshua Hahn
2026-11-13 23:52 ` [PATCH 1/3] mm/page_alloc: drop unused gfp_mask from zone_watermark_fast() Joshua Hahn
2026-11-13 23:52 ` [PATCH 2/3] mm/page_alloc: move the kswapd node skip check into a helper Joshua Hahn
@ 2026-11-13 23:52 ` Joshua Hahn
2026-11-14 1:49 ` SJ Park
2 siblings, 1 reply; 7+ messages in thread
From: Joshua Hahn @ 2026-11-13 23:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R. Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, Qi Zheng, Shakeel Butt, linux-mm,
linux-kernel, kernel-team
There are three sites which all check the watermark that kswapd reclaims
zones up to, which is the promo watermark when NUMA balancing memory
tiering is enabled, and the high watermark otherwise.
Consolidate the patern into a helper function. While at it, remove the
include for <linux/sched/sysctl.h> in mm/page_alloc.c now that the
dependency on sysctl_numa_balancing_mode has been abstracted into
vmscan code.
No functional changes intended.
Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
---
mm/internal.h | 1 +
mm/page_alloc.c | 6 +-----
mm/vmscan.c | 22 +++++++++++++---------
3 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/mm/internal.h b/mm/internal.h
index 566cbd2dce485..8beb5b263ca1f 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -68,6 +68,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
gfp_t gfp_mask, const nodemask_t *mask);
unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru,
int zone_idx);
+unsigned long kswapd_wmark_pages(const struct zone *zone);
#define MEMCG_RECLAIM_MAY_SWAP (1 << 1)
#define MEMCG_RECLAIM_PROACTIVE (1 << 2)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 2fe49f3c0de13..6d6da844f4b76 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -45,7 +45,6 @@
#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>
@@ -3801,10 +3800,7 @@ static bool should_skip_kswapd_zone(struct zone *zone, unsigned int order,
if (waitqueue_active(&zone->zone_pgdat->kswapd_wait))
return false;
- if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING)
- mark = promo_wmark_pages(zone);
- else
- mark = high_wmark_pages(zone);
+ mark = kswapd_wmark_pages(zone);
return !zone_watermark_fast(zone, order, mark, ac->highest_zoneidx, 0);
}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 9959ca158ce83..9af50783fc824 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -478,6 +478,17 @@ unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone
return size;
}
+/*
+ * The watermark kswapd reclaims zones up to. With NUMA balancing memory
+ * tiering, kswapd keeps room above the high watermark for promotions.
+ */
+unsigned long kswapd_wmark_pages(const struct zone *zone)
+{
+ if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING)
+ return promo_wmark_pages(zone);
+ return high_wmark_pages(zone);
+}
+
static unsigned long drop_slab_node(int nid)
{
unsigned long freed = 0;
@@ -5212,7 +5223,6 @@ static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc)
{
int i;
- enum zone_watermarks mark;
if (unlikely(sc->proactive && signal_pending(current)))
return true;
@@ -5224,12 +5234,9 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc)
if (!current_is_kswapd() || sc->order)
return false;
- mark = sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING ?
- WMARK_PROMO : WMARK_HIGH;
-
for (i = 0; i <= sc->reclaim_idx; i++) {
struct zone *zone = lruvec_pgdat(lruvec)->node_zones + i;
- unsigned long size = wmark_pages(zone, mark) + MIN_LRU_BATCH;
+ unsigned long size = kswapd_wmark_pages(zone) + MIN_LRU_BATCH;
if (managed_zone(zone) && !zone_watermark_ok(zone, 0, size, sc->reclaim_idx, 0))
return false;
@@ -7131,10 +7138,7 @@ static bool pgdat_balanced(pg_data_t *pgdat, int order, int highest_zoneidx)
enum zone_stat_item item;
unsigned long free_pages;
- if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING)
- mark = promo_wmark_pages(zone);
- else
- mark = high_wmark_pages(zone);
+ mark = kswapd_wmark_pages(zone);
/*
* In defrag_mode, watermarks must be met in whole
--
2.53.1-Meta
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 3/3] mm/vmscan: consolidate promo vs. high watermark checks
2026-11-13 23:52 ` [PATCH 3/3] mm/vmscan: consolidate promo vs. high watermark checks Joshua Hahn
@ 2026-11-14 1:49 ` SJ Park
0 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-11-14 1:49 UTC (permalink / raw)
To: Joshua Hahn
Cc: SJ Park, Andrew Morton, Vlastimil Babka, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, Qi Zheng, Shakeel Butt, linux-mm,
linux-kernel, kernel-team
On Fri, 13 Nov 2026 15:52:36 -0800 Joshua Hahn <joshua.hahnjy@gmail.com> wrote:
> There are three sites which all check the watermark that kswapd reclaims
> zones up to, which is the promo watermark when NUMA balancing memory
> tiering is enabled, and the high watermark otherwise.
>
> Consolidate the patern into a helper function. While at it, remove the
Nit of nit. s/patern/pattern/ ?
> include for <linux/sched/sysctl.h> in mm/page_alloc.c now that the
> dependency on sysctl_numa_balancing_mode has been abstracted into
> vmscan code.
>
> No functional changes intended.
Looks good to me.
>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Reviewed-by: SJ Park <sj@kernel.org>
Thanks,
SJ
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread