Implement cgroup2 "memory.max.effective" and "memory.swap.max.effective" files similar to existing "cpuset.cpus.effective". cgroup1 (by function memcg1_stat_format) contains two lines hierarchical_memory_limit %llu hierarchical_memsw_limit %llu which are useful for userland to easily and performance-wise find out the effective cgroup limits being applied. Otherwise userland has to open+read+close the file "memory.max" and/or "memory.swap.max" in multiple parent directories of a nested cgroup. For cgroup1 it was implemented by: memcg: show real limit under hierarchy mode https://github.com/torvalds/linux/commit/fee7b548e6f2bd4bfd03a1a45d3afd593de7d5e9 Date: Wed Jan 7 18:08:26 2009 -0800 But for cgroup2 it has been missing so far. v4: Adjust the commit message. v3: memory.stat fields -> *.max.effective separate files suggested by Michal Koutny v2: hierarchical_memsw_limit -> hierarchical_swap_limit fix found by Waiman Long v1: hierarchical_memory_limit && hierarchical_memsw_limit in memory.stat Signed-off-by: Jan Kratochvil (Azul) <jkratochvil@xxxxxxxx> mm/memcontrol.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 1ed40f9d3..8c4cb5f60 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6845,6 +6845,19 @@ static ssize_t memory_max_write(struct kernfs_open_file *of, return nbytes; } +static int memory_max_effective_show(struct seq_file *m, void *v) +{ + unsigned long memory; + struct mem_cgroup *mi; + + /* Hierarchical information */ + memory = PAGE_COUNTER_MAX; + for (mi = mem_cgroup_from_seq(m); mi; mi = parent_mem_cgroup(mi)) + memory = min(memory, READ_ONCE(mi->memory.max)); + + return seq_puts_memcg_tunable(m, memory); +} + /* * Note: don't forget to update the 'samples/cgroup/memcg_event_listener' * if any new events become available. @@ -7038,6 +7051,11 @@ static struct cftype memory_files[] = { .seq_show = memory_max_show, .write = memory_max_write, }, + { + .name = "max.effective", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = memory_max_effective_show, + }, { .name = "events", .flags = CFTYPE_NOT_ON_ROOT, @@ -8040,6 +8058,19 @@ static ssize_t swap_max_write(struct kernfs_open_file *of, return nbytes; } +static int swap_max_effective_show(struct seq_file *m, void *v) +{ + unsigned long swap; + struct mem_cgroup *mi; + + /* Hierarchical information */ + swap = PAGE_COUNTER_MAX; + for (mi = mem_cgroup_from_seq(m); mi; mi = parent_mem_cgroup(mi)) + swap = min(swap, READ_ONCE(mi->swap.max)); + + return seq_puts_memcg_tunable(m, swap); +} + static int swap_events_show(struct seq_file *m, void *v) { struct mem_cgroup *memcg = mem_cgroup_from_seq(m); @@ -8072,6 +8103,11 @@ static struct cftype swap_files[] = { .seq_show = swap_max_show, .write = swap_max_write, }, + { + .name = "swap.max.effective", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = swap_max_effective_show, + }, { .name = "swap.peak", .flags = CFTYPE_NOT_ON_ROOT,