From nobody Sat Jul 25 19:29:02 2026 Received: from mta20.hihonor.com (mta20.honor.com [81.70.206.69]) (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 D2AD53BED42; Tue, 14 Jul 2026 12:15:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.70.206.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784031343; cv=none; b=dZ47tgDSKHTdUA0MzOobAJ/vBlkVPFPj574mJXxDR/2EJ/mYDTC+Md/LpZMLilH4uDHXheJllryXZh0cAy5l+8KVAX+zW+ikb+qrKpkgnRtbN0bmahFIrf7GLwnqCROvI85ichzEFLq57P9s0f+KupQyGUNBimWIWRCvnqOirYQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784031343; c=relaxed/simple; bh=K5trL+Ani1qoBuWhLNtvjSFzEzNw+uYS2pyP1NmYRrI=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pjSi4jXPtSfpw/F3rokhaWv80n6Qoj+qOrrNrWQWfDlO+pbrSDLgcN66jk8N3riVqJIF1SRPkLK04t9jX639J/FaWwGHdC+JyRpXiX2Hgf44Q1IY6voeyo6Dp5qMuws8prJ0rTqXqzRAJhXbac1OISNe+GGQUmAk87Ci2zGdSBA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=honor.com; spf=pass smtp.mailfrom=honor.com; arc=none smtp.client-ip=81.70.206.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=honor.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=honor.com Received: from TW003.hihonor.com (unknown [10.77.199.161]) by mta20.hihonor.com (SkyGuard) with ESMTPS id 4gzytp3XY4zYlLgc; Tue, 14 Jul 2026 20:15:30 +0800 (CST) Received: from TA012-1.hihonor.com (10.77.199.132) by TW003.hihonor.com (10.77.199.161) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.43; Tue, 14 Jul 2026 20:15:28 +0800 Received: from localhost.localdomain (10.144.5.36) by TA012-1.hihonor.com (10.77.199.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.17; Tue, 14 Jul 2026 20:15:19 +0800 From: Zicheng Wang To: , CC: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , Zicheng Wang Subject: [RFC v2 1/3] mm/lru_gen: add AGING counter and proactive aging helper Date: Tue, 14 Jul 2026 20:15:27 +0800 Message-ID: <20260714121529.2237261-2-wangzicheng@honor.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260714121529.2237261-1-wangzicheng@honor.com> References: <20260714121529.2237261-1-wangzicheng@honor.com> 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-ClientProxiedBy: TW004-1.hihonor.com (10.77.232.85) To TA012-1.hihonor.com (10.77.199.132) Content-Type: text/plain; charset="utf-8" Add the two pieces the rest of the series builds on: an AGING node stat counter (the observation an aging policy pairs with workingset_refault to spot over-aging) and a reusable helper, lru_gen_age_memcg(), that ages a memcg by advancing max_seq without reclaim (the control primitive memory.aging wraps). The counter counts every pass in inc_max_seq(), regardless of caller. The helper mirrors lru_gen_seq_write()'s reclaim_state / memalloc_noreclaim / mm_walk setup; swappiness comes from get_swappiness() so anon is not rotated without swap, and a racing lruvec is skipped (-EAGAIN) without blocking other nodes. Gated by CONFIG_LRU_GEN. No user-visible change yet. Signed-off-by: Zicheng Wang --- include/linux/mmzone.h | 3 ++ mm/internal.h | 4 +++ mm/memcontrol.c | 9 +++++ mm/vmscan.c | 80 ++++++++++++++++++++++++++++++++++++++++++ mm/vmstat.c | 3 ++ 5 files changed, 99 insertions(+) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 9adb2ad21da5..ab5137c272ca 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -323,6 +323,9 @@ enum node_stat_item { PGSCAN_ANON, PGSCAN_FILE, PGREFILL, +#ifdef CONFIG_LRU_GEN + AGING, +#endif #ifdef CONFIG_HUGETLB_PAGE NR_HUGETLB, #endif diff --git a/mm/internal.h b/mm/internal.h index 5a2ddcf68e0b..96add6cd4627 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -634,6 +634,10 @@ extern void reclaim_throttle(pg_data_t *pgdat, enum vm= scan_throttle_state reason int user_proactive_reclaim(char *buf, struct mem_cgroup *memcg, pg_data_t *pgdat); =20 +#ifdef CONFIG_LRU_GEN +int lru_gen_age_memcg(struct mem_cgroup *memcg, unsigned long nr_gens); +#endif + /* * in mm/rmap.c: */ diff --git a/mm/memcontrol.c b/mm/memcontrol.c index d978e18b9b2d..e42f97e004b6 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -420,6 +420,9 @@ static const unsigned int memcg_node_stat_items[] =3D { PGSCAN_ANON, PGSCAN_FILE, PGREFILL, +#ifdef CONFIG_LRU_GEN + AGING, +#endif #ifdef CONFIG_HUGETLB_PAGE NR_HUGETLB, #endif @@ -1606,6 +1609,9 @@ static const struct memory_stat memory_stats[] =3D { #ifdef CONFIG_NUMA_BALANCING { "pgpromote_success", PGPROMOTE_SUCCESS }, #endif +#ifdef CONFIG_LRU_GEN + { "aging", AGING }, +#endif }; =20 /* The actual unit of the state item, not the same as the output unit */ @@ -1655,6 +1661,9 @@ static int memcg_page_state_output_unit(int item) case PGSCAN_KHUGEPAGED: case PGSCAN_PROACTIVE: case PGREFILL: +#ifdef CONFIG_LRU_GEN + case AGING: +#endif #ifdef CONFIG_NUMA_BALANCING case PGPROMOTE_SUCCESS: #endif diff --git a/mm/vmscan.c b/mm/vmscan.c index b3e555561417..805e29c499c8 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3987,6 +3987,9 @@ static bool inc_max_seq(struct lruvec *lruvec, unsign= ed long seq, int swappiness WRITE_ONCE(lrugen->timestamps[next], jiffies); /* make sure preceding modifications appear */ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); + + /* Count every aging pass for over-aging diagnosis. */ + mod_lruvec_state(lruvec, AGING, 1); unlock: lruvec_unlock_irq(lruvec); =20 @@ -5855,6 +5858,83 @@ static int __init init_lru_gen(void) }; late_initcall(init_lru_gen); =20 +/** + * lru_gen_age_memcg - Proactively age a memcg by N generations + * @memcg: target memcg + * @nr_gens: number of generations to age (1..MAX_NR_GENS) + * + * Advances max_seq without page reclaim on all nodes of @memcg, using + * the same lock/walk protocol as lru_gen_seq_write(). Swappiness comes + * from get_swappiness() so anon rotation is skipped without swap. + * to the next node. + * + * Return: 0 on success, -EINVAL on bad input, -EAGAIN if a lruvec did + * not advance, -ENOMEM on mm_walk failure, -EINTR on signal. + */ +int lru_gen_age_memcg(struct mem_cgroup *memcg, unsigned long nr_gens) +{ + struct scan_control sc =3D { + .may_writepage =3D true, + .may_unmap =3D true, + .may_swap =3D true, + .reclaim_idx =3D MAX_NR_ZONES - 1, + .gfp_mask =3D GFP_KERNEL, + .proactive =3D true, + }; + int nid, swappiness; + unsigned long i; + int ret =3D 0; + unsigned int flags; + struct blk_plug plug; + struct lruvec *lruvec; + + if (!memcg) + return -EINVAL; + + if (nr_gens =3D=3D 0 || nr_gens > MAX_NR_GENS) + return -EINVAL; + + set_task_reclaim_state(current, &sc.reclaim_state); + flags =3D memalloc_noreclaim_save(); + blk_start_plug(&plug); + if (!set_mm_walk(NULL, true)) { + ret =3D -ENOMEM; + goto done; + } + + for_each_node_state(nid, N_MEMORY) { + if (signal_pending(current)) { + ret =3D -EINTR; + goto done; + } + + lruvec =3D get_lruvec(memcg, nid); + if (!lruvec) + continue; + + swappiness =3D get_swappiness(lruvec, &sc); + + for (i =3D 0; i < nr_gens; i++) { + DEFINE_MAX_SEQ(lruvec); + + if (!try_to_inc_max_seq(lruvec, max_seq, + swappiness, false)) { + ret =3D -EAGAIN; + break; + } + } + + cond_resched(); + } + +done: + clear_mm_walk(); + blk_finish_plug(&plug); + memalloc_noreclaim_restore(flags); + set_task_reclaim_state(current, NULL); + return ret; +} + #else /* !CONFIG_LRU_GEN */ =20 static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_contro= l *sc) diff --git a/mm/vmstat.c b/mm/vmstat.c index f534972f517d..78ea093428c7 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1290,6 +1290,9 @@ const char * const vmstat_text[] =3D { [I(PGSCAN_ANON)] =3D "pgscan_anon", [I(PGSCAN_FILE)] =3D "pgscan_file", [I(PGREFILL)] =3D "pgrefill", +#ifdef CONFIG_LRU_GEN + [I(AGING)] =3D "aging", +#endif #ifdef CONFIG_HUGETLB_PAGE [I(NR_HUGETLB)] =3D "nr_hugetlb", #endif --=20 2.25.1 From nobody Sat Jul 25 19:29:02 2026 Received: from mta22.hihonor.com (mta22.honor.com [81.70.192.198]) (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 1C69E3BE154; Tue, 14 Jul 2026 12:15:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.70.192.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784031347; cv=none; b=i0/IV+jgWejmBGtS58lT5wIYgfu21+rCwmsm4KvttRS9hRnraSwRNICrI0KBYNkXpINoceFylpxi+2DWR1d7zFlF+wZ4XWSAsqMB0peGyP6k5W0YjmL8gw3JW1fXgPZWs0MJiviEPxkab4sIRXUrST/Pq/7DOlo79PT9r9SoZZ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784031347; c=relaxed/simple; bh=LONzgChPkSvGUxgkSO88PSwlJValMfRNHzxGIgyGodY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=esAWHf7UTXzFLKWl6UFKtDWEtEwsHFFOpKfjmYfERWzr34aZPku3dsX9gKMC9fX0XlpfeLy7PMw8K3iQKXoX8lfQogPIcFiwqavQAHcuQrOKEmJIi0uXfpIFxe/Mgb4Tm64djEkJ9wt+j2Oi9ryNS4RYIuoHSkQWxb1hcnh5GWY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=honor.com; spf=pass smtp.mailfrom=honor.com; dkim=pass (1024-bit key) header.d=honor.com header.i=@honor.com header.b=kzZC2sC5; arc=none smtp.client-ip=81.70.192.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=honor.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=honor.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=honor.com header.i=@honor.com header.b="kzZC2sC5" dkim-signature: v=1; a=rsa-sha256; d=honor.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=To:From; bh=PTW4Mp0JJ6AVsiV1oNONd40biUPQE43rVLpkGa6ZhVA=; b=kzZC2sC5NeUj3xvj/4OmiYy35YCivn0Y0FA7/mkxY+i64JpI3DVxZXqTIlyqc0B6RVQqXaanh Ne3KgVhtuzaClsPDeY29BxbpRQ83UTtXsJrUJv4awWjVWpMBroPlhjX/5U0S2HahHPlK+PoBVQn nF+PLe+DjkVykBypjWQ+CWQ= Received: from TW001.hihonor.com (unknown [10.77.229.151]) by mta22.hihonor.com (SkyGuard) with ESMTPS id 4gzytp6Dl2zYl0L8; Tue, 14 Jul 2026 20:15:30 +0800 (CST) Received: from TA012-1.hihonor.com (10.77.199.132) by TW001.hihonor.com (10.77.229.151) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.43; Tue, 14 Jul 2026 20:15:30 +0800 Received: from localhost.localdomain (10.144.5.36) by TA012-1.hihonor.com (10.77.199.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.17; Tue, 14 Jul 2026 20:15:20 +0800 From: Zicheng Wang To: , CC: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , Zicheng Wang Subject: [RFC v2 2/3] mm: memcontrol: add memory.aging cgroup v2 file Date: Tue, 14 Jul 2026 20:15:28 +0800 Message-ID: <20260714121529.2237261-3-wangzicheng@honor.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260714121529.2237261-1-wangzicheng@honor.com> References: <20260714121529.2237261-1-wangzicheng@honor.com> 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-ClientProxiedBy: TW004-1.hihonor.com (10.77.232.85) To TA012-1.hihonor.com (10.77.199.132) Content-Type: text/plain; charset="utf-8" Add a write-only memory.aging that triggers MGLRU aging (max_seq advancement) without reclaim - the aging counterpart of memory.reclaim. Userspace ages to rebalance the anon/file distribution across generations, then reclaims. This is the control half of the loop; the observability it needs lands in the next patch. Useful for workloads such as the Android app lifecycle, where anon concentrates in the 2nd-youngest generation while file fills the oldest two and gets over-reclaimed. echo 2 > /sys/fs/cgroup/foo/memory.aging echo "100M" > /sys/fs/cgroup/foo/memory.reclaim Gated by CONFIG_LRU_GEN. Concurrent writers race on the lruvec lock; a caller that did not advance the window gets -EAGAIN. Signed-off-by: Zicheng Wang --- Documentation/admin-guide/cgroup-v2.rst | 35 +++++++++++++++++++ Documentation/admin-guide/mm/multigen_lru.rst | 13 +++++++ mm/memcontrol.c | 30 ++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-= guide/cgroup-v2.rst index 6efd0095ed99..f42ac6dddbf6 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -1437,6 +1437,41 @@ The following nested keys are defined. The valid range for swappiness is [0-200, max], setting swappiness=3Dmax exclusively reclaims anonymous memory. =20 + memory.aging + A write-only file which exists for all cgroups when + CONFIG_LRU_GEN is enabled. + + This interface triggers MGLRU aging (generation advancement) + without page reclaim. It is useful for proactively rebalancing + the distribution of pages across generations before invoking + memory.reclaim, particularly for workloads (such as the Android + application lifecycle) where anonymous pages concentrate in + the youngest generations while file pages fill up the oldest + generation and get over-reclaimed. + + The single argument is the number of generations to age, in + the range [1, MAX_NR_GENS]. MAX_NR_GENS is currently 4. + + Example:: + + echo 2 > memory.aging + + Each aging pass advances both anonymous and file generations + simultaneously, matching MGLRU's max_seq semantics. There is + no way to age only one type - this is a property of MGLRU, + not of this interface. + + Proactive aging should be followed by memory.reclaim to + actually free pages:: + + echo 2 > /sys/fs/cgroup/foo/memory.aging + echo "100M" > /sys/fs/cgroup/foo/memory.reclaim + + Returns -EAGAIN if this call did not advance the generation + window (typically because a concurrent caller on the same + cgroup got there first). Returns -EINVAL if the argument is + missing, zero, or out of range. + memory.peak A read-write single value file which exists on non-root cgroups. =20 diff --git a/Documentation/admin-guide/mm/multigen_lru.rst b/Documentation/= admin-guide/mm/multigen_lru.rst index 9cb54b4ff5d9..fd8b4286014e 100644 --- a/Documentation/admin-guide/mm/multigen_lru.rst +++ b/Documentation/admin-guide/mm/multigen_lru.rst @@ -161,3 +161,16 @@ cold pages because of the overestimation, it retries o= n the next server according to the ranking result obtained from the working set estimation step. This less forceful approach limits the impacts on the existing jobs. + +Proactive aging +--------------- +For workloads where it is useful to rebalance the distribution of +anonymous and file pages across generations before reclaiming (for +example, the Android application lifecycle, where an application +that has just been backgrounded has most of its anonymous pages +clustered in the youngest generations while file pages dominate +the oldest), the ``memory.aging`` cgroup v2 file can be used to +advance the generation counter without eviction. The file returns +-EAGAIN if no advance happened, so callers can correlate with the +``aging`` counter to verify how many passes actually took +effect. See Documentation/admin-guide/cgroup-v2.rst. diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e42f97e004b6..c6a969b56878 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4896,6 +4896,29 @@ static ssize_t memory_reclaim(struct kernfs_open_fil= e *of, char *buf, return nbytes; } =20 +#ifdef CONFIG_LRU_GEN +static ssize_t memory_aging(struct kernfs_open_file *of, char *buf, + size_t nbytes, loff_t off) +{ + struct mem_cgroup *memcg =3D mem_cgroup_from_css(of_css(of)); + unsigned long nr_gens; + int ret; + + ret =3D kstrtoul(buf, 0, &nr_gens); + if (ret) + return ret; + + if (nr_gens =3D=3D 0 || nr_gens > MAX_NR_GENS) + return -EINVAL; + + ret =3D lru_gen_age_memcg(memcg, nr_gens); + if (ret) + return ret; + + return nbytes; +} +#endif + static struct cftype memory_files[] =3D { { .name =3D "current", @@ -4967,6 +4990,13 @@ static struct cftype memory_files[] =3D { .flags =3D CFTYPE_NS_DELEGATABLE, .write =3D memory_reclaim, }, +#ifdef CONFIG_LRU_GEN + { + .name =3D "aging", + .flags =3D CFTYPE_NS_DELEGATABLE, + .write =3D memory_aging, + }, +#endif { } /* terminate */ }; =20 --=20 2.25.1 From nobody Sat Jul 25 19:29:02 2026 Received: from mta22.hihonor.com (mta22.honor.com [81.70.192.198]) (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 C3B403BF68E; Tue, 14 Jul 2026 12:15:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.70.192.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784031346; cv=none; b=d1OZhmTByxhpIV69xMPIyWGs9pEqiDDUtNeaofjYWur4SY/oLxwNcUMq6dEMyyabFxD/uMS7YHpOg6wEFytI7sBfa2qqoqa5xcQuva3slsA3p/VfkKZe83MxUeaBeIkSTX8xZk7vFvvNSofnYI1Ocu8eSMLhtpYYQch6TUoVgZk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784031346; c=relaxed/simple; bh=AxrVQYJ2HupDfKvNptWjmg28qti3Iy59r7OC4/bVxqk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pAj2/aHqYHiQufVdgvuLY9fofXHhdOodCoYtL+fqkS0NEsbdClfik8tTt0y/B9BKEl4leyUpeYXVfwxE+Y7sVtvrG2F1syRyV146pB2R+aVZ8vTFv0CawYgxOofnGYMoAvrBHiF5Y777xWOnlQG5rcdMEn8gpzOVHmGYwZS6ebY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=honor.com; spf=pass smtp.mailfrom=honor.com; dkim=pass (1024-bit key) header.d=honor.com header.i=@honor.com header.b=bm6kt+pb; arc=none smtp.client-ip=81.70.192.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=honor.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=honor.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=honor.com header.i=@honor.com header.b="bm6kt+pb" dkim-signature: v=1; a=rsa-sha256; d=honor.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=To:From; bh=ajWSzJC8ZX5c1v8s4BktWV/r3nVerPCf8q26E0mJDVo=; b=bm6kt+pb2YDJ5eFS0HrJFxTuF9vH6LOkgpTUnkcRQTtLsSA/bEK9RLS/PrwpIVrNghjNzIqy2 SFBNTEKjWgbRLxSRRmvtZukmApB7kuHnmFJIgDjsFRX+4lgotpouNZW4YA5qOASCYC6UFRzhqnM 2uuiYcIdTBtzmE4M9vyA7OI= Received: from TW006-1.hihonor.com (unknown [10.77.215.153]) by mta22.hihonor.com (SkyGuard) with ESMTPS id 4gzytq1bcMzYlJxV; Tue, 14 Jul 2026 20:15:31 +0800 (CST) Received: from TA012-1.hihonor.com (10.77.199.132) by TW006-1.hihonor.com (10.77.215.153) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.43; Tue, 14 Jul 2026 20:15:31 +0800 Received: from localhost.localdomain (10.144.5.36) by TA012-1.hihonor.com (10.77.199.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.17; Tue, 14 Jul 2026 20:15:20 +0800 From: Zicheng Wang To: , CC: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , Zicheng Wang Subject: [RFC v2 3/3] mm/lru_gen: expose oldest-generation page counts in memory.stat Date: Tue, 14 Jul 2026 20:15:29 +0800 Message-ID: <20260714121529.2237261-4-wangzicheng@honor.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260714121529.2237261-1-wangzicheng@honor.com> References: <20260714121529.2237261-1-wangzicheng@honor.com> 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-ClientProxiedBy: TW004-1.hihonor.com (10.77.232.85) To TA012-1.hihonor.com (10.77.199.132) Content-Type: text/plain; charset="utf-8" Add nr_oldest_anon and nr_oldest_file to memory.stat: pages in the oldest generation (min_seq) of each type, aggregated over the cgroup subtree and all N_MEMORY nodes. This is the observation half of the proactive-aging loop - it shows a policy how much anon/file memory the next reclaim can evict directly, so it can decide whether aging is needed. Computed on demand from lrugen->nr_pages; no per-page maintenance, no hot-path cost. Paired with the AGING counter and workingset_refault_* to confirm an aging pass took effect and was not too aggressive. Gated by CONFIG_LRU_GEN; reported in pages, matching the lru_gen dump. Signed-off-by: Zicheng Wang --- Documentation/admin-guide/cgroup-v2.rst | 18 ++++++++++++++++ mm/internal.h | 2 ++ mm/memcontrol.c | 24 +++++++++++++++++++++ mm/vmscan.c | 28 +++++++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-= guide/cgroup-v2.rst index f42ac6dddbf6..a071175d11be 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -1658,6 +1658,24 @@ The following nested keys are defined. the value for the foo counter, since the foo counter is type-based, not list-based. =20 + nr_oldest_anon, nr_oldest_file + Number of pages in the oldest generation of the anonymous and + file types. Only present when CONFIG_LRU_GEN is enabled. + + With MGLRU, page reclaim starts from the oldest generation + (min_seq) of each type, so these counts show how much anonymous + and file memory the next reclaim pass (or a proactive + memory.reclaim) can directly evict. They are aggregated across + this cgroup's subtree and all NUMA nodes, and are reported in + pages, matching the per-generation breakdown in the ``lru_gen`` + debugfs file. + + Proactive-aging policy can use them together with the ``aging`` + counter and ``workingset_refault_*``: a large ``nr_oldest_file`` + with near-zero ``nr_oldest_anon`` indicates file cache piling up + in the oldest generation while anonymous pages stay young, which + is the condition ``memory.aging`` is meant to rebalance. + slab_reclaimable Part of "slab" that might be reclaimed, such as dentries and inodes. diff --git a/mm/internal.h b/mm/internal.h index 96add6cd4627..959d376c02a6 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -636,6 +636,8 @@ int user_proactive_reclaim(char *buf, =20 #ifdef CONFIG_LRU_GEN int lru_gen_age_memcg(struct mem_cgroup *memcg, unsigned long nr_gens); +void lru_gen_nr_oldest_pages(struct lruvec *lruvec, + unsigned long *nr_anon, unsigned long *nr_file); #endif =20 /* diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c6a969b56878..016194cabd60 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1733,6 +1733,30 @@ static void memcg_stat_format(struct mem_cgroup *mem= cg, struct seq_buf *s) } } =20 +#ifdef CONFIG_LRU_GEN + /* Oldest-generation (min_seq) anon/file pages over this cgroup's + * subtree and all N_MEMORY nodes; see lru_gen_nr_oldest_pages(). + */ + { + struct mem_cgroup *mi; + int nid; + unsigned long oldest_anon =3D 0, oldest_file =3D 0; + + for_each_mem_cgroup_tree(mi, memcg) { + for_each_node_state(nid, N_MEMORY) { + unsigned long nr_anon, nr_file; + struct lruvec *lruvec =3D mem_cgroup_lruvec(mi, NODE_DATA(nid)); + + lru_gen_nr_oldest_pages(lruvec, &nr_anon, &nr_file); + oldest_anon +=3D nr_anon; + oldest_file +=3D nr_file; + } + } + seq_buf_printf(s, "nr_oldest_anon %lu\n", oldest_anon); + seq_buf_printf(s, "nr_oldest_file %lu\n", oldest_file); + } +#endif + /* Accumulated memory events */ seq_buf_printf(s, "pgscan %lu\n", memcg_page_state(memcg, PGSCAN_KSWAPD) + diff --git a/mm/vmscan.c b/mm/vmscan.c index 805e29c499c8..3800f0c4f1f5 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -5935,6 +5935,34 @@ int lru_gen_age_memcg(struct mem_cgroup *memcg, unsi= gned long nr_gens) return ret; } =20 +/** + * lru_gen_nr_oldest_pages - pages in the oldest generation of a lruvec + * @lruvec: target lruvec + * @nr_anon: filled with oldest-generation anonymous page count + * @nr_file: filled with oldest-generation file page count + * + * Reclaim starts from the oldest generation (min_seq) of each type, so th= ese + * count what the next reclaim can evict directly. Computed from + * lrugen->nr_pages[min_seq type][zone]; eventually consistent, clamped to= 0. + */ +void lru_gen_nr_oldest_pages(struct lruvec *lruvec, + unsigned long *nr_anon, unsigned long *nr_file) +{ + int type, zone; + struct lru_gen_folio *lrugen =3D &lruvec->lrugen; + unsigned long size[ANON_AND_FILE] =3D {}; + + for (type =3D 0; type < ANON_AND_FILE; type++) { + int gen =3D lru_gen_from_seq(READ_ONCE(lrugen->min_seq[type])); + + for (zone =3D 0; zone < MAX_NR_ZONES; zone++) + size[type] +=3D max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L); + } + + *nr_anon =3D size[LRU_GEN_ANON]; + *nr_file =3D size[LRU_GEN_FILE]; +} + #else /* !CONFIG_LRU_GEN */ =20 static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_contro= l *sc) --=20 2.25.1