Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/4] mm/vmscan: fix swappiness=max and clean up per-node proactive reclaim
@ 2026-07-18  9:52 Ridong Chen
  2026-07-18  9:52 ` [PATCH v2 1/4] mm/vmscan: fix anon-only reclaim evicting file pages when swappiness=max Ridong Chen
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Ridong Chen @ 2026-07-18  9:52 UTC (permalink / raw)
  To: akpm
  Cc: hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong,
	baohua, axelrasmussen, yuanchu, weixugc, hezhongkun.hzk,
	muchun.song, dave, roman.gushchin, linux-mm, linux-kernel,
	ridong.chen, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

Fixes and one cleanup.

Patch 1 fixes "swappiness=max": the anon-only test in get_scan_count()
sat after the "cannot reclaim anon" check, so when no anon was
reclaimable the request fell back to SCAN_FILE and evicted page cache
instead.

Patch 2 fixes reclaim_store() collapsing every error into -EAGAIN, so
callers can no longer tell an invalid argument from a busy interface;
propagate the real error code, matching the memcg path.

Patch 3 drops the now-unused gfp_mask parameter from __node_reclaim().

Patch 4 fixes the same "swappiness=max" issue for MGLRU.

---

Changes since v1:
- Collected review tags for patches 1-3
- Added patch 4 to fix the same issue in the MGLRU path.

Ridong Chen (4):
  mm/vmscan: fix anon-only reclaim evicting file pages when
    swappiness=max
  mm: vmscan: propagate real error code from per-node proactive reclaim
  mm: vmscan: drop unused gfp_mask parameter from __node_reclaim()
  mm/mglru: fix anon-only reclaim evicting file pages when
    swappiness=max

 mm/vmscan.c | 43 +++++++++++++++++++++++++++++--------------
 1 file changed, 29 insertions(+), 14 deletions(-)

-- 
2.43.0



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/4] mm/vmscan: fix anon-only reclaim evicting file pages when swappiness=max
  2026-07-18  9:52 [PATCH v2 0/4] mm/vmscan: fix swappiness=max and clean up per-node proactive reclaim Ridong Chen
@ 2026-07-18  9:52 ` Ridong Chen
  2026-07-18  9:52 ` [PATCH v2 2/4] mm: vmscan: propagate real error code from per-node proactive reclaim Ridong Chen
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Ridong Chen @ 2026-07-18  9:52 UTC (permalink / raw)
  To: akpm
  Cc: hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong,
	baohua, axelrasmussen, yuanchu, weixugc, hezhongkun.hzk,
	muchun.song, dave, roman.gushchin, linux-mm, linux-kernel,
	ridong.chen, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

As Qi mentioned [1], when swappiness=max (SWAPPINESS_ANON_ONLY) is set,
the reclaim logic is expected to reclaim anonymous pages exclusively.
However, due to the current ordering of checks in get_scan_count(),
file pages may still be evicted if can_reclaim_anon_pages() returns
false, which contradicts the semantics of SWAPPINESS_ANON_ONLY.

Reproducer in a cgroup holding 64M of file cache, with no swap configured:

  Before (file cache is wrongly evicted):
    # cat memory.stat
    anon 196608
    file 67178496
    pgscan_proactive 0
    # echo "64M swappiness=max" > memory.reclaim
    # cat memory.stat
    anon 208896
    file 4096                 <- page cache evicted
    pgsteal_proactive 16400
    pgscan_proactive 16400

  After (file cache is left intact):
    # cat memory.stat
    anon 200704
    file 67178496
    pgscan_proactive 0
    # echo "64M swappiness=max" > memory.reclaim
    -bash: echo: write error: Resource temporarily unavailable
    # cat memory.stat
    anon 208896
    file 67178496             <- page cache untouched
    pgsteal_proactive 0
    pgscan_proactive 0

Fix this by bailing out early when SWAPPINESS_ANON_ONLY is set and no
anonymous pages are reclaimable, before falling back to file reclaim.

[1] https://lore.kernel.org/cgroups/7ddf3eee-5fe2-45f7-8614-c8936a039e04@linux.dev/

Fixes: 68a1436bde00 ("mm: add swappiness=max arg to memory.reclaim for only anon reclaim")
Suggested-by: Qi Zheng <qi.zheng@linux.dev>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Reviewed-by: Muchun Song <muchun.song@linux.dev>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 mm/vmscan.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 35c3bb15ae96..d6b383d96a0c 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2501,6 +2501,17 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
 	enum scan_balance scan_balance;
 	enum lru_list lru;
 
+	/* Proactive reclaim initiated by userspace for anonymous memory only */
+	if (swappiness == SWAPPINESS_ANON_ONLY) {
+		WARN_ON_ONCE(!sc->proactive);
+		if (!can_reclaim_anon_pages(memcg, pgdat->node_id, sc)) {
+			memset(nr, 0, sizeof(*nr) * NR_LRU_LISTS);
+			return;
+		}
+		scan_balance = SCAN_ANON;
+		goto out;
+	}
+
 	/* If we have no swap space, do not bother scanning anon folios. */
 	if (!sc->may_swap || !can_reclaim_anon_pages(memcg, pgdat->node_id, sc)) {
 		scan_balance = SCAN_FILE;
@@ -2519,13 +2530,6 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
 		goto out;
 	}
 
-	/* Proactive reclaim initiated by userspace for anonymous memory only */
-	if (swappiness == SWAPPINESS_ANON_ONLY) {
-		WARN_ON_ONCE(!sc->proactive);
-		scan_balance = SCAN_ANON;
-		goto out;
-	}
-
 	/*
 	 * Do not apply any pressure balancing cleverness when the
 	 * system is close to OOM, scan both anon and file equally
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/4] mm: vmscan: propagate real error code from per-node proactive reclaim
  2026-07-18  9:52 [PATCH v2 0/4] mm/vmscan: fix swappiness=max and clean up per-node proactive reclaim Ridong Chen
  2026-07-18  9:52 ` [PATCH v2 1/4] mm/vmscan: fix anon-only reclaim evicting file pages when swappiness=max Ridong Chen
@ 2026-07-18  9:52 ` Ridong Chen
  2026-07-18  9:52 ` [PATCH v2 3/4] mm: vmscan: drop unused gfp_mask parameter from __node_reclaim() Ridong Chen
  2026-07-18  9:52 ` [PATCH v2 4/4] mm/mglru: fix anon-only reclaim evicting file pages when swappiness=max Ridong Chen
  3 siblings, 0 replies; 6+ messages in thread
From: Ridong Chen @ 2026-07-18  9:52 UTC (permalink / raw)
  To: akpm
  Cc: hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong,
	baohua, axelrasmussen, yuanchu, weixugc, hezhongkun.hzk,
	muchun.song, dave, roman.gushchin, linux-mm, linux-kernel,
	ridong.chen, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

It has been observed that per-node proactive reclaim always returns
-EAGAIN when any error occurs. As discussed in the mailing list [1],
the interface should distinguish between cases where no reclaimable
memory is left and where another entity is concurrently using the
same interface. Propagate the real error code, consistent with how
memcg proactive reclaim handles errors.

[1] https://lore.kernel.org/all/20250717235604.2atyx2aobwowpge3@offworld/T/#m3514718be82a31b05726a49da9b61fbfc69a589e

Fixes: b980077899ea ("mm: introduce per-node proactive reclaim interface")
Reviewed-by: Muchun Song <muchun.song@linux.dev>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 mm/vmscan.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index d6b383d96a0c..cfe4556c96d7 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -8017,7 +8017,7 @@ static ssize_t reclaim_store(struct device *dev,
 	int ret, nid = dev->id;
 
 	ret = user_proactive_reclaim((char *)buf, NULL, NODE_DATA(nid));
-	return ret ? -EAGAIN : count;
+	return ret ? ret : count;
 }
 
 static DEVICE_ATTR_WO(reclaim);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 3/4] mm: vmscan: drop unused gfp_mask parameter from __node_reclaim()
  2026-07-18  9:52 [PATCH v2 0/4] mm/vmscan: fix swappiness=max and clean up per-node proactive reclaim Ridong Chen
  2026-07-18  9:52 ` [PATCH v2 1/4] mm/vmscan: fix anon-only reclaim evicting file pages when swappiness=max Ridong Chen
  2026-07-18  9:52 ` [PATCH v2 2/4] mm: vmscan: propagate real error code from per-node proactive reclaim Ridong Chen
@ 2026-07-18  9:52 ` Ridong Chen
  2026-07-18 13:12   ` Barry Song
  2026-07-18  9:52 ` [PATCH v2 4/4] mm/mglru: fix anon-only reclaim evicting file pages when swappiness=max Ridong Chen
  3 siblings, 1 reply; 6+ messages in thread
From: Ridong Chen @ 2026-07-18  9:52 UTC (permalink / raw)
  To: akpm
  Cc: hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong,
	baohua, axelrasmussen, yuanchu, weixugc, hezhongkun.hzk,
	muchun.song, dave, roman.gushchin, linux-mm, linux-kernel,
	ridong.chen, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

Commit 57972c78e678 ("mm/vmscan: make __node_reclaim() more generic")
moved the scan_control construction out to the callers and passed the
struct in by pointer.  After that change every use of the gfp mask inside
__node_reclaim() goes through sc->gfp_mask, leaving the gfp_mask parameter
unused. Just remove the dead parameter and update the callers accordingly.
No functional change.

Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Reviewed-by: Muchun Song <muchun.song@linux.dev>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 mm/vmscan.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index cfe4556c96d7..e1ef6350a300 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -7742,7 +7742,7 @@ static unsigned long node_pagecache_reclaimable(struct pglist_data *pgdat)
 /*
  * Try to free up some pages from this node through reclaim.
  */
-static unsigned long __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask,
+static unsigned long __node_reclaim(struct pglist_data *pgdat,
 				    unsigned long nr_pages,
 				    struct scan_control *sc)
 {
@@ -7834,7 +7834,7 @@ int node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order)
 	if (test_and_set_bit_lock(PGDAT_RECLAIM_LOCKED, &pgdat->flags))
 		return NODE_RECLAIM_NOSCAN;
 
-	ret = __node_reclaim(pgdat, gfp_mask, nr_pages, &sc) >= nr_pages;
+	ret = __node_reclaim(pgdat, nr_pages, &sc) >= nr_pages;
 	clear_bit_unlock(PGDAT_RECLAIM_LOCKED, &pgdat->flags);
 
 	if (ret)
@@ -7847,7 +7847,7 @@ int node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order)
 
 #else
 
-static unsigned long __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask,
+static unsigned long __node_reclaim(struct pglist_data *pgdat,
 				    unsigned long nr_pages,
 				    struct scan_control *sc)
 {
@@ -7949,8 +7949,7 @@ int user_proactive_reclaim(char *buf,
 						  &pgdat->flags))
 				return -EBUSY;
 
-			reclaimed = __node_reclaim(pgdat, gfp_mask,
-						   batch_size, &sc);
+			reclaimed = __node_reclaim(pgdat, batch_size, &sc);
 			clear_bit_unlock(PGDAT_RECLAIM_LOCKED, &pgdat->flags);
 		}
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 4/4] mm/mglru: fix anon-only reclaim evicting file pages when swappiness=max
  2026-07-18  9:52 [PATCH v2 0/4] mm/vmscan: fix swappiness=max and clean up per-node proactive reclaim Ridong Chen
                   ` (2 preceding siblings ...)
  2026-07-18  9:52 ` [PATCH v2 3/4] mm: vmscan: drop unused gfp_mask parameter from __node_reclaim() Ridong Chen
@ 2026-07-18  9:52 ` Ridong Chen
  3 siblings, 0 replies; 6+ messages in thread
From: Ridong Chen @ 2026-07-18  9:52 UTC (permalink / raw)
  To: akpm
  Cc: hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong,
	baohua, axelrasmussen, yuanchu, weixugc, hezhongkun.hzk,
	muchun.song, dave, roman.gushchin, linux-mm, linux-kernel,
	ridong.chen, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

The previous patch fixes this issue for the traditional LRU, which also
exists in MGLRU [1]. Fix this by checking whether swappiness is
SWAPPINESS_ANON_ONLY in get_swappiness() first, and returning 0 from
get_nr_to_scan() when swappiness is SWAPPINESS_ANON_ONLY and anon pages
cannot be reclaimed, to avoid useless work.

The test result:
Before fix:

  # cat /sys/kernel/mm/lru_gen/enabled
  0x0007
  # cat memory.stat
  anon 204800
  file 67108864
  ...
  pgsteal_proactive 0
  pgscan_proactive 0

  # echo "64M swappiness=max" > memory.reclaim
  # cat memory.stat
  anon 208896
  file 0
  ...
  pgsteal_proactive 16384
  pgscan_proactive 16384

After fix:

  # cat memory.stat
  anon 188416
  file 67215360
  kernel 1970176
  ...
  pgsteal_proactive 0
  pgscan_proactive 0

  # echo "64M swappiness=max" > memory.reclaim
  -bash: echo: write error: Resource temporarily unavailable
  # cat memory.stat
  anon 204800
  file 67215360
  ...
  pgsteal_proactive 0
  pgscan_proactive 0

[1] https://sashiko.dev/#/patchset/20260717113300.214717-1-ridong.chen@linux.dev

Fixes: 68a1436bde00 ("mm: add swappiness=max arg to memory.reclaim for only anon reclaim")
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 mm/vmscan.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index e1ef6350a300..240b04f0a21a 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2702,6 +2702,10 @@ static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc)
 {
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 	struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+	int swappiness = sc_swappiness(sc, memcg);
+
+	if (swappiness == SWAPPINESS_ANON_ONLY)
+		return swappiness;
 
 	if (!sc->may_swap)
 		return 0;
@@ -2710,7 +2714,7 @@ static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc)
 	    mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH)
 		return 0;
 
-	return sc_swappiness(sc, memcg);
+	return swappiness;
 }
 
 static int get_nr_gens(struct lruvec *lruvec, int type)
@@ -4915,6 +4919,14 @@ static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
 			   struct mem_cgroup *memcg, int swappiness)
 {
 	unsigned long nr_to_scan, evictable;
+	struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+
+	/* Proactive reclaim: anon-only requested but anon is not reclaimable */
+	if (swappiness == SWAPPINESS_ANON_ONLY &&
+	    !can_reclaim_anon_pages(memcg, pgdat->node_id, sc)) {
+		WARN_ON_ONCE(!sc->proactive);
+		return 0;
+	}
 
 	evictable = lruvec_evictable_size(lruvec, swappiness);
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 3/4] mm: vmscan: drop unused gfp_mask parameter from __node_reclaim()
  2026-07-18  9:52 ` [PATCH v2 3/4] mm: vmscan: drop unused gfp_mask parameter from __node_reclaim() Ridong Chen
@ 2026-07-18 13:12   ` Barry Song
  0 siblings, 0 replies; 6+ messages in thread
From: Barry Song @ 2026-07-18 13:12 UTC (permalink / raw)
  To: Ridong Chen
  Cc: akpm, hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong,
	axelrasmussen, yuanchu, weixugc, hezhongkun.hzk, muchun.song,
	dave, roman.gushchin, linux-mm, linux-kernel, Ridong Chen

On Sat, Jul 18, 2026 at 5:54 PM Ridong Chen <ridong.chen@linux.dev> wrote:
>
> From: Ridong Chen <chenridong@xiaomi.com>
>
> Commit 57972c78e678 ("mm/vmscan: make __node_reclaim() more generic")
> moved the scan_control construction out to the callers and passed the
> struct in by pointer.  After that change every use of the gfp mask inside
> __node_reclaim() goes through sc->gfp_mask, leaving the gfp_mask parameter
> unused. Just remove the dead parameter and update the callers accordingly.
> No functional change.
>
> Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
> Acked-by: Johannes Weiner <hannes@cmpxchg.org>
> Reviewed-by: Muchun Song <muchun.song@linux.dev>
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>

Reviewed-by: Barry Song <baohua@kernel.org>


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-07-18 13:12 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-18  9:52 [PATCH v2 0/4] mm/vmscan: fix swappiness=max and clean up per-node proactive reclaim Ridong Chen
2026-07-18  9:52 ` [PATCH v2 1/4] mm/vmscan: fix anon-only reclaim evicting file pages when swappiness=max Ridong Chen
2026-07-18  9:52 ` [PATCH v2 2/4] mm: vmscan: propagate real error code from per-node proactive reclaim Ridong Chen
2026-07-18  9:52 ` [PATCH v2 3/4] mm: vmscan: drop unused gfp_mask parameter from __node_reclaim() Ridong Chen
2026-07-18 13:12   ` Barry Song
2026-07-18  9:52 ` [PATCH v2 4/4] mm/mglru: fix anon-only reclaim evicting file pages when swappiness=max Ridong Chen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox