Re: [RFC PATCH 8/8] cgroup: add memory_tiered_limits cgroup mount option

Tao Cui <[email protected]>
Newsgroups org.kvack.linux-mm,org.kernel.vger.cgroups,org.kernel.vger.linux-kernel
Message-ID <[email protected]>

在 2026/8/18 10:31, [email protected] 写道:
> From: Qiqi Liu <[email protected]>
> 
> 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 <[email protected]>
> ---
>  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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.