Expose memory.cma.<area>.{current,max} files under each non-root cgroup
when cgroupfs memory_cma_accounting option is enabled.

memory.cma.<area>.current reports the cgroup's CMA usage for that area.
memory.cma.<area>.max is a read-write limit (default: max).

Each CMA area gets a cftype template with its name set from the CMA
area's name.

Signed-off-by: Eric Chanudet <[email protected]>
---
 Documentation/admin-guide/cgroup-v2.rst | 15 ++++++
 mm/memcontrol.c                         | 89 +++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+)

diff --git a/Documentation/admin-guide/cgroup-v2.rst 
b/Documentation/admin-guide/cgroup-v2.rst
index 3dfc13f31a0c..39871a7a08a8 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -1928,6 +1928,21 @@ The following nested keys are defined.
        This setting has no effect if zswap is disabled, and swapping
        is allowed unless memory.swap.max is set to 0.
 
+  memory.cma.<area>.current
+        A read-only single value file which exists on non-root cgroups. <area>
+        is the CMA area name (e.g. "reserved" for the default DMA CMA area).
+
+        The total amount of CMA memory currently charged to the cgroup for the
+        given CMA area. Only accounted when memory_cma_accounting is enabled.
+
+  memory.cma.<area>.max
+        A read-write single value file which exists on non-root cgroups. The
+        default is "max".
+
+        CMA usage hard limit for the given area. If a cgroup's CMA usage for
+        this area exceeds this limit, the allocation will fail. Only enforced
+        when memory_cma_accounting is enabled.
+
   memory.pressure
        A read-only nested-keyed file.
 
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 8eca00d30fc5..f28d610a59ce 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -6182,3 +6182,92 @@ void mem_cgroup_show_protected_memory(struct mem_cgroup 
*memcg)
                K(atomic_long_read(&memcg->memory.children_min_usage)),
                K(atomic_long_read(&memcg->memory.children_low_usage)));
 }
+
+#ifdef CONFIG_CMA
+static int memory_cma_current_show(struct seq_file *m, void *v)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
+       int idx = seq_cft(m)->private;
+       unsigned long cur = page_counter_read(&memcg->cma_counters[idx]);
+
+       seq_printf(m, "%lu\n", cur * PAGE_SIZE);
+
+       return 0;
+}
+
+static int memory_cma_max_show(struct seq_file *m, void *v)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
+       int idx = seq_cft(m)->private;
+       unsigned long max = READ_ONCE(memcg->cma_counters[idx].max);
+
+       return seq_puts_memcg_tunable(m, max);
+}
+
+static ssize_t memory_cma_max_write(struct kernfs_open_file *of, char *buf,
+                                   size_t nbytes, loff_t off)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
+       int idx = of_cft(of)->private;
+       unsigned long max;
+       int rc;
+
+       buf = strstrip(buf);
+       rc = page_counter_memparse(buf, "max", &max);
+       if (rc)
+               return rc;
+
+       xchg(&memcg->cma_counters[idx].max, max);
+
+       return nbytes;
+}
+
+static struct cftype cma_dfl_tmpl[] = {
+       {
+               .name = "current",
+               .seq_show = memory_cma_current_show,
+               .flags = CFTYPE_NOT_ON_ROOT,
+       },
+       {
+               .name = "max",
+               .seq_show = memory_cma_max_show,
+               .write = memory_cma_max_write,
+               .flags = CFTYPE_NOT_ON_ROOT,
+       },
+       { }
+};
+#define CMA_DFL_TMPL_SIZE      (ARRAY_SIZE(cma_dfl_tmpl) - 1)
+
+static struct cftype *cma_dfl_files;
+
+static int __init mem_cgroup_cma_init(void)
+{
+       struct cftype *cft;
+       unsigned int i, j;
+       int count;
+
+       if (mem_cgroup_disabled() || !cma_area_count)
+               return 0;
+
+       count = cma_area_count * CMA_DFL_TMPL_SIZE;
+       cma_dfl_files = kcalloc(count + 1, sizeof(*cma_dfl_tmpl), GFP_KERNEL);
+       if (!cma_dfl_files)
+               return -ENOMEM;
+
+       for (i = 0; i < cma_area_count; ++i)
+               for (j = 0; j < CMA_DFL_TMPL_SIZE; ++j) {
+                       cft = &cma_dfl_files[i * CMA_DFL_TMPL_SIZE + j];
+                       *cft = cma_dfl_tmpl[j];
+                       snprintf(cft->name, MAX_CFTYPE_NAME, "cma.%s.%s",
+                                cma_get_name(&cma_areas[i]),
+                                cma_dfl_tmpl[j].name);
+                       cft->private = i; /* cma area index for counters in 
callbacks. */
+                       lockdep_register_key(&cft->lockdep_key);
+               }
+
+       WARN_ON(cgroup_add_dfl_cftypes(&memory_cgrp_subsys, cma_dfl_files));
+
+       return 0;
+}
+subsys_initcall(mem_cgroup_cma_init);
+#endif /* CONFIG_CMA */

-- 
2.53.0


Reply via email to