[RFC v2 2/3] mm: memcontrol: add memory.aging cgroup v2 file

From: Zicheng Wang

Date: Tue Jul 14 2026 - 08:17:09 EST


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 <wangzicheng@xxxxxxxxx>
---
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=max exclusively reclaims anonymous memory.

+ 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.

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 on 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_file *of, char *buf,
return nbytes;
}

+#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 = mem_cgroup_from_css(of_css(of));
+ unsigned long nr_gens;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &nr_gens);
+ if (ret)
+ return ret;
+
+ if (nr_gens == 0 || nr_gens > MAX_NR_GENS)
+ return -EINVAL;
+
+ ret = lru_gen_age_memcg(memcg, nr_gens);
+ if (ret)
+ return ret;
+
+ return nbytes;
+}
+#endif
+
static struct cftype memory_files[] = {
{
.name = "current",
@@ -4967,6 +4990,13 @@ static struct cftype memory_files[] = {
.flags = CFTYPE_NS_DELEGATABLE,
.write = memory_reclaim,
},
+#ifdef CONFIG_LRU_GEN
+ {
+ .name = "aging",
+ .flags = CFTYPE_NS_DELEGATABLE,
+ .write = memory_aging,
+ },
+#endif
{ } /* terminate */
};

--
2.25.1