Re: [PATCH v3 2/2] mm/mglru: fix ineffective memory protection for non-kswapd reclaim
From: Johannes Weiner
Date: Thu Sep 03 2026 - 10:19:55 EST
On Thu, Sep 03, 2026 at 11:19:52AM +0800, Ridong Chen wrote:
> From: Ridong Chen <chenridong@xxxxxxxxxx>
>
> For MGLRU, memory.min/low is not honored during global proactive reclaim
> (writing to the root memory.reclaim) and global direct reclaim, because
> these paths shrink memcgs using stale or effective protection (emin/elow).
> It can be reproduced as follows:
>
> # echo 7 > /sys/kernel/mm/lru_gen/enabled
> # cd /sys/fs/cgroup
> # mkdir -p a/b
> # echo 100M > a/memory.min
> # echo +memory > a/cgroup.subtree_control
> # echo 100M > a/b/memory.min
> # echo $$ > a/b/cgroup.procs
> # dd if=/dev/zero of=/tmp/testfile bs=1M count=200
> # cat a/b/memory.current
> 222650368
> # echo 500M > memory.reclaim
> -bash: echo: write error: Resource temporarily unavailable
> # cat a/b/memory.current
> 6070272
>
> memory.min is 100M, yet reclaim drops a/b down to 6M, breaking the
> protection. The traditional LRU path is not affected because
> shrink_node() calls mem_cgroup_calculate_protection() for each memcg it
> visits during a top-down tree walk.
>
> Commit 30d77b7eef01 ("mm/mglru: fix ineffective protection calculation")
> moved the protection computation into lru_gen_age_node(), which only
> runs for kswapd. Non-kswapd global reclaim reaches shrink_one() through
> lru_gen_shrink_node() -> shrink_many() without any protection
> computation, so emin/elow are whatever a previous kswapd run left behind
> - or zero if kswapd never ran on this node. Relying on a prior kswapd
> pass is not correct either: a memcg's emin/elow are derived from its
> ancestors' memory.min/low settings and from children_min_usage, both of
> which change over time, so emin/elow go stale even after kswapd has run
> and must be recomputed at the point of reclaim.
>
> Introduce mem_cgroup_calculate_protection_path() which computes emin/elow
> along the root-to-target path only, by iterating through the cgroup
> ancestors array top-down. This avoids the full tree traversal that would
> be needed with mem_cgroup_calculate_protection(), limiting the cost to
> O(depth) per memcg - typically 3-5 levels.
>
> Call it from shrink_one() for the non-kswapd path so that each memcg
> about to be shrunk has correct protection values.
>
> Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Ridong Chen <chenridong@xxxxxxxxxx>
With Barry's two points of feedback (changelog and comment),
Reviewed-by: Johannes Weiner <hannes@xxxxxxxxxxx>