]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mm: memcg: move cgroup v1 interface files to memcontrol-v1.c
authorRoman Gushchin <roman.gushchin@linux.dev>
Tue, 25 Jun 2024 00:59:02 +0000 (17:59 -0700)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 5 Jul 2024 01:05:53 +0000 (18:05 -0700)
Move legacy cgroup v1 memory controller interfaces and corresponding code
into memcontrol-v1.c.

[roman.gushchin@linux.dev: move two functions]
Link: https://lkml.kernel.org/r/20240704002712.2077812-1-roman.gushchin@linux.dev
Link: https://lkml.kernel.org/r/20240625005906.106920-11-roman.gushchin@linux.dev
Signed-off-by: Roman Gushchin <roman.gushchin@linux.dev>
Acked-by: Michal Hocko <mhocko@suse.com>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Muchun Song <muchun.song@linux.dev>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/memcontrol-v1.c
mm/memcontrol-v1.h
mm/memcontrol.c

index 1d5608ee1606ab879f96cb2c6a0045d8897dd9a4..597b03ee9e35331319b9fbb95b1aaefe7cde43d9 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/poll.h>
 #include <linux/sort.h>
 #include <linux/file.h>
+#include <linux/seq_buf.h>
 
 #include "internal.h"
 #include "swap.h"
@@ -110,6 +111,18 @@ struct mem_cgroup_event {
        struct work_struct remove;
 };
 
+#define MEMFILE_PRIVATE(x, val)        ((x) << 16 | (val))
+#define MEMFILE_TYPE(val)      ((val) >> 16 & 0xffff)
+#define MEMFILE_ATTR(val)      ((val) & 0xffff)
+
+enum {
+       RES_USAGE,
+       RES_LIMIT,
+       RES_MAX_USAGE,
+       RES_FAILCNT,
+       RES_SOFT_LIMIT,
+};
+
 #ifdef CONFIG_LOCKDEP
 static struct lockdep_map memcg_oom_lock_dep_map = {
        .name = "memcg_oom_lock",
@@ -577,14 +590,14 @@ static inline int mem_cgroup_move_swap_account(swp_entry_t entry,
 }
 #endif
 
-u64 mem_cgroup_move_charge_read(struct cgroup_subsys_state *css,
+static u64 mem_cgroup_move_charge_read(struct cgroup_subsys_state *css,
                                struct cftype *cft)
 {
        return mem_cgroup_from_css(css)->move_charge_at_immigrate;
 }
 
 #ifdef CONFIG_MMU
-int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css,
+static int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css,
                                 struct cftype *cft, u64 val)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_css(css);
@@ -606,7 +619,7 @@ int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css,
        return 0;
 }
 #else
-int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css,
+static int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css,
                                 struct cftype *cft, u64 val)
 {
        return -ENOSYS;
@@ -1803,8 +1816,8 @@ static void memcg_event_ptable_queue_proc(struct file *file,
  * Input must be in format '<event_fd> <control_fd> <args>'.
  * Interpretation of args is defined by control file implementation.
  */
-ssize_t memcg_write_event_control(struct kernfs_open_file *of,
-                                 char *buf, size_t nbytes, loff_t off)
+static ssize_t memcg_write_event_control(struct kernfs_open_file *of,
+                                        char *buf, size_t nbytes, loff_t off)
 {
        struct cgroup_subsys_state *css = of_css(of);
        struct mem_cgroup *memcg = mem_cgroup_from_css(css);
@@ -2184,6 +2197,722 @@ void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked)
                mem_cgroup_oom_unlock(memcg);
 }
 
+static DEFINE_MUTEX(memcg_max_mutex);
+
+static int mem_cgroup_resize_max(struct mem_cgroup *memcg,
+                                unsigned long max, bool memsw)
+{
+       bool enlarge = false;
+       bool drained = false;
+       int ret;
+       bool limits_invariant;
+       struct page_counter *counter = memsw ? &memcg->memsw : &memcg->memory;
+
+       do {
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       break;
+               }
+
+               mutex_lock(&memcg_max_mutex);
+               /*
+                * Make sure that the new limit (memsw or memory limit) doesn't
+                * break our basic invariant rule memory.max <= memsw.max.
+                */
+               limits_invariant = memsw ? max >= READ_ONCE(memcg->memory.max) :
+                                          max <= memcg->memsw.max;
+               if (!limits_invariant) {
+                       mutex_unlock(&memcg_max_mutex);
+                       ret = -EINVAL;
+                       break;
+               }
+               if (max > counter->max)
+                       enlarge = true;
+               ret = page_counter_set_max(counter, max);
+               mutex_unlock(&memcg_max_mutex);
+
+               if (!ret)
+                       break;
+
+               if (!drained) {
+                       drain_all_stock(memcg);
+                       drained = true;
+                       continue;
+               }
+
+               if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
+                                       memsw ? 0 : MEMCG_RECLAIM_MAY_SWAP)) {
+                       ret = -EBUSY;
+                       break;
+               }
+       } while (true);
+
+       if (!ret && enlarge)
+               memcg1_oom_recover(memcg);
+
+       return ret;
+}
+
+/*
+ * Reclaims as many pages from the given memcg as possible.
+ *
+ * Caller is responsible for holding css reference for memcg.
+ */
+static int mem_cgroup_force_empty(struct mem_cgroup *memcg)
+{
+       int nr_retries = MAX_RECLAIM_RETRIES;
+
+       /* we call try-to-free pages for make this cgroup empty */
+       lru_add_drain_all();
+
+       drain_all_stock(memcg);
+
+       /* try to free all pages in this cgroup */
+       while (nr_retries && page_counter_read(&memcg->memory)) {
+               if (signal_pending(current))
+                       return -EINTR;
+
+               if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
+                                                 MEMCG_RECLAIM_MAY_SWAP))
+                       nr_retries--;
+       }
+
+       return 0;
+}
+
+static ssize_t mem_cgroup_force_empty_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));
+
+       if (mem_cgroup_is_root(memcg))
+               return -EINVAL;
+       return mem_cgroup_force_empty(memcg) ?: nbytes;
+}
+
+static u64 mem_cgroup_hierarchy_read(struct cgroup_subsys_state *css,
+                                    struct cftype *cft)
+{
+       return 1;
+}
+
+static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
+                                     struct cftype *cft, u64 val)
+{
+       if (val == 1)
+               return 0;
+
+       pr_warn_once("Non-hierarchical mode is deprecated. "
+                    "Please report your usecase to linux-mm@kvack.org if you "
+                    "depend on this functionality.\n");
+
+       return -EINVAL;
+}
+
+static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,
+                              struct cftype *cft)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+       struct page_counter *counter;
+
+       switch (MEMFILE_TYPE(cft->private)) {
+       case _MEM:
+               counter = &memcg->memory;
+               break;
+       case _MEMSWAP:
+               counter = &memcg->memsw;
+               break;
+       case _KMEM:
+               counter = &memcg->kmem;
+               break;
+       case _TCP:
+               counter = &memcg->tcpmem;
+               break;
+       default:
+               BUG();
+       }
+
+       switch (MEMFILE_ATTR(cft->private)) {
+       case RES_USAGE:
+               if (counter == &memcg->memory)
+                       return (u64)mem_cgroup_usage(memcg, false) * PAGE_SIZE;
+               if (counter == &memcg->memsw)
+                       return (u64)mem_cgroup_usage(memcg, true) * PAGE_SIZE;
+               return (u64)page_counter_read(counter) * PAGE_SIZE;
+       case RES_LIMIT:
+               return (u64)counter->max * PAGE_SIZE;
+       case RES_MAX_USAGE:
+               return (u64)counter->watermark * PAGE_SIZE;
+       case RES_FAILCNT:
+               return counter->failcnt;
+       case RES_SOFT_LIMIT:
+               return (u64)READ_ONCE(memcg->soft_limit) * PAGE_SIZE;
+       default:
+               BUG();
+       }
+}
+
+/*
+ * This function doesn't do anything useful. Its only job is to provide a read
+ * handler for a file so that cgroup_file_mode() will add read permissions.
+ */
+static int mem_cgroup_dummy_seq_show(__always_unused struct seq_file *m,
+                                    __always_unused void *v)
+{
+       return -EINVAL;
+}
+
+static int memcg_update_tcp_max(struct mem_cgroup *memcg, unsigned long max)
+{
+       int ret;
+
+       mutex_lock(&memcg_max_mutex);
+
+       ret = page_counter_set_max(&memcg->tcpmem, max);
+       if (ret)
+               goto out;
+
+       if (!memcg->tcpmem_active) {
+               /*
+                * The active flag needs to be written after the static_key
+                * update. This is what guarantees that the socket activation
+                * function is the last one to run. See mem_cgroup_sk_alloc()
+                * for details, and note that we don't mark any socket as
+                * belonging to this memcg until that flag is up.
+                *
+                * We need to do this, because static_keys will span multiple
+                * sites, but we can't control their order. If we mark a socket
+                * as accounted, but the accounting functions are not patched in
+                * yet, we'll lose accounting.
+                *
+                * We never race with the readers in mem_cgroup_sk_alloc(),
+                * because when this value change, the code to process it is not
+                * patched in yet.
+                */
+               static_branch_inc(&memcg_sockets_enabled_key);
+               memcg->tcpmem_active = true;
+       }
+out:
+       mutex_unlock(&memcg_max_mutex);
+       return ret;
+}
+
+/*
+ * The user of this function is...
+ * RES_LIMIT.
+ */
+static ssize_t mem_cgroup_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));
+       unsigned long nr_pages;
+       int ret;
+
+       buf = strstrip(buf);
+       ret = page_counter_memparse(buf, "-1", &nr_pages);
+       if (ret)
+               return ret;
+
+       switch (MEMFILE_ATTR(of_cft(of)->private)) {
+       case RES_LIMIT:
+               if (mem_cgroup_is_root(memcg)) { /* Can't set limit on root */
+                       ret = -EINVAL;
+                       break;
+               }
+               switch (MEMFILE_TYPE(of_cft(of)->private)) {
+               case _MEM:
+                       ret = mem_cgroup_resize_max(memcg, nr_pages, false);
+                       break;
+               case _MEMSWAP:
+                       ret = mem_cgroup_resize_max(memcg, nr_pages, true);
+                       break;
+               case _KMEM:
+                       pr_warn_once("kmem.limit_in_bytes is deprecated and will be removed. "
+                                    "Writing any value to this file has no effect. "
+                                    "Please report your usecase to linux-mm@kvack.org if you "
+                                    "depend on this functionality.\n");
+                       ret = 0;
+                       break;
+               case _TCP:
+                       ret = memcg_update_tcp_max(memcg, nr_pages);
+                       break;
+               }
+               break;
+       case RES_SOFT_LIMIT:
+               if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
+                       ret = -EOPNOTSUPP;
+               } else {
+                       WRITE_ONCE(memcg->soft_limit, nr_pages);
+                       ret = 0;
+               }
+               break;
+       }
+       return ret ?: nbytes;
+}
+
+static ssize_t mem_cgroup_reset(struct kernfs_open_file *of, char *buf,
+                               size_t nbytes, loff_t off)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
+       struct page_counter *counter;
+
+       switch (MEMFILE_TYPE(of_cft(of)->private)) {
+       case _MEM:
+               counter = &memcg->memory;
+               break;
+       case _MEMSWAP:
+               counter = &memcg->memsw;
+               break;
+       case _KMEM:
+               counter = &memcg->kmem;
+               break;
+       case _TCP:
+               counter = &memcg->tcpmem;
+               break;
+       default:
+               BUG();
+       }
+
+       switch (MEMFILE_ATTR(of_cft(of)->private)) {
+       case RES_MAX_USAGE:
+               page_counter_reset_watermark(counter);
+               break;
+       case RES_FAILCNT:
+               counter->failcnt = 0;
+               break;
+       default:
+               BUG();
+       }
+
+       return nbytes;
+}
+
+#ifdef CONFIG_NUMA
+
+#define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE))
+#define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON))
+#define LRU_ALL             ((1 << NR_LRU_LISTS) - 1)
+
+static unsigned long mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
+                               int nid, unsigned int lru_mask, bool tree)
+{
+       struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
+       unsigned long nr = 0;
+       enum lru_list lru;
+
+       VM_BUG_ON((unsigned)nid >= nr_node_ids);
+
+       for_each_lru(lru) {
+               if (!(BIT(lru) & lru_mask))
+                       continue;
+               if (tree)
+                       nr += lruvec_page_state(lruvec, NR_LRU_BASE + lru);
+               else
+                       nr += lruvec_page_state_local(lruvec, NR_LRU_BASE + lru);
+       }
+       return nr;
+}
+
+static unsigned long mem_cgroup_nr_lru_pages(struct mem_cgroup *memcg,
+                                            unsigned int lru_mask,
+                                            bool tree)
+{
+       unsigned long nr = 0;
+       enum lru_list lru;
+
+       for_each_lru(lru) {
+               if (!(BIT(lru) & lru_mask))
+                       continue;
+               if (tree)
+                       nr += memcg_page_state(memcg, NR_LRU_BASE + lru);
+               else
+                       nr += memcg_page_state_local(memcg, NR_LRU_BASE + lru);
+       }
+       return nr;
+}
+
+static int memcg_numa_stat_show(struct seq_file *m, void *v)
+{
+       struct numa_stat {
+               const char *name;
+               unsigned int lru_mask;
+       };
+
+       static const struct numa_stat stats[] = {
+               { "total", LRU_ALL },
+               { "file", LRU_ALL_FILE },
+               { "anon", LRU_ALL_ANON },
+               { "unevictable", BIT(LRU_UNEVICTABLE) },
+       };
+       const struct numa_stat *stat;
+       int nid;
+       struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
+
+       mem_cgroup_flush_stats(memcg);
+
+       for (stat = stats; stat < stats + ARRAY_SIZE(stats); stat++) {
+               seq_printf(m, "%s=%lu", stat->name,
+                          mem_cgroup_nr_lru_pages(memcg, stat->lru_mask,
+                                                  false));
+               for_each_node_state(nid, N_MEMORY)
+                       seq_printf(m, " N%d=%lu", nid,
+                                  mem_cgroup_node_nr_lru_pages(memcg, nid,
+                                                       stat->lru_mask, false));
+               seq_putc(m, '\n');
+       }
+
+       for (stat = stats; stat < stats + ARRAY_SIZE(stats); stat++) {
+
+               seq_printf(m, "hierarchical_%s=%lu", stat->name,
+                          mem_cgroup_nr_lru_pages(memcg, stat->lru_mask,
+                                                  true));
+               for_each_node_state(nid, N_MEMORY)
+                       seq_printf(m, " N%d=%lu", nid,
+                                  mem_cgroup_node_nr_lru_pages(memcg, nid,
+                                                       stat->lru_mask, true));
+               seq_putc(m, '\n');
+       }
+
+       return 0;
+}
+#endif /* CONFIG_NUMA */
+
+static const unsigned int memcg1_stats[] = {
+       NR_FILE_PAGES,
+       NR_ANON_MAPPED,
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       NR_ANON_THPS,
+#endif
+       NR_SHMEM,
+       NR_FILE_MAPPED,
+       NR_FILE_DIRTY,
+       NR_WRITEBACK,
+       WORKINGSET_REFAULT_ANON,
+       WORKINGSET_REFAULT_FILE,
+#ifdef CONFIG_SWAP
+       MEMCG_SWAP,
+       NR_SWAPCACHE,
+#endif
+};
+
+static const char *const memcg1_stat_names[] = {
+       "cache",
+       "rss",
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       "rss_huge",
+#endif
+       "shmem",
+       "mapped_file",
+       "dirty",
+       "writeback",
+       "workingset_refault_anon",
+       "workingset_refault_file",
+#ifdef CONFIG_SWAP
+       "swap",
+       "swapcached",
+#endif
+};
+
+/* Universal VM events cgroup1 shows, original sort order */
+static const unsigned int memcg1_events[] = {
+       PGPGIN,
+       PGPGOUT,
+       PGFAULT,
+       PGMAJFAULT,
+};
+
+void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)
+{
+       unsigned long memory, memsw;
+       struct mem_cgroup *mi;
+       unsigned int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(memcg1_stat_names) != ARRAY_SIZE(memcg1_stats));
+
+       mem_cgroup_flush_stats(memcg);
+
+       for (i = 0; i < ARRAY_SIZE(memcg1_stats); i++) {
+               unsigned long nr;
+
+               nr = memcg_page_state_local_output(memcg, memcg1_stats[i]);
+               seq_buf_printf(s, "%s %lu\n", memcg1_stat_names[i], nr);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(memcg1_events); i++)
+               seq_buf_printf(s, "%s %lu\n", vm_event_name(memcg1_events[i]),
+                              memcg_events_local(memcg, memcg1_events[i]));
+
+       for (i = 0; i < NR_LRU_LISTS; i++)
+               seq_buf_printf(s, "%s %lu\n", lru_list_name(i),
+                              memcg_page_state_local(memcg, NR_LRU_BASE + i) *
+                              PAGE_SIZE);
+
+       /* Hierarchical information */
+       memory = memsw = PAGE_COUNTER_MAX;
+       for (mi = memcg; mi; mi = parent_mem_cgroup(mi)) {
+               memory = min(memory, READ_ONCE(mi->memory.max));
+               memsw = min(memsw, READ_ONCE(mi->memsw.max));
+       }
+       seq_buf_printf(s, "hierarchical_memory_limit %llu\n",
+                      (u64)memory * PAGE_SIZE);
+       seq_buf_printf(s, "hierarchical_memsw_limit %llu\n",
+                      (u64)memsw * PAGE_SIZE);
+
+       for (i = 0; i < ARRAY_SIZE(memcg1_stats); i++) {
+               unsigned long nr;
+
+               nr = memcg_page_state_output(memcg, memcg1_stats[i]);
+               seq_buf_printf(s, "total_%s %llu\n", memcg1_stat_names[i],
+                              (u64)nr);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(memcg1_events); i++)
+               seq_buf_printf(s, "total_%s %llu\n",
+                              vm_event_name(memcg1_events[i]),
+                              (u64)memcg_events(memcg, memcg1_events[i]));
+
+       for (i = 0; i < NR_LRU_LISTS; i++)
+               seq_buf_printf(s, "total_%s %llu\n", lru_list_name(i),
+                              (u64)memcg_page_state(memcg, NR_LRU_BASE + i) *
+                              PAGE_SIZE);
+
+#ifdef CONFIG_DEBUG_VM
+       {
+               pg_data_t *pgdat;
+               struct mem_cgroup_per_node *mz;
+               unsigned long anon_cost = 0;
+               unsigned long file_cost = 0;
+
+               for_each_online_pgdat(pgdat) {
+                       mz = memcg->nodeinfo[pgdat->node_id];
+
+                       anon_cost += mz->lruvec.anon_cost;
+                       file_cost += mz->lruvec.file_cost;
+               }
+               seq_buf_printf(s, "anon_cost %lu\n", anon_cost);
+               seq_buf_printf(s, "file_cost %lu\n", file_cost);
+       }
+#endif
+}
+
+static u64 mem_cgroup_swappiness_read(struct cgroup_subsys_state *css,
+                                     struct cftype *cft)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+
+       return mem_cgroup_swappiness(memcg);
+}
+
+static int mem_cgroup_swappiness_write(struct cgroup_subsys_state *css,
+                                      struct cftype *cft, u64 val)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+
+       if (val > 200)
+               return -EINVAL;
+
+       if (!mem_cgroup_is_root(memcg))
+               WRITE_ONCE(memcg->swappiness, val);
+       else
+               WRITE_ONCE(vm_swappiness, val);
+
+       return 0;
+}
+
+static int mem_cgroup_oom_control_read(struct seq_file *sf, void *v)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_seq(sf);
+
+       seq_printf(sf, "oom_kill_disable %d\n", READ_ONCE(memcg->oom_kill_disable));
+       seq_printf(sf, "under_oom %d\n", (bool)memcg->under_oom);
+       seq_printf(sf, "oom_kill %lu\n",
+                  atomic_long_read(&memcg->memory_events[MEMCG_OOM_KILL]));
+       return 0;
+}
+
+static int mem_cgroup_oom_control_write(struct cgroup_subsys_state *css,
+       struct cftype *cft, u64 val)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+
+       /* cannot set to root cgroup and only 0 and 1 are allowed */
+       if (mem_cgroup_is_root(memcg) || !((val == 0) || (val == 1)))
+               return -EINVAL;
+
+       WRITE_ONCE(memcg->oom_kill_disable, val);
+       if (!val)
+               memcg1_oom_recover(memcg);
+
+       return 0;
+}
+
+#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_SLUB_DEBUG)
+static int mem_cgroup_slab_show(struct seq_file *m, void *p)
+{
+       /*
+        * Deprecated.
+        * Please, take a look at tools/cgroup/memcg_slabinfo.py .
+        */
+       return 0;
+}
+#endif
+
+struct cftype mem_cgroup_legacy_files[] = {
+       {
+               .name = "usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_MEM, RES_USAGE),
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "max_usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_MEM, RES_MAX_USAGE),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "limit_in_bytes",
+               .private = MEMFILE_PRIVATE(_MEM, RES_LIMIT),
+               .write = mem_cgroup_write,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "soft_limit_in_bytes",
+               .private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT),
+               .write = mem_cgroup_write,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "failcnt",
+               .private = MEMFILE_PRIVATE(_MEM, RES_FAILCNT),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "stat",
+               .seq_show = memory_stat_show,
+       },
+       {
+               .name = "force_empty",
+               .write = mem_cgroup_force_empty_write,
+       },
+       {
+               .name = "use_hierarchy",
+               .write_u64 = mem_cgroup_hierarchy_write,
+               .read_u64 = mem_cgroup_hierarchy_read,
+       },
+       {
+               .name = "cgroup.event_control",         /* XXX: for compat */
+               .write = memcg_write_event_control,
+               .flags = CFTYPE_NO_PREFIX | CFTYPE_WORLD_WRITABLE,
+       },
+       {
+               .name = "swappiness",
+               .read_u64 = mem_cgroup_swappiness_read,
+               .write_u64 = mem_cgroup_swappiness_write,
+       },
+       {
+               .name = "move_charge_at_immigrate",
+               .read_u64 = mem_cgroup_move_charge_read,
+               .write_u64 = mem_cgroup_move_charge_write,
+       },
+       {
+               .name = "oom_control",
+               .seq_show = mem_cgroup_oom_control_read,
+               .write_u64 = mem_cgroup_oom_control_write,
+       },
+       {
+               .name = "pressure_level",
+               .seq_show = mem_cgroup_dummy_seq_show,
+       },
+#ifdef CONFIG_NUMA
+       {
+               .name = "numa_stat",
+               .seq_show = memcg_numa_stat_show,
+       },
+#endif
+       {
+               .name = "kmem.limit_in_bytes",
+               .private = MEMFILE_PRIVATE(_KMEM, RES_LIMIT),
+               .write = mem_cgroup_write,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "kmem.usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_KMEM, RES_USAGE),
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "kmem.failcnt",
+               .private = MEMFILE_PRIVATE(_KMEM, RES_FAILCNT),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "kmem.max_usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_KMEM, RES_MAX_USAGE),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_SLUB_DEBUG)
+       {
+               .name = "kmem.slabinfo",
+               .seq_show = mem_cgroup_slab_show,
+       },
+#endif
+       {
+               .name = "kmem.tcp.limit_in_bytes",
+               .private = MEMFILE_PRIVATE(_TCP, RES_LIMIT),
+               .write = mem_cgroup_write,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "kmem.tcp.usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_TCP, RES_USAGE),
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "kmem.tcp.failcnt",
+               .private = MEMFILE_PRIVATE(_TCP, RES_FAILCNT),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "kmem.tcp.max_usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_TCP, RES_MAX_USAGE),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       { },    /* terminate */
+};
+
+struct cftype memsw_files[] = {
+       {
+               .name = "memsw.usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE),
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "memsw.max_usage_in_bytes",
+               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_MAX_USAGE),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "memsw.limit_in_bytes",
+               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_LIMIT),
+               .write = mem_cgroup_write,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       {
+               .name = "memsw.failcnt",
+               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_FAILCNT),
+               .write = mem_cgroup_reset,
+               .read_u64 = mem_cgroup_read_u64,
+       },
+       { },    /* terminate */
+};
+
 static int __init memcg1_init(void)
 {
        int node;
index 972c493a8ae32d25d4f83eef833b5ef7ef9b07fd..0a4d5092c51b0db1bca73ddb7df8252178097f02 100644 (file)
@@ -3,6 +3,8 @@
 #ifndef __MM_MEMCONTROL_V1_H
 #define __MM_MEMCONTROL_V1_H
 
+#include <linux/cgroup-defs.h>
+
 void memcg1_update_tree(struct mem_cgroup *memcg, int nid);
 void memcg1_remove_from_trees(struct mem_cgroup *memcg);
 
@@ -34,12 +36,6 @@ int memcg1_can_attach(struct cgroup_taskset *tset);
 void memcg1_cancel_attach(struct cgroup_taskset *tset);
 void memcg1_move_task(void);
 
-struct cftype;
-u64 mem_cgroup_move_charge_read(struct cgroup_subsys_state *css,
-                               struct cftype *cft);
-int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css,
-                                struct cftype *cft, u64 val);
-
 /*
  * Per memcg event counter is incremented at every pagein/pageout. With THP,
  * it will be incremented by the number of pages. This counter is used
@@ -86,11 +82,23 @@ enum res_type {
 bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg,
                                enum mem_cgroup_events_target target);
 unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap);
-ssize_t memcg_write_event_control(struct kernfs_open_file *of,
-                                 char *buf, size_t nbytes, loff_t off);
 
 bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked);
 void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked);
 void memcg1_oom_recover(struct mem_cgroup *memcg);
 
+void drain_all_stock(struct mem_cgroup *root_memcg);
+
+unsigned long memcg_events(struct mem_cgroup *memcg, int event);
+unsigned long memcg_events_local(struct mem_cgroup *memcg, int event);
+unsigned long memcg_page_state_local(struct mem_cgroup *memcg, int idx);
+unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item);
+unsigned long memcg_page_state_local_output(struct mem_cgroup *memcg, int item);
+int memory_stat_show(struct seq_file *m, void *v);
+
+void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s);
+
+extern struct cftype memsw_files[];
+extern struct cftype mem_cgroup_legacy_files[];
+
 #endif /* __MM_MEMCONTROL_V1_H */
index 4f40f06e2317fa626037c61490bed707f6fc8673..b47c00f603327760e6d198b6b26b3a8cdebb0afe 100644 (file)
@@ -95,10 +95,6 @@ static DECLARE_WAIT_QUEUE_HEAD(memcg_cgwb_frn_waitq);
 #define THRESHOLDS_EVENTS_TARGET 128
 #define SOFTLIMIT_EVENTS_TARGET 1024
 
-#define MEMFILE_PRIVATE(x, val)        ((x) << 16 | (val))
-#define MEMFILE_TYPE(val)      ((val) >> 16 & 0xffff)
-#define MEMFILE_ATTR(val)      ((val) & 0xffff)
-
 static inline bool task_is_dying(void)
 {
        return tsk_is_oom_victim(current) || fatal_signal_pending(current) ||
@@ -675,7 +671,7 @@ void __mod_memcg_state(struct mem_cgroup *memcg, enum memcg_stat_item idx,
 }
 
 /* idx can be of type enum memcg_stat_item or node_stat_item. */
-static unsigned long memcg_page_state_local(struct mem_cgroup *memcg, int idx)
+unsigned long memcg_page_state_local(struct mem_cgroup *memcg, int idx)
 {
        long x;
        int i = memcg_stats_index(idx);
@@ -824,7 +820,7 @@ void __count_memcg_events(struct mem_cgroup *memcg, enum vm_event_item idx,
        memcg_stats_unlock();
 }
 
-static unsigned long memcg_events(struct mem_cgroup *memcg, int event)
+unsigned long memcg_events(struct mem_cgroup *memcg, int event)
 {
        int i = memcg_events_index(event);
 
@@ -834,7 +830,7 @@ static unsigned long memcg_events(struct mem_cgroup *memcg, int event)
        return READ_ONCE(memcg->vmstats->events[i]);
 }
 
-static unsigned long memcg_events_local(struct mem_cgroup *memcg, int event)
+unsigned long memcg_events_local(struct mem_cgroup *memcg, int event)
 {
        int i = memcg_events_index(event);
 
@@ -1419,15 +1415,13 @@ static int memcg_page_state_output_unit(int item)
        }
 }
 
-static inline unsigned long memcg_page_state_output(struct mem_cgroup *memcg,
-                                                   int item)
+unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item)
 {
        return memcg_page_state(memcg, item) *
                memcg_page_state_output_unit(item);
 }
 
-static inline unsigned long memcg_page_state_local_output(
-               struct mem_cgroup *memcg, int item)
+unsigned long memcg_page_state_local_output(struct mem_cgroup *memcg, int item)
 {
        return memcg_page_state_local(memcg, item) *
                memcg_page_state_output_unit(item);
@@ -1486,8 +1480,6 @@ static void memcg_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)
        WARN_ON_ONCE(seq_buf_has_overflowed(s));
 }
 
-static void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s);
-
 static void memory_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)
 {
        if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
@@ -1860,7 +1852,7 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
  * Drains all per-CPU charge caches for given root_memcg resp. subtree
  * of the hierarchy under it.
  */
-static void drain_all_stock(struct mem_cgroup *root_memcg)
+void drain_all_stock(struct mem_cgroup *root_memcg)
 {
        int cpu, curcpu;
 
@@ -3113,120 +3105,6 @@ void split_page_memcg(struct page *head, int old_order, int new_order)
                css_get_many(&memcg->css, old_nr / new_nr - 1);
 }
 
-
-static DEFINE_MUTEX(memcg_max_mutex);
-
-static int mem_cgroup_resize_max(struct mem_cgroup *memcg,
-                                unsigned long max, bool memsw)
-{
-       bool enlarge = false;
-       bool drained = false;
-       int ret;
-       bool limits_invariant;
-       struct page_counter *counter = memsw ? &memcg->memsw : &memcg->memory;
-
-       do {
-               if (signal_pending(current)) {
-                       ret = -EINTR;
-                       break;
-               }
-
-               mutex_lock(&memcg_max_mutex);
-               /*
-                * Make sure that the new limit (memsw or memory limit) doesn't
-                * break our basic invariant rule memory.max <= memsw.max.
-                */
-               limits_invariant = memsw ? max >= READ_ONCE(memcg->memory.max) :
-                                          max <= memcg->memsw.max;
-               if (!limits_invariant) {
-                       mutex_unlock(&memcg_max_mutex);
-                       ret = -EINVAL;
-                       break;
-               }
-               if (max > counter->max)
-                       enlarge = true;
-               ret = page_counter_set_max(counter, max);
-               mutex_unlock(&memcg_max_mutex);
-
-               if (!ret)
-                       break;
-
-               if (!drained) {
-                       drain_all_stock(memcg);
-                       drained = true;
-                       continue;
-               }
-
-               if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
-                                       memsw ? 0 : MEMCG_RECLAIM_MAY_SWAP)) {
-                       ret = -EBUSY;
-                       break;
-               }
-       } while (true);
-
-       if (!ret && enlarge)
-               memcg1_oom_recover(memcg);
-
-       return ret;
-}
-
-/*
- * Reclaims as many pages from the given memcg as possible.
- *
- * Caller is responsible for holding css reference for memcg.
- */
-static int mem_cgroup_force_empty(struct mem_cgroup *memcg)
-{
-       int nr_retries = MAX_RECLAIM_RETRIES;
-
-       /* we call try-to-free pages for make this cgroup empty */
-       lru_add_drain_all();
-
-       drain_all_stock(memcg);
-
-       /* try to free all pages in this cgroup */
-       while (nr_retries && page_counter_read(&memcg->memory)) {
-               if (signal_pending(current))
-                       return -EINTR;
-
-               if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
-                                                 MEMCG_RECLAIM_MAY_SWAP))
-                       nr_retries--;
-       }
-
-       return 0;
-}
-
-static ssize_t mem_cgroup_force_empty_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));
-
-       if (mem_cgroup_is_root(memcg))
-               return -EINVAL;
-       return mem_cgroup_force_empty(memcg) ?: nbytes;
-}
-
-static u64 mem_cgroup_hierarchy_read(struct cgroup_subsys_state *css,
-                                    struct cftype *cft)
-{
-       return 1;
-}
-
-static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
-                                     struct cftype *cft, u64 val)
-{
-       if (val == 1)
-               return 0;
-
-       pr_warn_once("Non-hierarchical mode is deprecated. "
-                    "Please report your usecase to linux-mm@kvack.org if you "
-                    "depend on this functionality.\n");
-
-       return -EINVAL;
-}
-
 unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
 {
        unsigned long val;
@@ -3249,67 +3127,6 @@ unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
        return val;
 }
 
-enum {
-       RES_USAGE,
-       RES_LIMIT,
-       RES_MAX_USAGE,
-       RES_FAILCNT,
-       RES_SOFT_LIMIT,
-};
-
-static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,
-                              struct cftype *cft)
-{
-       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
-       struct page_counter *counter;
-
-       switch (MEMFILE_TYPE(cft->private)) {
-       case _MEM:
-               counter = &memcg->memory;
-               break;
-       case _MEMSWAP:
-               counter = &memcg->memsw;
-               break;
-       case _KMEM:
-               counter = &memcg->kmem;
-               break;
-       case _TCP:
-               counter = &memcg->tcpmem;
-               break;
-       default:
-               BUG();
-       }
-
-       switch (MEMFILE_ATTR(cft->private)) {
-       case RES_USAGE:
-               if (counter == &memcg->memory)
-                       return (u64)mem_cgroup_usage(memcg, false) * PAGE_SIZE;
-               if (counter == &memcg->memsw)
-                       return (u64)mem_cgroup_usage(memcg, true) * PAGE_SIZE;
-               return (u64)page_counter_read(counter) * PAGE_SIZE;
-       case RES_LIMIT:
-               return (u64)counter->max * PAGE_SIZE;
-       case RES_MAX_USAGE:
-               return (u64)counter->watermark * PAGE_SIZE;
-       case RES_FAILCNT:
-               return counter->failcnt;
-       case RES_SOFT_LIMIT:
-               return (u64)READ_ONCE(memcg->soft_limit) * PAGE_SIZE;
-       default:
-               BUG();
-       }
-}
-
-/*
- * This function doesn't do anything useful. Its only job is to provide a read
- * handler for a file so that cgroup_file_mode() will add read permissions.
- */
-static int mem_cgroup_dummy_seq_show(__always_unused struct seq_file *m,
-                                    __always_unused void *v)
-{
-       return -EINVAL;
-}
-
 #ifdef CONFIG_MEMCG_KMEM
 static int memcg_online_kmem(struct mem_cgroup *memcg)
 {
@@ -3371,390 +3188,6 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg)
 }
 #endif /* CONFIG_MEMCG_KMEM */
 
-static int memcg_update_tcp_max(struct mem_cgroup *memcg, unsigned long max)
-{
-       int ret;
-
-       mutex_lock(&memcg_max_mutex);
-
-       ret = page_counter_set_max(&memcg->tcpmem, max);
-       if (ret)
-               goto out;
-
-       if (!memcg->tcpmem_active) {
-               /*
-                * The active flag needs to be written after the static_key
-                * update. This is what guarantees that the socket activation
-                * function is the last one to run. See mem_cgroup_sk_alloc()
-                * for details, and note that we don't mark any socket as
-                * belonging to this memcg until that flag is up.
-                *
-                * We need to do this, because static_keys will span multiple
-                * sites, but we can't control their order. If we mark a socket
-                * as accounted, but the accounting functions are not patched in
-                * yet, we'll lose accounting.
-                *
-                * We never race with the readers in mem_cgroup_sk_alloc(),
-                * because when this value change, the code to process it is not
-                * patched in yet.
-                */
-               static_branch_inc(&memcg_sockets_enabled_key);
-               memcg->tcpmem_active = true;
-       }
-out:
-       mutex_unlock(&memcg_max_mutex);
-       return ret;
-}
-
-/*
- * The user of this function is...
- * RES_LIMIT.
- */
-static ssize_t mem_cgroup_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));
-       unsigned long nr_pages;
-       int ret;
-
-       buf = strstrip(buf);
-       ret = page_counter_memparse(buf, "-1", &nr_pages);
-       if (ret)
-               return ret;
-
-       switch (MEMFILE_ATTR(of_cft(of)->private)) {
-       case RES_LIMIT:
-               if (mem_cgroup_is_root(memcg)) { /* Can't set limit on root */
-                       ret = -EINVAL;
-                       break;
-               }
-               switch (MEMFILE_TYPE(of_cft(of)->private)) {
-               case _MEM:
-                       ret = mem_cgroup_resize_max(memcg, nr_pages, false);
-                       break;
-               case _MEMSWAP:
-                       ret = mem_cgroup_resize_max(memcg, nr_pages, true);
-                       break;
-               case _KMEM:
-                       pr_warn_once("kmem.limit_in_bytes is deprecated and will be removed. "
-                                    "Writing any value to this file has no effect. "
-                                    "Please report your usecase to linux-mm@kvack.org if you "
-                                    "depend on this functionality.\n");
-                       ret = 0;
-                       break;
-               case _TCP:
-                       ret = memcg_update_tcp_max(memcg, nr_pages);
-                       break;
-               }
-               break;
-       case RES_SOFT_LIMIT:
-               if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
-                       ret = -EOPNOTSUPP;
-               } else {
-                       WRITE_ONCE(memcg->soft_limit, nr_pages);
-                       ret = 0;
-               }
-               break;
-       }
-       return ret ?: nbytes;
-}
-
-static ssize_t mem_cgroup_reset(struct kernfs_open_file *of, char *buf,
-                               size_t nbytes, loff_t off)
-{
-       struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
-       struct page_counter *counter;
-
-       switch (MEMFILE_TYPE(of_cft(of)->private)) {
-       case _MEM:
-               counter = &memcg->memory;
-               break;
-       case _MEMSWAP:
-               counter = &memcg->memsw;
-               break;
-       case _KMEM:
-               counter = &memcg->kmem;
-               break;
-       case _TCP:
-               counter = &memcg->tcpmem;
-               break;
-       default:
-               BUG();
-       }
-
-       switch (MEMFILE_ATTR(of_cft(of)->private)) {
-       case RES_MAX_USAGE:
-               page_counter_reset_watermark(counter);
-               break;
-       case RES_FAILCNT:
-               counter->failcnt = 0;
-               break;
-       default:
-               BUG();
-       }
-
-       return nbytes;
-}
-
-#ifdef CONFIG_NUMA
-
-#define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE))
-#define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON))
-#define LRU_ALL             ((1 << NR_LRU_LISTS) - 1)
-
-static unsigned long mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
-                               int nid, unsigned int lru_mask, bool tree)
-{
-       struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
-       unsigned long nr = 0;
-       enum lru_list lru;
-
-       VM_BUG_ON((unsigned)nid >= nr_node_ids);
-
-       for_each_lru(lru) {
-               if (!(BIT(lru) & lru_mask))
-                       continue;
-               if (tree)
-                       nr += lruvec_page_state(lruvec, NR_LRU_BASE + lru);
-               else
-                       nr += lruvec_page_state_local(lruvec, NR_LRU_BASE + lru);
-       }
-       return nr;
-}
-
-static unsigned long mem_cgroup_nr_lru_pages(struct mem_cgroup *memcg,
-                                            unsigned int lru_mask,
-                                            bool tree)
-{
-       unsigned long nr = 0;
-       enum lru_list lru;
-
-       for_each_lru(lru) {
-               if (!(BIT(lru) & lru_mask))
-                       continue;
-               if (tree)
-                       nr += memcg_page_state(memcg, NR_LRU_BASE + lru);
-               else
-                       nr += memcg_page_state_local(memcg, NR_LRU_BASE + lru);
-       }
-       return nr;
-}
-
-static int memcg_numa_stat_show(struct seq_file *m, void *v)
-{
-       struct numa_stat {
-               const char *name;
-               unsigned int lru_mask;
-       };
-
-       static const struct numa_stat stats[] = {
-               { "total", LRU_ALL },
-               { "file", LRU_ALL_FILE },
-               { "anon", LRU_ALL_ANON },
-               { "unevictable", BIT(LRU_UNEVICTABLE) },
-       };
-       const struct numa_stat *stat;
-       int nid;
-       struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
-
-       mem_cgroup_flush_stats(memcg);
-
-       for (stat = stats; stat < stats + ARRAY_SIZE(stats); stat++) {
-               seq_printf(m, "%s=%lu", stat->name,
-                          mem_cgroup_nr_lru_pages(memcg, stat->lru_mask,
-                                                  false));
-               for_each_node_state(nid, N_MEMORY)
-                       seq_printf(m, " N%d=%lu", nid,
-                                  mem_cgroup_node_nr_lru_pages(memcg, nid,
-                                                       stat->lru_mask, false));
-               seq_putc(m, '\n');
-       }
-
-       for (stat = stats; stat < stats + ARRAY_SIZE(stats); stat++) {
-
-               seq_printf(m, "hierarchical_%s=%lu", stat->name,
-                          mem_cgroup_nr_lru_pages(memcg, stat->lru_mask,
-                                                  true));
-               for_each_node_state(nid, N_MEMORY)
-                       seq_printf(m, " N%d=%lu", nid,
-                                  mem_cgroup_node_nr_lru_pages(memcg, nid,
-                                                       stat->lru_mask, true));
-               seq_putc(m, '\n');
-       }
-
-       return 0;
-}
-#endif /* CONFIG_NUMA */
-
-static const unsigned int memcg1_stats[] = {
-       NR_FILE_PAGES,
-       NR_ANON_MAPPED,
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-       NR_ANON_THPS,
-#endif
-       NR_SHMEM,
-       NR_FILE_MAPPED,
-       NR_FILE_DIRTY,
-       NR_WRITEBACK,
-       WORKINGSET_REFAULT_ANON,
-       WORKINGSET_REFAULT_FILE,
-#ifdef CONFIG_SWAP
-       MEMCG_SWAP,
-       NR_SWAPCACHE,
-#endif
-};
-
-static const char *const memcg1_stat_names[] = {
-       "cache",
-       "rss",
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-       "rss_huge",
-#endif
-       "shmem",
-       "mapped_file",
-       "dirty",
-       "writeback",
-       "workingset_refault_anon",
-       "workingset_refault_file",
-#ifdef CONFIG_SWAP
-       "swap",
-       "swapcached",
-#endif
-};
-
-/* Universal VM events cgroup1 shows, original sort order */
-static const unsigned int memcg1_events[] = {
-       PGPGIN,
-       PGPGOUT,
-       PGFAULT,
-       PGMAJFAULT,
-};
-
-static void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)
-{
-       unsigned long memory, memsw;
-       struct mem_cgroup *mi;
-       unsigned int i;
-
-       BUILD_BUG_ON(ARRAY_SIZE(memcg1_stat_names) != ARRAY_SIZE(memcg1_stats));
-
-       mem_cgroup_flush_stats(memcg);
-
-       for (i = 0; i < ARRAY_SIZE(memcg1_stats); i++) {
-               unsigned long nr;
-
-               nr = memcg_page_state_local_output(memcg, memcg1_stats[i]);
-               seq_buf_printf(s, "%s %lu\n", memcg1_stat_names[i], nr);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(memcg1_events); i++)
-               seq_buf_printf(s, "%s %lu\n", vm_event_name(memcg1_events[i]),
-                              memcg_events_local(memcg, memcg1_events[i]));
-
-       for (i = 0; i < NR_LRU_LISTS; i++)
-               seq_buf_printf(s, "%s %lu\n", lru_list_name(i),
-                              memcg_page_state_local(memcg, NR_LRU_BASE + i) *
-                              PAGE_SIZE);
-
-       /* Hierarchical information */
-       memory = memsw = PAGE_COUNTER_MAX;
-       for (mi = memcg; mi; mi = parent_mem_cgroup(mi)) {
-               memory = min(memory, READ_ONCE(mi->memory.max));
-               memsw = min(memsw, READ_ONCE(mi->memsw.max));
-       }
-       seq_buf_printf(s, "hierarchical_memory_limit %llu\n",
-                      (u64)memory * PAGE_SIZE);
-       seq_buf_printf(s, "hierarchical_memsw_limit %llu\n",
-                      (u64)memsw * PAGE_SIZE);
-
-       for (i = 0; i < ARRAY_SIZE(memcg1_stats); i++) {
-               unsigned long nr;
-
-               nr = memcg_page_state_output(memcg, memcg1_stats[i]);
-               seq_buf_printf(s, "total_%s %llu\n", memcg1_stat_names[i],
-                              (u64)nr);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(memcg1_events); i++)
-               seq_buf_printf(s, "total_%s %llu\n",
-                              vm_event_name(memcg1_events[i]),
-                              (u64)memcg_events(memcg, memcg1_events[i]));
-
-       for (i = 0; i < NR_LRU_LISTS; i++)
-               seq_buf_printf(s, "total_%s %llu\n", lru_list_name(i),
-                              (u64)memcg_page_state(memcg, NR_LRU_BASE + i) *
-                              PAGE_SIZE);
-
-#ifdef CONFIG_DEBUG_VM
-       {
-               pg_data_t *pgdat;
-               struct mem_cgroup_per_node *mz;
-               unsigned long anon_cost = 0;
-               unsigned long file_cost = 0;
-
-               for_each_online_pgdat(pgdat) {
-                       mz = memcg->nodeinfo[pgdat->node_id];
-
-                       anon_cost += mz->lruvec.anon_cost;
-                       file_cost += mz->lruvec.file_cost;
-               }
-               seq_buf_printf(s, "anon_cost %lu\n", anon_cost);
-               seq_buf_printf(s, "file_cost %lu\n", file_cost);
-       }
-#endif
-}
-
-static u64 mem_cgroup_swappiness_read(struct cgroup_subsys_state *css,
-                                     struct cftype *cft)
-{
-       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
-
-       return mem_cgroup_swappiness(memcg);
-}
-
-static int mem_cgroup_swappiness_write(struct cgroup_subsys_state *css,
-                                      struct cftype *cft, u64 val)
-{
-       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
-
-       if (val > 200)
-               return -EINVAL;
-
-       if (!mem_cgroup_is_root(memcg))
-               WRITE_ONCE(memcg->swappiness, val);
-       else
-               WRITE_ONCE(vm_swappiness, val);
-
-       return 0;
-}
-
-static int mem_cgroup_oom_control_read(struct seq_file *sf, void *v)
-{
-       struct mem_cgroup *memcg = mem_cgroup_from_seq(sf);
-
-       seq_printf(sf, "oom_kill_disable %d\n", READ_ONCE(memcg->oom_kill_disable));
-       seq_printf(sf, "under_oom %d\n", (bool)memcg->under_oom);
-       seq_printf(sf, "oom_kill %lu\n",
-                  atomic_long_read(&memcg->memory_events[MEMCG_OOM_KILL]));
-       return 0;
-}
-
-static int mem_cgroup_oom_control_write(struct cgroup_subsys_state *css,
-       struct cftype *cft, u64 val)
-{
-       struct mem_cgroup *memcg = mem_cgroup_from_css(css);
-
-       /* cannot set to root cgroup and only 0 and 1 are allowed */
-       if (mem_cgroup_is_root(memcg) || !((val == 0) || (val == 1)))
-               return -EINVAL;
-
-       WRITE_ONCE(memcg->oom_kill_disable, val);
-       if (!val)
-               memcg1_oom_recover(memcg);
-
-       return 0;
-}
-
 #ifdef CONFIG_CGROUP_WRITEBACK
 
 #include <trace/events/writeback.h>
@@ -3968,147 +3401,6 @@ static void memcg_wb_domain_size_changed(struct mem_cgroup *memcg)
 
 #endif /* CONFIG_CGROUP_WRITEBACK */
 
-#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_SLUB_DEBUG)
-static int mem_cgroup_slab_show(struct seq_file *m, void *p)
-{
-       /*
-        * Deprecated.
-        * Please, take a look at tools/cgroup/memcg_slabinfo.py .
-        */
-       return 0;
-}
-#endif
-
-static int memory_stat_show(struct seq_file *m, void *v);
-
-static struct cftype mem_cgroup_legacy_files[] = {
-       {
-               .name = "usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_MEM, RES_USAGE),
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "max_usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_MEM, RES_MAX_USAGE),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "limit_in_bytes",
-               .private = MEMFILE_PRIVATE(_MEM, RES_LIMIT),
-               .write = mem_cgroup_write,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "soft_limit_in_bytes",
-               .private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT),
-               .write = mem_cgroup_write,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "failcnt",
-               .private = MEMFILE_PRIVATE(_MEM, RES_FAILCNT),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "stat",
-               .seq_show = memory_stat_show,
-       },
-       {
-               .name = "force_empty",
-               .write = mem_cgroup_force_empty_write,
-       },
-       {
-               .name = "use_hierarchy",
-               .write_u64 = mem_cgroup_hierarchy_write,
-               .read_u64 = mem_cgroup_hierarchy_read,
-       },
-       {
-               .name = "cgroup.event_control",         /* XXX: for compat */
-               .write = memcg_write_event_control,
-               .flags = CFTYPE_NO_PREFIX | CFTYPE_WORLD_WRITABLE,
-       },
-       {
-               .name = "swappiness",
-               .read_u64 = mem_cgroup_swappiness_read,
-               .write_u64 = mem_cgroup_swappiness_write,
-       },
-       {
-               .name = "move_charge_at_immigrate",
-               .read_u64 = mem_cgroup_move_charge_read,
-               .write_u64 = mem_cgroup_move_charge_write,
-       },
-       {
-               .name = "oom_control",
-               .seq_show = mem_cgroup_oom_control_read,
-               .write_u64 = mem_cgroup_oom_control_write,
-       },
-       {
-               .name = "pressure_level",
-               .seq_show = mem_cgroup_dummy_seq_show,
-       },
-#ifdef CONFIG_NUMA
-       {
-               .name = "numa_stat",
-               .seq_show = memcg_numa_stat_show,
-       },
-#endif
-       {
-               .name = "kmem.limit_in_bytes",
-               .private = MEMFILE_PRIVATE(_KMEM, RES_LIMIT),
-               .write = mem_cgroup_write,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "kmem.usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_KMEM, RES_USAGE),
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "kmem.failcnt",
-               .private = MEMFILE_PRIVATE(_KMEM, RES_FAILCNT),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "kmem.max_usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_KMEM, RES_MAX_USAGE),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_SLUB_DEBUG)
-       {
-               .name = "kmem.slabinfo",
-               .seq_show = mem_cgroup_slab_show,
-       },
-#endif
-       {
-               .name = "kmem.tcp.limit_in_bytes",
-               .private = MEMFILE_PRIVATE(_TCP, RES_LIMIT),
-               .write = mem_cgroup_write,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "kmem.tcp.usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_TCP, RES_USAGE),
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "kmem.tcp.failcnt",
-               .private = MEMFILE_PRIVATE(_TCP, RES_FAILCNT),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "kmem.tcp.max_usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_TCP, RES_MAX_USAGE),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       { },    /* terminate */
-};
-
 /*
  * Private memory cgroup IDR
  *
@@ -4900,7 +4192,7 @@ static int memory_events_local_show(struct seq_file *m, void *v)
        return 0;
 }
 
-static int memory_stat_show(struct seq_file *m, void *v)
+int memory_stat_show(struct seq_file *m, void *v)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
        char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
@@ -6099,33 +5391,6 @@ static struct cftype swap_files[] = {
        { }     /* terminate */
 };
 
-static struct cftype memsw_files[] = {
-       {
-               .name = "memsw.usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE),
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "memsw.max_usage_in_bytes",
-               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_MAX_USAGE),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "memsw.limit_in_bytes",
-               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_LIMIT),
-               .write = mem_cgroup_write,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       {
-               .name = "memsw.failcnt",
-               .private = MEMFILE_PRIVATE(_MEMSWAP, RES_FAILCNT),
-               .write = mem_cgroup_reset,
-               .read_u64 = mem_cgroup_read_u64,
-       },
-       { },    /* terminate */
-};
-
 #if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_ZSWAP)
 /**
  * obj_cgroup_may_zswap - check if this cgroup can zswap