Commit bef8620c authored by Roman Gushchin's avatar Roman Gushchin Committed by Linus Torvalds

mm: memcg: deprecate the non-hierarchical mode

Patch series "mm: memcg: deprecate cgroup v1 non-hierarchical mode", v1.

The non-hierarchical cgroup v1 mode is a legacy of early days
of the memory controller and doesn't bring any value today.
However, it complicates the code and creates many edge cases
all over the memory controller code.

It's a good time to deprecate it completely. This patchset removes
the internal logic, adjusts the user interface and updates
the documentation. The alt patch removes some bits of the cgroup
core code, which become obsolete.

Michal Hocko said:
  "All that we know today is that we have a warning in place to complain
   loudly when somebody relies on use_hierarchy=0 with a deeper
   hierarchy. For all those years we have seen _zero_ reports that would
   describe a sensible usecase.

   Moreover we (SUSE) have backported this warning into old distribution
   kernels (since 3.0 based kernels) to extend the coverage and didn't
   hear even for users who adopt new kernels only very slowly. The only
   report we have seen so far was a LTP test suite which doesn't really
   reflect any real life usecase"

This patch (of 3):

The non-hierarchical cgroup v1 mode is a legacy of early days of the
memory controller and doesn't bring any value today.  However, it
complicates the code and creates many edge cases all over the memory
controller code.

It's a good time to deprecate it completely.

Functionally this patch enabled is by default for all cgroups and forbids
switching it off.  Nothing changes if cgroup v2 is used: hierarchical mode
was enforced from scratch.

To protect the ABI memory.use_hierarchy interface is preserved with a
limited functionality: reading always returns "1", writing of "1" passes
silently, writing of any other value fails with -EINVAL and a warning to
dmesg (on the first occasion).

Link: https://lkml.kernel.org/r/20201110220800.929549-1-guro@fb.com
Link: https://lkml.kernel.org/r/20201110220800.929549-2-guro@fb.comSigned-off-by: default avatarRoman Gushchin <guro@fb.com>
Acked-by: default avatarMichal Hocko <mhocko@suse.com>
Reviewed-by: default avatarShakeel Butt <shakeelb@google.com>
Acked-by: default avatarDavid Rientjes <rientjes@google.com>
Acked-by: default avatarJohannes Weiner <hannes@cmpxchg.org>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent a7cb874b
...@@ -234,11 +234,6 @@ struct mem_cgroup { ...@@ -234,11 +234,6 @@ struct mem_cgroup {
/* vmpressure notifications */ /* vmpressure notifications */
struct vmpressure vmpressure; struct vmpressure vmpressure;
/*
* Should the accounting and control be hierarchical, per subtree?
*/
bool use_hierarchy;
/* /*
* Should the OOM killer kill all belonging tasks, had it kill one? * Should the OOM killer kill all belonging tasks, had it kill one?
*/ */
...@@ -588,8 +583,6 @@ static inline bool mem_cgroup_is_descendant(struct mem_cgroup *memcg, ...@@ -588,8 +583,6 @@ static inline bool mem_cgroup_is_descendant(struct mem_cgroup *memcg,
{ {
if (root == memcg) if (root == memcg)
return true; return true;
if (!root->use_hierarchy)
return false;
return cgroup_is_descendant(memcg->css.cgroup, root->css.cgroup); return cgroup_is_descendant(memcg->css.cgroup, root->css.cgroup);
} }
......
...@@ -281,9 +281,6 @@ bool cgroup_ssid_enabled(int ssid) ...@@ -281,9 +281,6 @@ bool cgroup_ssid_enabled(int ssid)
* - cpuset: a task can be moved into an empty cpuset, and again it takes * - cpuset: a task can be moved into an empty cpuset, and again it takes
* masks of ancestors. * masks of ancestors.
* *
* - memcg: use_hierarchy is on by default and the cgroup file for the flag
* is not created.
*
* - blkcg: blk-throttle becomes properly hierarchical. * - blkcg: blk-throttle becomes properly hierarchical.
* *
* - debug: disallowed on the default hierarchy. * - debug: disallowed on the default hierarchy.
...@@ -5156,8 +5153,6 @@ static struct cgroup_subsys_state *css_create(struct cgroup *cgrp, ...@@ -5156,8 +5153,6 @@ static struct cgroup_subsys_state *css_create(struct cgroup *cgrp,
cgroup_parent(parent)) { cgroup_parent(parent)) {
pr_warn("%s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n", pr_warn("%s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n",
current->comm, current->pid, ss->name); current->comm, current->pid, ss->name);
if (!strcmp(ss->name, "memory"))
pr_warn("\"memory\" requires setting use_hierarchy to 1 on the root\n");
ss->warned_broken_hierarchy = true; ss->warned_broken_hierarchy = true;
} }
......
...@@ -1141,12 +1141,6 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, ...@@ -1141,12 +1141,6 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
if (prev && !reclaim) if (prev && !reclaim)
pos = prev; pos = prev;
if (!root->use_hierarchy && root != root_mem_cgroup) {
if (prev)
goto out;
return root;
}
rcu_read_lock(); rcu_read_lock();
if (reclaim) { if (reclaim) {
...@@ -1226,7 +1220,6 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, ...@@ -1226,7 +1220,6 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
out:
if (prev && prev != root) if (prev && prev != root)
css_put(&prev->css); css_put(&prev->css);
...@@ -3461,10 +3454,7 @@ unsigned long mem_cgroup_soft_limit_reclaim(pg_data_t *pgdat, int order, ...@@ -3461,10 +3454,7 @@ unsigned long mem_cgroup_soft_limit_reclaim(pg_data_t *pgdat, int order,
} }
/* /*
* Test whether @memcg has children, dead or alive. Note that this * Test whether @memcg has children, dead or alive.
* function doesn't care whether @memcg has use_hierarchy enabled and
* returns %true if there are child csses according to the cgroup
* hierarchy. Testing use_hierarchy is the caller's responsibility.
*/ */
static inline bool memcg_has_children(struct mem_cgroup *memcg) static inline bool memcg_has_children(struct mem_cgroup *memcg)
{ {
...@@ -3524,37 +3514,20 @@ static ssize_t mem_cgroup_force_empty_write(struct kernfs_open_file *of, ...@@ -3524,37 +3514,20 @@ static ssize_t mem_cgroup_force_empty_write(struct kernfs_open_file *of,
static u64 mem_cgroup_hierarchy_read(struct cgroup_subsys_state *css, static u64 mem_cgroup_hierarchy_read(struct cgroup_subsys_state *css,
struct cftype *cft) struct cftype *cft)
{ {
return mem_cgroup_from_css(css)->use_hierarchy; return 1;
} }
static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css, static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
struct cftype *cft, u64 val) struct cftype *cft, u64 val)
{ {
int retval = 0; if (val == 1)
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
struct mem_cgroup *parent_memcg = mem_cgroup_from_css(memcg->css.parent);
if (memcg->use_hierarchy == val)
return 0; return 0;
/* pr_warn_once("Non-hierarchical mode is deprecated. "
* If parent's use_hierarchy is set, we can't make any modifications "Please report your usecase to linux-mm@kvack.org if you "
* in the child subtrees. If it is unset, then the change can "depend on this functionality.\n");
* occur, provided the current cgroup has no children.
*
* For the root cgroup, parent_mem is NULL, we allow value to be
* set if there are no children.
*/
if ((!parent_memcg || !parent_memcg->use_hierarchy) &&
(val == 1 || val == 0)) {
if (!memcg_has_children(memcg))
memcg->use_hierarchy = val;
else
retval = -EBUSY;
} else
retval = -EINVAL;
return retval; return -EINVAL;
} }
static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap) static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
...@@ -3742,8 +3715,6 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg) ...@@ -3742,8 +3715,6 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg)
child = mem_cgroup_from_css(css); child = mem_cgroup_from_css(css);
BUG_ON(child->kmemcg_id != kmemcg_id); BUG_ON(child->kmemcg_id != kmemcg_id);
child->kmemcg_id = parent->kmemcg_id; child->kmemcg_id = parent->kmemcg_id;
if (!memcg->use_hierarchy)
break;
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -5334,38 +5305,22 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) ...@@ -5334,38 +5305,22 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
if (parent) { if (parent) {
memcg->swappiness = mem_cgroup_swappiness(parent); memcg->swappiness = mem_cgroup_swappiness(parent);
memcg->oom_kill_disable = parent->oom_kill_disable; memcg->oom_kill_disable = parent->oom_kill_disable;
}
if (!parent) {
page_counter_init(&memcg->memory, NULL);
page_counter_init(&memcg->swap, NULL);
page_counter_init(&memcg->kmem, NULL);
page_counter_init(&memcg->tcpmem, NULL);
} else if (parent->use_hierarchy) {
memcg->use_hierarchy = true;
page_counter_init(&memcg->memory, &parent->memory); page_counter_init(&memcg->memory, &parent->memory);
page_counter_init(&memcg->swap, &parent->swap); page_counter_init(&memcg->swap, &parent->swap);
page_counter_init(&memcg->kmem, &parent->kmem); page_counter_init(&memcg->kmem, &parent->kmem);
page_counter_init(&memcg->tcpmem, &parent->tcpmem); page_counter_init(&memcg->tcpmem, &parent->tcpmem);
} else { } else {
page_counter_init(&memcg->memory, &root_mem_cgroup->memory); page_counter_init(&memcg->memory, NULL);
page_counter_init(&memcg->swap, &root_mem_cgroup->swap); page_counter_init(&memcg->swap, NULL);
page_counter_init(&memcg->kmem, &root_mem_cgroup->kmem); page_counter_init(&memcg->kmem, NULL);
page_counter_init(&memcg->tcpmem, &root_mem_cgroup->tcpmem); page_counter_init(&memcg->tcpmem, NULL);
/*
* Deeper hierachy with use_hierarchy == false doesn't make
* much sense so let cgroup subsystem know about this
* unfortunate state in our controller.
*/
if (parent != root_mem_cgroup)
memory_cgrp_subsys.broken_hierarchy = true;
}
/* The following stuff does not apply to the root */
if (!parent) {
root_mem_cgroup = memcg; root_mem_cgroup = memcg;
return &memcg->css; return &memcg->css;
} }
/* The following stuff does not apply to the root */
error = memcg_online_kmem(memcg); error = memcg_online_kmem(memcg);
if (error) if (error)
goto fail; goto fail;
...@@ -6202,24 +6157,6 @@ static void mem_cgroup_move_task(void) ...@@ -6202,24 +6157,6 @@ static void mem_cgroup_move_task(void)
} }
#endif #endif
/*
* Cgroup retains root cgroups across [un]mount cycles making it necessary
* to verify whether we're attached to the default hierarchy on each mount
* attempt.
*/
static void mem_cgroup_bind(struct cgroup_subsys_state *root_css)
{
/*
* use_hierarchy is forced on the default hierarchy. cgroup core
* guarantees that @root doesn't have any children, so turning it
* on for the root memcg is enough.
*/
if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
root_mem_cgroup->use_hierarchy = true;
else
root_mem_cgroup->use_hierarchy = false;
}
static int seq_puts_memcg_tunable(struct seq_file *m, unsigned long value) static int seq_puts_memcg_tunable(struct seq_file *m, unsigned long value)
{ {
if (value == PAGE_COUNTER_MAX) if (value == PAGE_COUNTER_MAX)
...@@ -6557,7 +6494,6 @@ struct cgroup_subsys memory_cgrp_subsys = { ...@@ -6557,7 +6494,6 @@ struct cgroup_subsys memory_cgrp_subsys = {
.can_attach = mem_cgroup_can_attach, .can_attach = mem_cgroup_can_attach,
.cancel_attach = mem_cgroup_cancel_attach, .cancel_attach = mem_cgroup_cancel_attach,
.post_attach = mem_cgroup_move_task, .post_attach = mem_cgroup_move_task,
.bind = mem_cgroup_bind,
.dfl_cftypes = memory_files, .dfl_cftypes = memory_files,
.legacy_cftypes = mem_cgroup_legacy_files, .legacy_cftypes = mem_cgroup_legacy_files,
.early_init = 0, .early_init = 0,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment