The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v2 0/2] mm: fix node reclaim swappiness handling
@ 2026-07-16  3:17 Ridong
  2026-07-16  3:17 ` [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h Ridong
  2026-07-16  3:17 ` [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter Ridong
  0 siblings, 2 replies; 12+ messages in thread
From: Ridong @ 2026-07-16  3:17 UTC (permalink / raw)
  To: Johannes Weiner, Michal Hocko, Roman Gushchin, Shakeel Butt,
	Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Qi Zheng, Lorenzo Stoakes,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups,
	linux-mm, linux-kernel, Ridong Chen, Ridong

From: Ridong <chenridong@xiaomi.com>

The per-node proactive reclaim interface
(/sys/devices/system/node/nodeX/reclaim) accepts a swappiness parameter,
but it is silently ignored when CONFIG_MEMCG is disabled. The root cause
is that sc_swappiness() has separate implementations for CONFIG_MEMCG and
!CONFIG_MEMCG, and the latter never checks proactive_swappiness.

Patch 1 moves mem_cgroup_swappiness() and vm_swappiness out of the public
include/linux/swap.h into the mm-private mm/swap.h, and makes the helper
handle both CONFIG_MEMCG and !CONFIG_MEMCG in a single inline function.
This is a prerequisite for unifying sc_swappiness().

Patch 2 consolidates sc_swappiness() into a single definition that works
regardless of CONFIG_MEMCG, fixing the node reclaim swappiness bug.

---

v1 -> v2:
 - Move mem_cgroup_swappiness() and vm_swappiness to mm/swap.h instead of
   include/linux/memcontrol.h. Suggested by Barry Song.
 - Correct fix tag for patch 2.

v1: https://lore.kernel.org/all/20260711091157.306070-1-ridong.chen@linux.dev/

Ridong Chen (2):
  memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h
  mm: vmscan: fix node reclaim ignoring swappiness parameter

 include/linux/memcontrol.h |  4 ++--
 include/linux/swap.h       | 19 -------------------
 mm/memcontrol.c            |  4 ++--
 mm/swap.h                  | 20 ++++++++++++++++++++
 mm/vmscan.c                | 19 +++++++------------
 5 files changed, 31 insertions(+), 35 deletions(-)

-- 
2.34.1


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

* [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h
  2026-07-16  3:17 [PATCH v2 0/2] mm: fix node reclaim swappiness handling Ridong
@ 2026-07-16  3:17 ` Ridong
  2026-07-16  6:06   ` Tao Cui
  2026-07-16  7:14   ` Barry Song
  2026-07-16  3:17 ` [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter Ridong
  1 sibling, 2 replies; 12+ messages in thread
From: Ridong @ 2026-07-16  3:17 UTC (permalink / raw)
  To: Johannes Weiner, Michal Hocko, Roman Gushchin, Shakeel Butt,
	Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Qi Zheng, Lorenzo Stoakes,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups,
	linux-mm, linux-kernel, Ridong Chen, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

The per-memcg swappiness knob is v1-only; v2 always uses global
vm_swappiness and ignores the per-cgroup field.

Both mem_cgroup_swappiness() and vm_swappiness are only used within
mm/ (memcontrol.c, memcontrol-v1.c, vmscan.c), so move them out of the
public include/linux/swap.h into the mm-private mm/swap.h. This keeps
unrelated declarations out of include/linux/swap.h.

Guard memcg->swappiness with CONFIG_MEMCG_V1 as well, so v2-only
kernels drop the unused field.

No functional change for v1; v2-only kernels drop the unused field.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 include/linux/memcontrol.h |  4 ++--
 include/linux/swap.h       | 19 -------------------
 mm/memcontrol.c            |  4 ++--
 mm/swap.h                  | 20 ++++++++++++++++++++
 4 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 957260677678..e7a794dfb40e 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -239,8 +239,6 @@ struct mem_cgroup {
 	 */
 	bool oom_group;
 
-	int swappiness;
-
 	/* memory.events and memory.events.local */
 	struct cgroup_file events_file;
 	struct cgroup_file events_local_file;
@@ -318,6 +316,8 @@ struct mem_cgroup {
 	/* List of events which userspace want to receive */
 	struct list_head event_list;
 	spinlock_t event_list_lock;
+
+	int swappiness;
 #endif /* CONFIG_MEMCG_V1 */
 
 	struct mem_cgroup_per_node *nodeinfo[];
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 696ed01709c2..330a420fd6de 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -309,7 +309,6 @@ static inline bool lru_cache_disabled(void)
 }
 
 extern unsigned long shrink_all_memory(unsigned long nr_pages);
-extern int vm_swappiness;
 long remove_mapping(struct address_space *mapping, struct folio *folio);
 
 #if defined(CONFIG_SYSFS) && defined(CONFIG_NUMA)
@@ -468,25 +467,7 @@ static inline int add_swap_extent(struct swap_info_struct *sis,
 }
 #endif /* CONFIG_SWAP */
 #ifdef CONFIG_MEMCG
-static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
-{
-	/* Cgroup2 doesn't have per-cgroup swappiness */
-	if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
-		return READ_ONCE(vm_swappiness);
-
-	/* root ? */
-	if (mem_cgroup_disabled() || mem_cgroup_is_root(memcg))
-		return READ_ONCE(vm_swappiness);
-
-	return READ_ONCE(memcg->swappiness);
-}
-
 void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid);
-#else
-static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
-{
-	return READ_ONCE(vm_swappiness);
-}
 #endif
 
 #if defined(CONFIG_SWAP) && defined(CONFIG_MEMCG) && defined(CONFIG_BLK_CGROUP)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 22f55aeb94f3..d6ba276740ea 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -63,6 +63,7 @@
 #include <linux/sched/isolation.h>
 #include <linux/kmemleak.h>
 #include "internal.h"
+#include "swap.h"
 #include "swap_table.h"
 #include <net/sock.h>
 #include <net/ip.h>
@@ -4174,11 +4175,10 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
 #endif
 	page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
 	if (parent) {
-		WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
-
 		page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
 		page_counter_init(&memcg->swap, &parent->swap, false);
 #ifdef CONFIG_MEMCG_V1
+		WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
 		memcg->memory.track_failcnt = !memcg_on_dfl;
 		WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable));
 		page_counter_init(&memcg->kmem, &parent->kmem, false);
diff --git a/mm/swap.h b/mm/swap.h
index b51ad3071a73..a0e04b97a466 100644
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -4,6 +4,7 @@
 
 #include <linux/atomic.h> /* for atomic_long_t */
 #include <linux/mm.h> /* for PAGE_SHIFT */
+#include <linux/memcontrol.h> /* for mem_cgroup_swappiness() */
 struct mempolicy;
 struct swap_iocb;
 struct swap_memcg_table;
@@ -76,6 +77,25 @@ enum swap_cluster_flags {
 	CLUSTER_FLAG_MAX,
 };
 
+extern int vm_swappiness;
+
+static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
+{
+#ifdef CONFIG_MEMCG_V1
+	/* Cgroup2 doesn't have per-cgroup swappiness */
+	if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
+		return READ_ONCE(vm_swappiness);
+
+	/* root ? */
+	if (mem_cgroup_disabled() || mem_cgroup_is_root(memcg))
+		return READ_ONCE(vm_swappiness);
+
+	return READ_ONCE(memcg->swappiness);
+#else
+	return READ_ONCE(vm_swappiness);
+#endif
+}
+
 #ifdef CONFIG_SWAP
 #include <linux/swapops.h> /* for swp_offset */
 #include <linux/blk_types.h> /* for bio_end_io_t */
-- 
2.34.1


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

* [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  3:17 [PATCH v2 0/2] mm: fix node reclaim swappiness handling Ridong
  2026-07-16  3:17 ` [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h Ridong
@ 2026-07-16  3:17 ` Ridong
  2026-07-16  3:25   ` Qi Zheng
                     ` (2 more replies)
  1 sibling, 3 replies; 12+ messages in thread
From: Ridong @ 2026-07-16  3:17 UTC (permalink / raw)
  To: Johannes Weiner, Michal Hocko, Roman Gushchin, Shakeel Butt,
	Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Qi Zheng, Lorenzo Stoakes,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups,
	linux-mm, linux-kernel, Ridong Chen, Ridong Chen, stable

From: Ridong Chen <chenridong@xiaomi.com>

sc_swappiness() had two separate definitions depending on
CONFIG_MEMCG. The !CONFIG_MEMCG variant simply returned
vm_swappiness, ignoring the proactive_swappiness value passed
through scan_control. This caused the swappiness parameter
written to /sys/devices/system/node/nodeX/reclaim to have no
effect when CONFIG_MEMCG is disabled.

Fix this by consolidating sc_swappiness() into a single definition
that checks sc->proactive_swappiness first, then falls back to
mem_cgroup_swappiness() which already handles both CONFIG_MEMCG
and !CONFIG_MEMCG.

Before fix (swappiness=max ignored, mostly file pages reclaimed):

    # cat /proc/sys/vm/swappiness
    60
    # cat /proc/vmstat | grep pgsteal
    pgsteal_kswapd 0
    pgsteal_direct 0
    pgsteal_khugepaged 0
    pgsteal_proactive 1840
    pgsteal_anon 25
    pgsteal_file 1815
    # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
    # cat /proc/vmstat | grep pgsteal
    pgsteal_kswapd 0
    pgsteal_direct 0
    pgsteal_khugepaged 0
    pgsteal_proactive 18013
    pgsteal_anon 337
    pgsteal_file 17676

After fix (swappiness=max honored, anon pages reclaimed as expected):

    # cat /proc/vmstat | grep pgsteal
    pgsteal_kswapd 0
    pgsteal_direct 0
    pgsteal_khugepaged 0
    pgsteal_proactive 0
    pgsteal_anon 0
    pgsteal_file 0
    # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
    # cat /proc/vmstat | grep pgsteal
    pgsteal_kswapd 0
    pgsteal_direct 0
    pgsteal_khugepaged 0
    pgsteal_proactive 16283
    pgsteal_anon 16283
    pgsteal_file 0

Cc: stable@vger.kernel.org # 6.17+
Fixes: b980077899ea ("mm: introduce per-node proactive reclaim interface")
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Reviewed-by: Barry Song <baohua@kernel.org>
---
 mm/vmscan.c | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 986dde8e7429..bd71595c8b2b 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -199,6 +199,13 @@ struct scan_control {
  */
 int vm_swappiness = 60;
 
+static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
+{
+	if (sc->proactive && sc->proactive_swappiness)
+		return *sc->proactive_swappiness;
+	return mem_cgroup_swappiness(memcg);
+}
+
 #ifdef CONFIG_MEMCG
 
 /* Returns true for reclaim through cgroup limits or cgroup interfaces. */
@@ -239,13 +246,6 @@ static bool writeback_throttling_sane(struct scan_control *sc)
 #endif
 	return false;
 }
-
-static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
-{
-	if (sc->proactive && sc->proactive_swappiness)
-		return *sc->proactive_swappiness;
-	return mem_cgroup_swappiness(memcg);
-}
 #else
 static bool cgroup_reclaim(struct scan_control *sc)
 {
@@ -261,11 +261,6 @@ static bool writeback_throttling_sane(struct scan_control *sc)
 {
 	return true;
 }
-
-static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
-{
-	return READ_ONCE(vm_swappiness);
-}
 #endif
 
 static void set_task_reclaim_state(struct task_struct *task,
-- 
2.34.1


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

* Re: [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  3:17 ` [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter Ridong
@ 2026-07-16  3:25   ` Qi Zheng
  2026-07-16  3:58   ` Qi Zheng
  2026-07-16  6:06   ` Tao Cui
  2 siblings, 0 replies; 12+ messages in thread
From: Qi Zheng @ 2026-07-16  3:25 UTC (permalink / raw)
  To: Ridong, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Lorenzo Stoakes, Axel Rasmussen,
	Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups, linux-mm,
	linux-kernel, Ridong Chen, stable



On 7/16/26 11:17 AM, Ridong wrote:
> From: Ridong Chen <chenridong@xiaomi.com>
> 
> sc_swappiness() had two separate definitions depending on
> CONFIG_MEMCG. The !CONFIG_MEMCG variant simply returned
> vm_swappiness, ignoring the proactive_swappiness value passed
> through scan_control. This caused the swappiness parameter
> written to /sys/devices/system/node/nodeX/reclaim to have no
> effect when CONFIG_MEMCG is disabled.
> 
> Fix this by consolidating sc_swappiness() into a single definition
> that checks sc->proactive_swappiness first, then falls back to
> mem_cgroup_swappiness() which already handles both CONFIG_MEMCG
> and !CONFIG_MEMCG.
> 
> Before fix (swappiness=max ignored, mostly file pages reclaimed):
> 
>      # cat /proc/sys/vm/swappiness
>      60
>      # cat /proc/vmstat | grep pgsteal
>      pgsteal_kswapd 0
>      pgsteal_direct 0
>      pgsteal_khugepaged 0
>      pgsteal_proactive 1840
>      pgsteal_anon 25
>      pgsteal_file 1815
>      # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
>      # cat /proc/vmstat | grep pgsteal
>      pgsteal_kswapd 0
>      pgsteal_direct 0
>      pgsteal_khugepaged 0
>      pgsteal_proactive 18013
>      pgsteal_anon 337
>      pgsteal_file 17676
> 
> After fix (swappiness=max honored, anon pages reclaimed as expected):
> 
>      # cat /proc/vmstat | grep pgsteal
>      pgsteal_kswapd 0
>      pgsteal_direct 0
>      pgsteal_khugepaged 0
>      pgsteal_proactive 0
>      pgsteal_anon 0
>      pgsteal_file 0
>      # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
>      # cat /proc/vmstat | grep pgsteal
>      pgsteal_kswapd 0
>      pgsteal_direct 0
>      pgsteal_khugepaged 0
>      pgsteal_proactive 16283
>      pgsteal_anon 16283
>      pgsteal_file 0
> 
> Cc: stable@vger.kernel.org # 6.17+
> Fixes: b980077899ea ("mm: introduce per-node proactive reclaim interface")
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> Acked-by: Johannes Weiner <hannes@cmpxchg.org>
> Reviewed-by: Barry Song <baohua@kernel.org>
> ---
>   mm/vmscan.c | 19 +++++++------------
>   1 file changed, 7 insertions(+), 12 deletions(-)

Thanks for fixing this!

Acked-by: Qi Zheng <qi.zheng@linux.dev>



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

* Re: [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  3:17 ` [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter Ridong
  2026-07-16  3:25   ` Qi Zheng
@ 2026-07-16  3:58   ` Qi Zheng
  2026-07-16  8:21     ` Ridong Chen
  2026-07-16  6:06   ` Tao Cui
  2 siblings, 1 reply; 12+ messages in thread
From: Qi Zheng @ 2026-07-16  3:58 UTC (permalink / raw)
  To: Ridong, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Lorenzo Stoakes, Axel Rasmussen,
	Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups, linux-mm,
	linux-kernel, Ridong Chen, stable

Hi Ridong,

On 7/16/26 11:17 AM, Ridong wrote:
> From: Ridong Chen <chenridong@xiaomi.com>
> 
> sc_swappiness() had two separate definitions depending on
> CONFIG_MEMCG. The !CONFIG_MEMCG variant simply returned
> vm_swappiness, ignoring the proactive_swappiness value passed
> through scan_control. This caused the swappiness parameter
> written to /sys/devices/system/node/nodeX/reclaim to have no
> effect when CONFIG_MEMCG is disabled.
> 
> Fix this by consolidating sc_swappiness() into a single definition
> that checks sc->proactive_swappiness first, then falls back to
> mem_cgroup_swappiness() which already handles both CONFIG_MEMCG
> and !CONFIG_MEMCG.
> 
> Before fix (swappiness=max ignored, mostly file pages reclaimed):
> 
>      # cat /proc/sys/vm/swappiness
>      60
>      # cat /proc/vmstat | grep pgsteal
>      pgsteal_kswapd 0
>      pgsteal_direct 0
>      pgsteal_khugepaged 0
>      pgsteal_proactive 1840
>      pgsteal_anon 25
>      pgsteal_file 1815
>      # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
>      # cat /proc/vmstat | grep pgsteal
>      pgsteal_kswapd 0
>      pgsteal_direct 0
>      pgsteal_khugepaged 0
>      pgsteal_proactive 18013
>      pgsteal_anon 337
>      pgsteal_file 17676
> 
> After fix (swappiness=max honored, anon pages reclaimed as expected):
> 

By the way, in get_scan_count(), it seems we still reclaim file pages
without swap space, even if swappiness=max (SWAPPINESS_ANON_ONLY) is
set.

This behavior appears to contradict the semantics of
SWAPPINESS_ANON_ONLY, which probably needs a fix as well.

Thanks,
Qi


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

* Re: [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  3:17 ` [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter Ridong
  2026-07-16  3:25   ` Qi Zheng
  2026-07-16  3:58   ` Qi Zheng
@ 2026-07-16  6:06   ` Tao Cui
  2026-07-16  8:23     ` Ridong Chen
  2 siblings, 1 reply; 12+ messages in thread
From: Tao Cui @ 2026-07-16  6:06 UTC (permalink / raw)
  To: Ridong, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: cui.tao, Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He,
	Barry Song, Youngjun Park, David Hildenbrand, Qi Zheng,
	Lorenzo Stoakes, Axel Rasmussen, Yuanchu Xie, Wei Xu,
	Davidlohr Bueso, cgroups, linux-mm, linux-kernel, Ridong Chen,
	stable

Hi Ridong,

Reproduced the bug and verified the fix in a QEMU x86_64 guest with
CONFIG_MEMCG disabled. Global vm.swappiness pinned to 1, then:

    echo "64M swappiness=<x>" > /sys/devices/system/node/node0/reclaim

  swappiness= | buggy (anon/file)   | patched (anon/file)
  ----------- | -------------------- | --------------------
  max         |      0  /  16385     |  16292  /  0
  200         |      2  /  16384     |  16837  /  0
  1           |      0  /  16386     |      0  /  16467

Unpatched ignores the swappiness arg (always file); patched honors it.
Matches the commit message.

Tested-by: Song Hu <husong@kylinos.cn>
Reviewed-by: Song Hu <husong@kylinos.cn>

在 2026/7/16 11:17, Ridong 写道:
> From: Ridong Chen <chenridong@xiaomi.com>
> 
> sc_swappiness() had two separate definitions depending on
> CONFIG_MEMCG. The !CONFIG_MEMCG variant simply returned
> vm_swappiness, ignoring the proactive_swappiness value passed
> through scan_control. This caused the swappiness parameter
> written to /sys/devices/system/node/nodeX/reclaim to have no
> effect when CONFIG_MEMCG is disabled.
> 
> Fix this by consolidating sc_swappiness() into a single definition
> that checks sc->proactive_swappiness first, then falls back to
> mem_cgroup_swappiness() which already handles both CONFIG_MEMCG
> and !CONFIG_MEMCG.
> 
> Before fix (swappiness=max ignored, mostly file pages reclaimed):
> 
>     # cat /proc/sys/vm/swappiness
>     60
>     # cat /proc/vmstat | grep pgsteal
>     pgsteal_kswapd 0
>     pgsteal_direct 0
>     pgsteal_khugepaged 0
>     pgsteal_proactive 1840
>     pgsteal_anon 25
>     pgsteal_file 1815
>     # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
>     # cat /proc/vmstat | grep pgsteal
>     pgsteal_kswapd 0
>     pgsteal_direct 0
>     pgsteal_khugepaged 0
>     pgsteal_proactive 18013
>     pgsteal_anon 337
>     pgsteal_file 17676
> 
> After fix (swappiness=max honored, anon pages reclaimed as expected):
> 
>     # cat /proc/vmstat | grep pgsteal
>     pgsteal_kswapd 0
>     pgsteal_direct 0
>     pgsteal_khugepaged 0
>     pgsteal_proactive 0
>     pgsteal_anon 0
>     pgsteal_file 0
>     # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
>     # cat /proc/vmstat | grep pgsteal
>     pgsteal_kswapd 0
>     pgsteal_direct 0
>     pgsteal_khugepaged 0
>     pgsteal_proactive 16283
>     pgsteal_anon 16283
>     pgsteal_file 0
> 
> Cc: stable@vger.kernel.org # 6.17+
> Fixes: b980077899ea ("mm: introduce per-node proactive reclaim interface")
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> Acked-by: Johannes Weiner <hannes@cmpxchg.org>
> Reviewed-by: Barry Song <baohua@kernel.org>
> ---
>  mm/vmscan.c | 19 +++++++------------
>  1 file changed, 7 insertions(+), 12 deletions(-)
> 
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 986dde8e7429..bd71595c8b2b 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -199,6 +199,13 @@ struct scan_control {
>   */
>  int vm_swappiness = 60;
>  
> +static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
> +{
> +	if (sc->proactive && sc->proactive_swappiness)
> +		return *sc->proactive_swappiness;
> +	return mem_cgroup_swappiness(memcg);
> +}
> +
>  #ifdef CONFIG_MEMCG
>  
>  /* Returns true for reclaim through cgroup limits or cgroup interfaces. */
> @@ -239,13 +246,6 @@ static bool writeback_throttling_sane(struct scan_control *sc)
>  #endif
>  	return false;
>  }
> -
> -static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
> -{
> -	if (sc->proactive && sc->proactive_swappiness)
> -		return *sc->proactive_swappiness;
> -	return mem_cgroup_swappiness(memcg);
> -}
>  #else
>  static bool cgroup_reclaim(struct scan_control *sc)
>  {
> @@ -261,11 +261,6 @@ static bool writeback_throttling_sane(struct scan_control *sc)
>  {
>  	return true;
>  }
> -
> -static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
> -{
> -	return READ_ONCE(vm_swappiness);
> -}
>  #endif
>  
>  static void set_task_reclaim_state(struct task_struct *task,


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

* Re: [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h
  2026-07-16  3:17 ` [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h Ridong
@ 2026-07-16  6:06   ` Tao Cui
  2026-07-16  7:14   ` Barry Song
  1 sibling, 0 replies; 12+ messages in thread
From: Tao Cui @ 2026-07-16  6:06 UTC (permalink / raw)
  To: Ridong, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: cui.tao, Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He,
	Barry Song, Youngjun Park, David Hildenbrand, Qi Zheng,
	Lorenzo Stoakes, Axel Rasmussen, Yuanchu Xie, Wei Xu,
	Davidlohr Bueso, cgroups, linux-mm, linux-kernel, Ridong Chen


Reviewed-by: Song Hu <husong@kylinos.cn>
在 2026/7/16 11:17, Ridong 写道:
> From: Ridong Chen <chenridong@xiaomi.com>
> 
> The per-memcg swappiness knob is v1-only; v2 always uses global
> vm_swappiness and ignores the per-cgroup field.
> 
> Both mem_cgroup_swappiness() and vm_swappiness are only used within
> mm/ (memcontrol.c, memcontrol-v1.c, vmscan.c), so move them out of the
> public include/linux/swap.h into the mm-private mm/swap.h. This keeps
> unrelated declarations out of include/linux/swap.h.
> 
> Guard memcg->swappiness with CONFIG_MEMCG_V1 as well, so v2-only
> kernels drop the unused field.
> 
> No functional change for v1; v2-only kernels drop the unused field.
> 
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> ---
>  include/linux/memcontrol.h |  4 ++--
>  include/linux/swap.h       | 19 -------------------
>  mm/memcontrol.c            |  4 ++--
>  mm/swap.h                  | 20 ++++++++++++++++++++
>  4 files changed, 24 insertions(+), 23 deletions(-)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 957260677678..e7a794dfb40e 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -239,8 +239,6 @@ struct mem_cgroup {
>  	 */
>  	bool oom_group;
>  
> -	int swappiness;
> -
>  	/* memory.events and memory.events.local */
>  	struct cgroup_file events_file;
>  	struct cgroup_file events_local_file;
> @@ -318,6 +316,8 @@ struct mem_cgroup {
>  	/* List of events which userspace want to receive */
>  	struct list_head event_list;
>  	spinlock_t event_list_lock;
> +
> +	int swappiness;
>  #endif /* CONFIG_MEMCG_V1 */
>  
>  	struct mem_cgroup_per_node *nodeinfo[];
> diff --git a/include/linux/swap.h b/include/linux/swap.h
> index 696ed01709c2..330a420fd6de 100644
> --- a/include/linux/swap.h
> +++ b/include/linux/swap.h
> @@ -309,7 +309,6 @@ static inline bool lru_cache_disabled(void)
>  }
>  
>  extern unsigned long shrink_all_memory(unsigned long nr_pages);
> -extern int vm_swappiness;
>  long remove_mapping(struct address_space *mapping, struct folio *folio);
>  
>  #if defined(CONFIG_SYSFS) && defined(CONFIG_NUMA)
> @@ -468,25 +467,7 @@ static inline int add_swap_extent(struct swap_info_struct *sis,
>  }
>  #endif /* CONFIG_SWAP */
>  #ifdef CONFIG_MEMCG
> -static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
> -{
> -	/* Cgroup2 doesn't have per-cgroup swappiness */
> -	if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
> -		return READ_ONCE(vm_swappiness);
> -
> -	/* root ? */
> -	if (mem_cgroup_disabled() || mem_cgroup_is_root(memcg))
> -		return READ_ONCE(vm_swappiness);
> -
> -	return READ_ONCE(memcg->swappiness);
> -}
> -
>  void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid);
> -#else
> -static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
> -{
> -	return READ_ONCE(vm_swappiness);
> -}
>  #endif
>  
>  #if defined(CONFIG_SWAP) && defined(CONFIG_MEMCG) && defined(CONFIG_BLK_CGROUP)
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 22f55aeb94f3..d6ba276740ea 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -63,6 +63,7 @@
>  #include <linux/sched/isolation.h>
>  #include <linux/kmemleak.h>
>  #include "internal.h"
> +#include "swap.h"
>  #include "swap_table.h"
>  #include <net/sock.h>
>  #include <net/ip.h>
> @@ -4174,11 +4175,10 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
>  #endif
>  	page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
>  	if (parent) {
> -		WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
> -
>  		page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
>  		page_counter_init(&memcg->swap, &parent->swap, false);
>  #ifdef CONFIG_MEMCG_V1
> +		WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
>  		memcg->memory.track_failcnt = !memcg_on_dfl;
>  		WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable));
>  		page_counter_init(&memcg->kmem, &parent->kmem, false);
> diff --git a/mm/swap.h b/mm/swap.h
> index b51ad3071a73..a0e04b97a466 100644
> --- a/mm/swap.h
> +++ b/mm/swap.h
> @@ -4,6 +4,7 @@
>  
>  #include <linux/atomic.h> /* for atomic_long_t */
>  #include <linux/mm.h> /* for PAGE_SHIFT */
> +#include <linux/memcontrol.h> /* for mem_cgroup_swappiness() */
>  struct mempolicy;
>  struct swap_iocb;
>  struct swap_memcg_table;
> @@ -76,6 +77,25 @@ enum swap_cluster_flags {
>  	CLUSTER_FLAG_MAX,
>  };
>  
> +extern int vm_swappiness;
> +
> +static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg)
> +{
> +#ifdef CONFIG_MEMCG_V1
> +	/* Cgroup2 doesn't have per-cgroup swappiness */
> +	if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
> +		return READ_ONCE(vm_swappiness);
> +
> +	/* root ? */
> +	if (mem_cgroup_disabled() || mem_cgroup_is_root(memcg))
> +		return READ_ONCE(vm_swappiness);
> +
> +	return READ_ONCE(memcg->swappiness);
> +#else
> +	return READ_ONCE(vm_swappiness);
> +#endif
> +}
> +
>  #ifdef CONFIG_SWAP
>  #include <linux/swapops.h> /* for swp_offset */
>  #include <linux/blk_types.h> /* for bio_end_io_t */


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

* Re: [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h
  2026-07-16  3:17 ` [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h Ridong
  2026-07-16  6:06   ` Tao Cui
@ 2026-07-16  7:14   ` Barry Song
  1 sibling, 0 replies; 12+ messages in thread
From: Barry Song @ 2026-07-16  7:14 UTC (permalink / raw)
  To: Ridong
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Shakeel Butt,
	Andrew Morton, Chris Li, Kairui Song, Muchun Song, Kemeng Shi,
	Nhat Pham, Baoquan He, Youngjun Park, David Hildenbrand, Qi Zheng,
	Lorenzo Stoakes, Axel Rasmussen, Yuanchu Xie, Wei Xu,
	Davidlohr Bueso, cgroups, linux-mm, linux-kernel, Ridong Chen

On Thu, Jul 16, 2026 at 11:18 AM Ridong <ridong.chen@linux.dev> wrote:
>
> From: Ridong Chen <chenridong@xiaomi.com>
>
> The per-memcg swappiness knob is v1-only; v2 always uses global
> vm_swappiness and ignores the per-cgroup field.
>
> Both mem_cgroup_swappiness() and vm_swappiness are only used within
> mm/ (memcontrol.c, memcontrol-v1.c, vmscan.c), so move them out of the
> public include/linux/swap.h into the mm-private mm/swap.h. This keeps
> unrelated declarations out of include/linux/swap.h.
>
> Guard memcg->swappiness with CONFIG_MEMCG_V1 as well, so v2-only
> kernels drop the unused field.
>
> No functional change for v1; v2-only kernels drop the unused field.
>
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> ---

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

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

* Re: [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  3:58   ` Qi Zheng
@ 2026-07-16  8:21     ` Ridong Chen
  2026-07-16  8:42       ` Qi Zheng
  0 siblings, 1 reply; 12+ messages in thread
From: Ridong Chen @ 2026-07-16  8:21 UTC (permalink / raw)
  To: Qi Zheng, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Lorenzo Stoakes, Axel Rasmussen,
	Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups, linux-mm,
	linux-kernel, Ridong Chen, stable



On 7/16/2026 11:58 AM, Qi Zheng wrote:
> Hi Ridong,
> 
> On 7/16/26 11:17 AM, Ridong wrote:
>> From: Ridong Chen <chenridong@xiaomi.com>
>>
>> sc_swappiness() had two separate definitions depending on
>> CONFIG_MEMCG. The !CONFIG_MEMCG variant simply returned
>> vm_swappiness, ignoring the proactive_swappiness value passed
>> through scan_control. This caused the swappiness parameter
>> written to /sys/devices/system/node/nodeX/reclaim to have no
>> effect when CONFIG_MEMCG is disabled.
>>
>> Fix this by consolidating sc_swappiness() into a single definition
>> that checks sc->proactive_swappiness first, then falls back to
>> mem_cgroup_swappiness() which already handles both CONFIG_MEMCG
>> and !CONFIG_MEMCG.
>>
>> Before fix (swappiness=max ignored, mostly file pages reclaimed):
>>
>>      # cat /proc/sys/vm/swappiness
>>      60
>>      # cat /proc/vmstat | grep pgsteal
>>      pgsteal_kswapd 0
>>      pgsteal_direct 0
>>      pgsteal_khugepaged 0
>>      pgsteal_proactive 1840
>>      pgsteal_anon 25
>>      pgsteal_file 1815
>>      # echo "64M swappiness=max" > /sys/devices/system/node/node0/reclaim
>>      # cat /proc/vmstat | grep pgsteal
>>      pgsteal_kswapd 0
>>      pgsteal_direct 0
>>      pgsteal_khugepaged 0
>>      pgsteal_proactive 18013
>>      pgsteal_anon 337
>>      pgsteal_file 17676
>>
>> After fix (swappiness=max honored, anon pages reclaimed as expected):
>>
> 
> By the way, in get_scan_count(), it seems we still reclaim file pages
> without swap space, even if swappiness=max (SWAPPINESS_ANON_ONLY) is
> set.
> 
> This behavior appears to contradict the semantics of
> SWAPPINESS_ANON_ONLY, which probably needs a fix as well.
> 
Thanks, Qi.

You're right. Currently, when can_reclaim_anon_pages() returns false, 
the reclaim logic falls back to scanning file folios even if swappiness 
== SWAPPINESS_ANON_ONLY is set:

```
static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
			   unsigned long *nr)
{
	....
	/* 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;
		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;
	}
	...
}
```

The !can_reclaim_anon_pages() check above takes precedence over the 
SWAPPINESS_ANON_ONLY branch, making the latter effectively unreachable 
when anonymous pages are deemed non-reclaimable.

To fix this, could we move the SWAPPINESS_ANON_ONLY check up before the 
can_reclaim_anon_pages() check?

```
static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
			   unsigned long *nr)
{
	...
	/* 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;
	}
	....
	/* 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;
		goto out;
	}

	...
}
```

-- 
Best regards
Ridong


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

* Re: [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  6:06   ` Tao Cui
@ 2026-07-16  8:23     ` Ridong Chen
  0 siblings, 0 replies; 12+ messages in thread
From: Ridong Chen @ 2026-07-16  8:23 UTC (permalink / raw)
  To: Tao Cui, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Qi Zheng, Lorenzo Stoakes,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups,
	linux-mm, linux-kernel, Ridong Chen, stable



On 7/16/2026 2:06 PM, Tao Cui wrote:
> Hi Ridong,
> 
> Reproduced the bug and verified the fix in a QEMU x86_64 guest with
> CONFIG_MEMCG disabled. Global vm.swappiness pinned to 1, then:
> 
>      echo "64M swappiness=<x>" > /sys/devices/system/node/node0/reclaim
> 
>    swappiness= | buggy (anon/file)   | patched (anon/file)
>    ----------- | -------------------- | --------------------
>    max         |      0  /  16385     |  16292  /  0
>    200         |      2  /  16384     |  16837  /  0
>    1           |      0  /  16386     |      0  /  16467
> 
> Unpatched ignores the swappiness arg (always file); patched honors it.
> Matches the commit message.
> 
> Tested-by: Song Hu <husong@kylinos.cn>
> Reviewed-by: Song Hu <husong@kylinos.cn>
> 
Thanks.

-- 
Best regards
Ridong


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

* Re: [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  8:21     ` Ridong Chen
@ 2026-07-16  8:42       ` Qi Zheng
  2026-07-16 11:17         ` Ridong Chen
  0 siblings, 1 reply; 12+ messages in thread
From: Qi Zheng @ 2026-07-16  8:42 UTC (permalink / raw)
  To: Ridong Chen, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Lorenzo Stoakes, Axel Rasmussen,
	Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups, linux-mm,
	linux-kernel, Ridong Chen, stable



On 7/16/26 4:21 PM, Ridong Chen wrote:
> 
> 
> On 7/16/2026 11:58 AM, Qi Zheng wrote:
>> Hi Ridong,
>>
>> On 7/16/26 11:17 AM, Ridong wrote:
>>> From: Ridong Chen <chenridong@xiaomi.com>
>>>
>>> sc_swappiness() had two separate definitions depending on
>>> CONFIG_MEMCG. The !CONFIG_MEMCG variant simply returned
>>> vm_swappiness, ignoring the proactive_swappiness value passed
>>> through scan_control. This caused the swappiness parameter
>>> written to /sys/devices/system/node/nodeX/reclaim to have no
>>> effect when CONFIG_MEMCG is disabled.
>>>
>>> Fix this by consolidating sc_swappiness() into a single definition
>>> that checks sc->proactive_swappiness first, then falls back to
>>> mem_cgroup_swappiness() which already handles both CONFIG_MEMCG
>>> and !CONFIG_MEMCG.
>>>
>>> Before fix (swappiness=max ignored, mostly file pages reclaimed):
>>>
>>>      # cat /proc/sys/vm/swappiness
>>>      60
>>>      # cat /proc/vmstat | grep pgsteal
>>>      pgsteal_kswapd 0
>>>      pgsteal_direct 0
>>>      pgsteal_khugepaged 0
>>>      pgsteal_proactive 1840
>>>      pgsteal_anon 25
>>>      pgsteal_file 1815
>>>      # echo "64M swappiness=max" > /sys/devices/system/node/node0/ 
>>> reclaim
>>>      # cat /proc/vmstat | grep pgsteal
>>>      pgsteal_kswapd 0
>>>      pgsteal_direct 0
>>>      pgsteal_khugepaged 0
>>>      pgsteal_proactive 18013
>>>      pgsteal_anon 337
>>>      pgsteal_file 17676
>>>
>>> After fix (swappiness=max honored, anon pages reclaimed as expected):
>>>
>>
>> By the way, in get_scan_count(), it seems we still reclaim file pages
>> without swap space, even if swappiness=max (SWAPPINESS_ANON_ONLY) is
>> set.
>>
>> This behavior appears to contradict the semantics of
>> SWAPPINESS_ANON_ONLY, which probably needs a fix as well.
>>
> Thanks, Qi.
> 
> You're right. Currently, when can_reclaim_anon_pages() returns false, 
> the reclaim logic falls back to scanning file folios even if swappiness 
> == SWAPPINESS_ANON_ONLY is set:
> 
> ```
> static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
>                 unsigned long *nr)
> {
>      ....
>      /* 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;
>          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;
>      }
>      ...
> }
> ```
> 
> The !can_reclaim_anon_pages() check above takes precedence over the 
> SWAPPINESS_ANON_ONLY branch, making the latter effectively unreachable 
> when anonymous pages are deemed non-reclaimable.
> 
> To fix this, could we move the SWAPPINESS_ANON_ONLY check up before the 
> can_reclaim_anon_pages() check?
> 
> ```
> static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
>                 unsigned long *nr)
> {
>      ...
>      /* 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;

For SWAPPINESS_ANON_ONLY + !can_reclaim_anon_pages, perhaps we should
just return directly instead of goto out, since no pages can be
reclaimed in this situation.

Thanks,
Qi

>      }
>      ....
>      /* 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;
>          goto out;
>      }
> 
>      ...
> }
> ```
> 


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

* Re: [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter
  2026-07-16  8:42       ` Qi Zheng
@ 2026-07-16 11:17         ` Ridong Chen
  0 siblings, 0 replies; 12+ messages in thread
From: Ridong Chen @ 2026-07-16 11:17 UTC (permalink / raw)
  To: Qi Zheng, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Shakeel Butt, Andrew Morton, Chris Li, Kairui Song
  Cc: Muchun Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Youngjun Park, David Hildenbrand, Lorenzo Stoakes, Axel Rasmussen,
	Yuanchu Xie, Wei Xu, Davidlohr Bueso, cgroups, linux-mm,
	linux-kernel, Ridong Chen, stable



On 7/16/2026 4:42 PM, Qi Zheng wrote:
> 
> 
> On 7/16/26 4:21 PM, Ridong Chen wrote:
>>
>>
>> On 7/16/2026 11:58 AM, Qi Zheng wrote:
>>> Hi Ridong,
>>>
>>> On 7/16/26 11:17 AM, Ridong wrote:
>>>> From: Ridong Chen <chenridong@xiaomi.com>
>>>>
>>>> sc_swappiness() had two separate definitions depending on
>>>> CONFIG_MEMCG. The !CONFIG_MEMCG variant simply returned
>>>> vm_swappiness, ignoring the proactive_swappiness value passed
>>>> through scan_control. This caused the swappiness parameter
>>>> written to /sys/devices/system/node/nodeX/reclaim to have no
>>>> effect when CONFIG_MEMCG is disabled.
>>>>
>>>> Fix this by consolidating sc_swappiness() into a single definition
>>>> that checks sc->proactive_swappiness first, then falls back to
>>>> mem_cgroup_swappiness() which already handles both CONFIG_MEMCG
>>>> and !CONFIG_MEMCG.
>>>>
>>>> Before fix (swappiness=max ignored, mostly file pages reclaimed):
>>>>
>>>>      # cat /proc/sys/vm/swappiness
>>>>      60
>>>>      # cat /proc/vmstat | grep pgsteal
>>>>      pgsteal_kswapd 0
>>>>      pgsteal_direct 0
>>>>      pgsteal_khugepaged 0
>>>>      pgsteal_proactive 1840
>>>>      pgsteal_anon 25
>>>>      pgsteal_file 1815
>>>>      # echo "64M swappiness=max" > /sys/devices/system/node/node0/ 
>>>> reclaim
>>>>      # cat /proc/vmstat | grep pgsteal
>>>>      pgsteal_kswapd 0
>>>>      pgsteal_direct 0
>>>>      pgsteal_khugepaged 0
>>>>      pgsteal_proactive 18013
>>>>      pgsteal_anon 337
>>>>      pgsteal_file 17676
>>>>
>>>> After fix (swappiness=max honored, anon pages reclaimed as expected):
>>>>
>>>
>>> By the way, in get_scan_count(), it seems we still reclaim file pages
>>> without swap space, even if swappiness=max (SWAPPINESS_ANON_ONLY) is
>>> set.
>>>
>>> This behavior appears to contradict the semantics of
>>> SWAPPINESS_ANON_ONLY, which probably needs a fix as well.
>>>
>> Thanks, Qi.
>>
>> You're right. Currently, when can_reclaim_anon_pages() returns false, 
>> the reclaim logic falls back to scanning file folios even if 
>> swappiness == SWAPPINESS_ANON_ONLY is set:
>>
>> ```
>> static void get_scan_count(struct lruvec *lruvec, struct scan_control 
>> *sc,
>>                 unsigned long *nr)
>> {
>>      ....
>>      /* 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;
>>          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;
>>      }
>>      ...
>> }
>> ```
>>
>> The !can_reclaim_anon_pages() check above takes precedence over the 
>> SWAPPINESS_ANON_ONLY branch, making the latter effectively unreachable 
>> when anonymous pages are deemed non-reclaimable.
>>
>> To fix this, could we move the SWAPPINESS_ANON_ONLY check up before 
>> the can_reclaim_anon_pages() check?
>>
>> ```
>> static void get_scan_count(struct lruvec *lruvec, struct scan_control 
>> *sc,
>>                 unsigned long *nr)
>> {
>>      ...
>>      /* 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;
> 
> For SWAPPINESS_ANON_ONLY + !can_reclaim_anon_pages, perhaps we should
> just return directly instead of goto out, since no pages can be
> reclaimed in this situation.
> 

Much better.
Would you mind if I send a separate patch to fix this issue?

> 
>>      }
>>      ....
>>      /* 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;
>>          goto out;
>>      }
>>
>>      ...
>> }
>> ```
>>
> 

-- 
Best regards
Ridong


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

end of thread, other threads:[~2026-07-16 11:17 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16  3:17 [PATCH v2 0/2] mm: fix node reclaim swappiness handling Ridong
2026-07-16  3:17 ` [PATCH v2 1/2] memcg: move mem_cgroup_swappiness and vm_swappiness to mm/swap.h Ridong
2026-07-16  6:06   ` Tao Cui
2026-07-16  7:14   ` Barry Song
2026-07-16  3:17 ` [PATCH v2 2/2] mm: vmscan: fix node reclaim ignoring swappiness parameter Ridong
2026-07-16  3:25   ` Qi Zheng
2026-07-16  3:58   ` Qi Zheng
2026-07-16  8:21     ` Ridong Chen
2026-07-16  8:42       ` Qi Zheng
2026-07-16 11:17         ` Ridong Chen
2026-07-16  6:06   ` Tao Cui
2026-07-16  8:23     ` Ridong Chen

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