From nobody Fri Jul 24 05:21:23 2026 Received: from out-184.mta0.migadu.com (out-184.mta0.migadu.com [91.218.175.184]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4D450471423 for ; Thu, 23 Jul 2026 13:06:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784811996; cv=none; b=DFXaWLKajE0gguo4noP3HaKyY0C6CkoIuwF4vZvyKITHcqmAsVUBVlrHJE191BTkbHi+tNmJ4XUraGyj1hAp0fpx/J0gpY+GldRAQXlgTuCEk8gWGBiNKaMURQcL9ILdrUeHLh+aoAXprBksJuZPMHYCYcsI1q204k89GKHkub8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784811996; c=relaxed/simple; bh=8tIuI45njjKHWCFy6rpVBXkW0TUiJ/2otNPP7XwvcjI=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=Z0MYjvmuFFJ1DPTh2cafESKQdNaREWfemELPkrWh6QWn6rw3AEvwJtwMw/g542Rjz0fkLsybc5II/PWLqi7BmqaX44qu+VyKF5eL6UaTl37IFRE8YhS6iuUAMDS/CkZ34pzAI+NdsCAw007ZxhCPk1rjhK/YmEagIXt5t7TgoAc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=aCG4fDtB; arc=none smtp.client-ip=91.218.175.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="aCG4fDtB" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784811986; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=MGfalNcUUGb9oiqhuCNvSsUVOzxB2Lc/6ZPh2Rebisw=; b=aCG4fDtBd6UAAUlJI4VcXesHNuPmOjIK9ENyYlBK85L6PNfcKzazsg+firV76xk99jhzdk kpd5JqcodSOF99HLOzVZnj2G9CdMKTNatWWQuJWXEQWhIfVXWaqSDvtBSG3yW1bOHTrXNy pW5YRBPgXrbfEvpaNt907qVWGXzDbzI= From: Ridong To: Andrew Morton , Johannes Weiner Cc: David Hildenbrand , Michal Hocko , Qi Zheng , Shakeel Butt , Lorenzo Stoakes , Kairui Song , Barry Song , Axel Rasmussen , Yuanchu Xie , Wei Xu , Yu Zhao , linux-mm@kvack.org, linux-kernel@vger.kernel.org, Ridong Chen , Ridong Chen Subject: [PATCH] mm/mglru: fix memcg protection for global proactive reclaim Date: Thu, 23 Jul 2026 21:05:59 +0800 Message-Id: <20260723130559.2343690-1-ridong.chen@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Ridong Chen memory.min/low is silently bypassed for MGLRU during global proactive reclaim (writing to the root memory.reclaim). 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=3D/dev/zero of=3D/tmp/testfile bs=3D1M count=3D200 # 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 is not affected. Commit 30d77b7eef01 ("mm/mglru: fix ineffective protection calculation") computes the memcg protection in lru_gen_age_node(), which only runs for kswapd. Global proactive reclaim reaches shrink_one() through lru_gen_shrink_node() without any aging step, so emin/elow is not couputed, Factor the tree traversal out into update_memcg_protection() and call it from lru_gen_shrink_node() for the non-kswapd path, so the protection is computed before shrinking. kswapd keeps computing it in lru_gen_age_node(), which also needs it for the min_ttl OOM check. Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists") Signed-off-by: Ridong Chen --- mm/vmscan.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 06e103f9781e..dc3549583e97 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4122,6 +4122,16 @@ static bool lruvec_is_reclaimable(struct lruvec *lru= vec, struct scan_control *sc return time_is_before_jiffies(birth + min_ttl); } =20 +static void update_memcg_protection(void) +{ + struct mem_cgroup *memcg; + + memcg =3D mem_cgroup_iter(NULL, NULL, NULL); + do { + mem_cgroup_calculate_protection(NULL, memcg); + } while ((memcg =3D mem_cgroup_iter(NULL, memcg, NULL))); +} + /* to protect the working set of the last N jiffies */ static unsigned long lru_gen_min_ttl __read_mostly; =20 @@ -4135,15 +4145,19 @@ static void lru_gen_age_node(struct pglist_data *pg= dat, struct scan_control *sc) =20 set_initial_priority(pgdat, sc); =20 - memcg =3D mem_cgroup_iter(NULL, NULL, NULL); - do { - struct lruvec *lruvec =3D mem_cgroup_lruvec(memcg, pgdat); + update_memcg_protection(); =20 - mem_cgroup_calculate_protection(NULL, memcg); + if (min_ttl) { + memcg =3D mem_cgroup_iter(NULL, NULL, NULL); + do { + struct lruvec *lruvec =3D mem_cgroup_lruvec(memcg, pgdat); =20 - if (!reclaimable) - reclaimable =3D lruvec_is_reclaimable(lruvec, sc, min_ttl); - } while ((memcg =3D mem_cgroup_iter(NULL, memcg, NULL))); + if (lruvec_is_reclaimable(lruvec, sc, min_ttl)) { + reclaimable =3D true; + break; + } + } while ((memcg =3D mem_cgroup_iter(NULL, memcg, NULL))); + } =20 /* * The main goal is to OOM kill if every generation from all memcgs is @@ -5004,7 +5018,7 @@ static int shrink_one(struct lruvec *lruvec, struct s= can_control *sc) struct mem_cgroup *memcg =3D lruvec_memcg(lruvec); struct pglist_data *pgdat =3D lruvec_pgdat(lruvec); =20 - /* lru_gen_age_node() called mem_cgroup_calculate_protection() */ + /* update_memcg_protection() computed the protection */ if (mem_cgroup_below_min(NULL, memcg)) return MEMCG_LRU_YOUNG; =20 @@ -5151,8 +5165,11 @@ static void lru_gen_shrink_node(struct pglist_data *= pgdat, struct scan_control * =20 set_initial_priority(pgdat, sc); =20 + /* kswapd called update_memcg_protection in lru_gen_age_node */ if (current_is_kswapd()) sc->nr_reclaimed =3D 0; + else + update_memcg_protection(); =20 if (mem_cgroup_disabled()) shrink_one(&pgdat->__lruvec, sc); --=20 2.34.1