[RFC PATCH 2/8] mm/vmscan: add try_to_free_mem_cgroup_pages_nodemask

From: liuqiqi

Date: Mon Aug 17 2026 - 22:36:48 EST


From: Qiqi Liu <liuqiqi@xxxxxxxxxx>

try_to_free_mem_cgroup_pages() reclaims from all nodes of a cgroup.
Refactor it to call a new helper,
try_to_free_mem_cgroup_pages_nodemask(), which constrains scanning to a
caller-supplied nodemask. The existing API is preserved as a wrapper
passing NULL, leaving all current callers unchanged.

The reclaim core already honours sc->nodemask for cgroup reclaim via
shrink_zones(), so this change integrates cleanly.

This is needed for per-tier memory control: reclaim must be scoped to
the nodes of the over-limit tier so that enforcing one tier's limit
does not evict pages from unrelated tiers. The new helper will be used
to enforce both tier.high and tier.max.

Signed-off-by: Qiqi Liu <liuqiqi@xxxxxxxxxx>
---
include/linux/swap.h | 6 ++++++
mm/vmscan.c | 26 ++++++++++++++++++++++++--
2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/include/linux/swap.h b/include/linux/swap.h
index 8f0f68e245ba..6a79d6995082 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -367,6 +367,12 @@ extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
gfp_t gfp_mask,
unsigned int reclaim_options,
int *swappiness);
+extern unsigned long try_to_free_mem_cgroup_pages_nodemask(struct mem_cgroup *memcg,
+ unsigned long nr_pages,
+ gfp_t gfp_mask,
+ unsigned int reclaim_options,
+ int *swappiness,
+ nodemask_t *nodemask);
extern unsigned long mem_cgroup_shrink_node(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap,
pg_data_t *pgdat,
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 35c3bb15ae96..e0602e681d96 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -6759,11 +6759,12 @@ unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,
return sc.nr_reclaimed;
}

-unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
+unsigned long try_to_free_mem_cgroup_pages_nodemask(struct mem_cgroup *memcg,
unsigned long nr_pages,
gfp_t gfp_mask,
unsigned int reclaim_options,
- int *swappiness)
+ int *swappiness,
+ nodemask_t *nodemask)
{
unsigned long nr_reclaimed;
unsigned int noreclaim_flag;
@@ -6779,6 +6780,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
.may_unmap = 1,
.may_swap = !!(reclaim_options & MEMCG_RECLAIM_MAY_SWAP),
.proactive = !!(reclaim_options & MEMCG_RECLAIM_PROACTIVE),
+ .nodemask = nodemask,
};
/*
* Traverse the ZONELIST_FALLBACK zonelist of the current node to put
@@ -6799,6 +6801,16 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,

return nr_reclaimed;
}
+
+unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
+ unsigned long nr_pages,
+ gfp_t gfp_mask,
+ unsigned int reclaim_options,
+ int *swappiness)
+{
+ return try_to_free_mem_cgroup_pages_nodemask(memcg, nr_pages, gfp_mask,
+ reclaim_options, swappiness, NULL);
+}
#else
unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
unsigned long nr_pages,
@@ -6808,6 +6820,16 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
{
return 0;
}
+
+unsigned long try_to_free_mem_cgroup_pages_nodemask(struct mem_cgroup *memcg,
+ unsigned long nr_pages,
+ gfp_t gfp_mask,
+ unsigned int reclaim_options,
+ int *swappiness,
+ nodemask_t *nodemask)
+{
+ return 0;
+}
#endif

static void kswapd_age_node(struct pglist_data *pgdat, struct scan_control *sc)
--
2.43.0