From: Tao Cui <cui.tao@linux.dev>
To: liuqiqi@kylinos.cn, linux-mm@kvack.org
Cc: cui.tao@linux.dev, tj@kernel.org, mkoutny@suse.com,
hannes@cmpxchg.org, mhocko@kernel.org, roman.gushchin@linux.dev,
shakeel.butt@linux.dev, muchun.song@linux.dev,
akpm@linux-foundation.org, cgroups@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH 8/8] cgroup: add memory_tiered_limits cgroup mount option
Date: Tue, 18 Aug 2026 12:56:05 +0800 [thread overview]
Message-ID: <43f12802-de69-45a2-bb97-317a6cc2ee4b@linux.dev> (raw)
In-Reply-To: <20260818023121.100613-9-liuqiqi@kylinos.cn>
在 2026/8/18 10:31, liuqiqi@kylinos.cn 写道:
> From: Qiqi Liu <liuqiqi@kylinos.cn>
>
> Introduce the memory_tiered_limits cgroup v2 mount option to enable
> tier-aware memory control. The option follows the same pattern as
> memory_recursiveprot and memory_hugetlb_accounting.
>
> When enabled, per-tier accounting, charging, stock batching, auto-
> derivation, and the memory.tier control file are active. When disabled
> (default), all tier-specific code is gated at entry points: no counters
> are created, no charge/uncharge occurs, and memory.tier remains empty.
> This ensures no measurable overhead for systems that do not opt in.
>
> Usage:
> mount -t cgroup2 none /sys/fs/cgroup -o memory_tiered_limits
>
> Because the cgroup2 mount is owned by the init system, early-boot
> cgroups are created before userspace can specify mount options. To
> cover these boot-time cgroups, mirror the cgroup_favordynmods approach
> and add a kernel command-line parameter,
> cgroup_memory_tiered_limits=<bool>.
> This sets CGRP_ROOT_MEMORY_TIERED_LIMITS on all cgroup2 mounts by
> default. The mount option remains available for runtime, per-mount
> control.
>
> Signed-off-by: Qiqi Liu <liuqiqi@kylinos.cn>
> ---
> include/linux/cgroup-defs.h | 5 +++++
> include/linux/memcontrol.h | 12 ++++++++++++
> kernel/cgroup/cgroup.c | 21 +++++++++++++++++++++
> mm/memcontrol.c | 22 ++++++++++++++++++----
> 4 files changed, 56 insertions(+), 4 deletions(-)
>
> diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
> index de2cd6238c2a..4324d68dbce9 100644
> --- a/include/linux/cgroup-defs.h
> +++ b/include/linux/cgroup-defs.h
> @@ -129,6 +129,11 @@ enum {
> * Enable legacy local pids.events.
> */
> CGRP_ROOT_PIDS_LOCAL_EVENTS = (1 << 20),
> +
> + /*
> + * Enable tier-aware limits for the memory controller.
> + */
> + CGRP_ROOT_MEMORY_TIERED_LIMITS = (1 << 21),
> };
>
> /* cftype->flags */
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 8848bc5eeb24..094b9a839977 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -544,6 +544,18 @@ static inline bool mem_cgroup_disabled(void)
> return !cgroup_subsys_enabled(memory_cgrp_subsys);
> }
>
> +#ifdef CONFIG_NUMA
> +static inline bool mem_cgroup_tiered_limits(void)
> +{
> + return cgrp_dfl_root.flags & CGRP_ROOT_MEMORY_TIERED_LIMITS;
> +}
> +#else
> +static inline bool mem_cgroup_tiered_limits(void)
> +{
> + return false;
> +}
> +#endif
> +
> static inline void mem_cgroup_protection(struct mem_cgroup *root,
> struct mem_cgroup *memcg,
> unsigned long *min,
> diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
> index 38f8d9df8fbc..da94bcda0859 100644
> --- a/kernel/cgroup/cgroup.c
> +++ b/kernel/cgroup/cgroup.c
> @@ -231,6 +231,7 @@ static u32 have_release_callback __read_mostly;
> static u32 have_canfork_callback __read_mostly;
>
> static bool have_favordynmods __ro_after_init = IS_ENABLED(CONFIG_CGROUP_FAVOR_DYNMODS);
> +static bool have_memory_tiered_limits __ro_after_init;
>
> /*
> * Write protected by cgroup_mutex and write-lock of cgroup_threadgroup_rwsem,
> @@ -1985,6 +1986,7 @@ enum cgroup2_param {
> Opt_memory_recursiveprot,
> Opt_memory_hugetlb_accounting,
> Opt_pids_localevents,
> + Opt_memory_tiered_limits,
> nr__cgroup2_params
> };
>
> @@ -1995,6 +1997,7 @@ static const struct fs_parameter_spec cgroup2_fs_parameters[] = {
> fsparam_flag("memory_recursiveprot", Opt_memory_recursiveprot),
> fsparam_flag("memory_hugetlb_accounting", Opt_memory_hugetlb_accounting),
> fsparam_flag("pids_localevents", Opt_pids_localevents),
> + fsparam_flag("memory_tiered_limits", Opt_memory_tiered_limits),
> {}
> };
>
> @@ -2027,6 +2030,9 @@ static int cgroup2_parse_param(struct fs_context *fc, struct fs_parameter *param
> case Opt_pids_localevents:
> ctx->flags |= CGRP_ROOT_PIDS_LOCAL_EVENTS;
> return 0;
> + case Opt_memory_tiered_limits:
> + ctx->flags |= CGRP_ROOT_MEMORY_TIERED_LIMITS;
> + return 0;
> }
> return -EINVAL;
> }
> @@ -2068,6 +2074,11 @@ static void apply_cgroup_root_flags(unsigned int root_flags)
> cgrp_dfl_root.flags |= CGRP_ROOT_PIDS_LOCAL_EVENTS;
> else
> cgrp_dfl_root.flags &= ~CGRP_ROOT_PIDS_LOCAL_EVENTS;
> +
> + if (root_flags & CGRP_ROOT_MEMORY_TIERED_LIMITS)
> + cgrp_dfl_root.flags |= CGRP_ROOT_MEMORY_TIERED_LIMITS;
> + else
> + cgrp_dfl_root.flags &= ~CGRP_ROOT_MEMORY_TIERED_LIMITS;
> }
> }
>
> @@ -2085,6 +2096,8 @@ static int cgroup_show_options(struct seq_file *seq, struct kernfs_root *kf_root
> seq_puts(seq, ",memory_hugetlb_accounting");
> if (cgrp_dfl_root.flags & CGRP_ROOT_PIDS_LOCAL_EVENTS)
> seq_puts(seq, ",pids_localevents");
> + if (cgrp_dfl_root.flags & CGRP_ROOT_MEMORY_TIERED_LIMITS)
> + seq_puts(seq, ",memory_tiered_limits");
> return 0;
> }
>
> @@ -2363,6 +2376,8 @@ static int cgroup_init_fs_context(struct fs_context *fc)
>
> if (have_favordynmods)
> ctx->flags |= CGRP_ROOT_FAVOR_DYNMODS;
> + if (have_memory_tiered_limits)
> + ctx->flags |= CGRP_ROOT_MEMORY_TIERED_LIMITS;
>
> return 0;
> }
> @@ -7214,6 +7229,12 @@ static int __init cgroup_favordynmods_setup(char *str)
> }
> __setup("cgroup_favordynmods=", cgroup_favordynmods_setup);
>
> +static int __init cgroup_memory_tiered_limits_setup(char *str)
> +{
> + return (kstrtobool(str, &have_memory_tiered_limits) == 0);
> +}
> +__setup("cgroup_memory_tiered_limits=", cgroup_memory_tiered_limits_setup);
> +
> /**
> * css_tryget_online_from_dir - get corresponding css from a cgroup dentry
> * @dentry: directory dentry of interest
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index f39a702d2301..891051f164ff 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -2462,7 +2462,7 @@ static void memcg_charge_tier_id(struct mem_cgroup *memcg, int tier_id,
> {
> struct memcg_tier_counter *tc;
>
> - if (tier_id < 0)
> + if (!mem_cgroup_tiered_limits() || tier_id < 0)
The cover says remount "affects newly created cgroups only", but the
gate is the live global flag on both charge and uncharge.
(same in memcg_uncharge_tier_id() and refill_tier_stock()).
> return;
> rcu_read_lock();
> tc = memcg_tier_counter_find(memcg, tier_id);
> @@ -2476,7 +2476,7 @@ static void memcg_uncharge_tier_id(struct mem_cgroup *memcg, int tier_id,
> {
> struct memcg_tier_counter *tc;
>
> - if (tier_id < 0)
> + if (!mem_cgroup_tiered_limits() || tier_id < 0)
> return;
> rcu_read_lock();
> tc = memcg_tier_counter_find(memcg, tier_id);
> @@ -2581,6 +2581,9 @@ static void tier_update_derived_limits(struct mem_cgroup *memcg)
> unsigned long total;
> int i, nr_entries;
>
> + if (!mem_cgroup_tiered_limits())
> + return;
> +
> spin_lock(&tier_cap_lock);
> total = tier_total_capacity;
> nr_entries = nr_tier_entries;
> @@ -3198,6 +3201,9 @@ static void refill_tier_stock(struct mem_cgroup *memcg, int tier_id,
> uint8_t pages;
> int i;
>
> + if (!mem_cgroup_tiered_limits())
> + return;
> +
> /* Too big to cache: direct uncharge, leave the stock untouched. */
> if (nr_pages > MEMCG_CHARGE_BATCH) {
> rcu_read_lock();
> @@ -3252,7 +3258,7 @@ static int try_charge_memcg_tier(struct mem_cgroup *memcg, gfp_t gfp_mask,
> bool drained = false;
> nodemask_t nodes, *nmp = NULL;
>
> - if (tier_id < 0)
> + if (!mem_cgroup_tiered_limits() || tier_id < 0)
> return 0;
>
> rcu_read_lock();
> @@ -4736,7 +4742,7 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
> page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
> page_counter_init(&memcg->swap, &parent->swap, false);
>
> - {
> + if (mem_cgroup_tiered_limits()) {
> int nid, tid;
>
> for_each_online_node(nid) {
> @@ -5459,6 +5465,8 @@ static int memory_tier_show(struct seq_file *m, void *v)
> struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
> struct memcg_tier_counter *tc;
>
> + if (!mem_cgroup_tiered_limits())
> + return 0;
> rcu_read_lock();
> list_for_each_entry_rcu(tc, &memcg->tier_counters, list) {
> seq_printf(m, "tier%d.current=%llu\n", tc->tier_id,
> @@ -5484,6 +5492,9 @@ static ssize_t memory_tier_write(struct kernfs_open_file *of,
> char knob[8], *p;
> int tier_id, err;
>
> + if (!mem_cgroup_tiered_limits())
> + return -EOPNOTSUPP;
> +
> buf = strstrip(buf);
> if (sscanf(buf, "tier%d.%7[^=]", &tier_id, knob) != 2)
> return -EINVAL;
> @@ -6267,6 +6278,9 @@ static int __meminit memcg_tier_hotplug_cb(struct notifier_block *self,
> struct mem_cgroup *memcg;
> int tid;
>
> + if (!mem_cgroup_tiered_limits())
> + return notifier_from_errno(0);
> +
> switch (action) {
> case NODE_ADDED_FIRST_MEMORY:
> tid = node_to_tier_id(nn->nid);
prev parent reply other threads:[~2026-08-18 4:56 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-18 2:31 [RFC PATCH 0/8] mm/memcontrol: introduce per-tier memory accounting and control liuqiqi
2026-08-18 2:31 ` [RFC PATCH 1/8] mm/memory-tiers: add node_to_tier_id and tier_id_to_nodemask liuqiqi
2026-08-18 2:31 ` [RFC PATCH 2/8] mm/vmscan: add try_to_free_mem_cgroup_pages_nodemask liuqiqi
2026-08-18 2:31 ` [RFC PATCH 3/8] mm/memcontrol: add per-tier page counter infrastructure and lifecycle liuqiqi
2026-08-18 2:31 ` [RFC PATCH 4/8] mm/memcontrol: add per-tier charge and uncharge liuqiqi
2026-08-18 4:32 ` Tao Cui
2026-08-18 2:31 ` [RFC PATCH 5/8] mm/memcontrol: add per-cpu stock for tier charge/uncharge liuqiqi
2026-08-18 2:31 ` [RFC PATCH 6/8] mm/memcontrol: add memory.tier control file liuqiqi
2026-08-18 2:31 ` [RFC PATCH 7/8] mm/memcontrol: auto-derive tier high/max from memory.high/max liuqiqi
2026-08-18 4:46 ` Tao Cui
2026-08-18 2:31 ` [RFC PATCH 8/8] cgroup: add memory_tiered_limits cgroup mount option liuqiqi
2026-08-18 4:56 ` Tao Cui [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=43f12802-de69-45a2-bb97-317a6cc2ee4b@linux.dev \
--to=cui.tao@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=cgroups@vger.kernel.org \
--cc=hannes@cmpxchg.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=liuqiqi@kylinos.cn \
--cc=mhocko@kernel.org \
--cc=mkoutny@suse.com \
--cc=muchun.song@linux.dev \
--cc=roman.gushchin@linux.dev \
--cc=shakeel.butt@linux.dev \
--cc=tj@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox