From a28f8f5e995fe5964ae304444913536058f26e37 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Tue, 13 Jun 2017 17:18:02 -0400 Subject: cgroup: Move debug cgroup to its own file The debug cgroup currently resides within cgroup-v1.c and is enabled only for v1 cgroup. To enable the debug cgroup also for v2, it makes sense to put the code into its own file as it will no longer be v1 specific. There is no change to the debug cgroup specific code. Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/debug.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 kernel/cgroup/debug.c (limited to 'kernel/cgroup/debug.c') diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c new file mode 100644 index 000000000000..1c209aa43733 --- /dev/null +++ b/kernel/cgroup/debug.c @@ -0,0 +1,153 @@ +#include +#include +#include + +#include "cgroup-internal.h" + +static struct cgroup_subsys_state * +debug_css_alloc(struct cgroup_subsys_state *parent_css) +{ + struct cgroup_subsys_state *css = kzalloc(sizeof(*css), GFP_KERNEL); + + if (!css) + return ERR_PTR(-ENOMEM); + + return css; +} + +static void debug_css_free(struct cgroup_subsys_state *css) +{ + kfree(css); +} + +/* + * debug_taskcount_read - return the number of tasks in a cgroup. + * @cgrp: the cgroup in question + */ +static u64 debug_taskcount_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return cgroup_task_count(css->cgroup); +} + +static u64 current_css_set_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return (u64)(unsigned long)current->cgroups; +} + +static u64 current_css_set_refcount_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + u64 count; + + rcu_read_lock(); + count = refcount_read(&task_css_set(current)->refcount); + rcu_read_unlock(); + return count; +} + +static int current_css_set_cg_links_read(struct seq_file *seq, void *v) +{ + struct cgrp_cset_link *link; + struct css_set *cset; + char *name_buf; + + name_buf = kmalloc(NAME_MAX + 1, GFP_KERNEL); + if (!name_buf) + return -ENOMEM; + + spin_lock_irq(&css_set_lock); + rcu_read_lock(); + cset = rcu_dereference(current->cgroups); + list_for_each_entry(link, &cset->cgrp_links, cgrp_link) { + struct cgroup *c = link->cgrp; + + cgroup_name(c, name_buf, NAME_MAX + 1); + seq_printf(seq, "Root %d group %s\n", + c->root->hierarchy_id, name_buf); + } + rcu_read_unlock(); + spin_unlock_irq(&css_set_lock); + kfree(name_buf); + return 0; +} + +#define MAX_TASKS_SHOWN_PER_CSS 25 +static int cgroup_css_links_read(struct seq_file *seq, void *v) +{ + struct cgroup_subsys_state *css = seq_css(seq); + struct cgrp_cset_link *link; + + spin_lock_irq(&css_set_lock); + list_for_each_entry(link, &css->cgroup->cset_links, cset_link) { + struct css_set *cset = link->cset; + struct task_struct *task; + int count = 0; + + seq_printf(seq, "css_set %pK\n", cset); + + list_for_each_entry(task, &cset->tasks, cg_list) { + if (count++ > MAX_TASKS_SHOWN_PER_CSS) + goto overflow; + seq_printf(seq, " task %d\n", task_pid_vnr(task)); + } + + list_for_each_entry(task, &cset->mg_tasks, cg_list) { + if (count++ > MAX_TASKS_SHOWN_PER_CSS) + goto overflow; + seq_printf(seq, " task %d\n", task_pid_vnr(task)); + } + continue; + overflow: + seq_puts(seq, " ...\n"); + } + spin_unlock_irq(&css_set_lock); + return 0; +} + +static u64 releasable_read(struct cgroup_subsys_state *css, struct cftype *cft) +{ + return (!cgroup_is_populated(css->cgroup) && + !css_has_online_children(&css->cgroup->self)); +} + +static struct cftype debug_files[] = { + { + .name = "taskcount", + .read_u64 = debug_taskcount_read, + }, + + { + .name = "current_css_set", + .read_u64 = current_css_set_read, + }, + + { + .name = "current_css_set_refcount", + .read_u64 = current_css_set_refcount_read, + }, + + { + .name = "current_css_set_cg_links", + .seq_show = current_css_set_cg_links_read, + }, + + { + .name = "cgroup_css_links", + .seq_show = cgroup_css_links_read, + }, + + { + .name = "releasable", + .read_u64 = releasable_read, + }, + + { } /* terminate */ +}; + +struct cgroup_subsys debug_cgrp_subsys = { + .css_alloc = debug_css_alloc, + .css_free = debug_css_free, + .legacy_cftypes = debug_files, +}; -- cgit From 23b0be480f341db26ce0dee7d3f6e67f8e0e166f Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Tue, 13 Jun 2017 17:18:03 -0400 Subject: cgroup: Make Kconfig prompt of debug cgroup more accurate The Kconfig prompt and description of the debug cgroup controller more accurate by saying that it is for debug purpose only and its interfaces are unstable. Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- init/Kconfig | 7 +++++-- kernel/cgroup/debug.c | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'kernel/cgroup/debug.c') diff --git a/init/Kconfig b/init/Kconfig index 1d3475fc9496..5f9d13929ae0 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1205,11 +1205,14 @@ config CGROUP_BPF inet sockets. config CGROUP_DEBUG - bool "Example controller" + bool "Debug controller" default n + depends on DEBUG_KERNEL help This option enables a simple controller that exports - debugging information about the cgroups framework. + debugging information about the cgroups framework. This + controller is for control cgroup debugging only. Its + interfaces are not stable. Say N. diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c index 1c209aa43733..cbe77a2087c5 100644 --- a/kernel/cgroup/debug.c +++ b/kernel/cgroup/debug.c @@ -1,3 +1,9 @@ +/* + * Debug controller + * + * WARNING: This controller is for cgroup core debugging only. + * Its interfaces are unstable and subject to changes at any time. + */ #include #include #include -- cgit From 575313f40ff33d0c2aff2701dfb2ccfcd6211d55 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Tue, 13 Jun 2017 17:18:04 -0400 Subject: cgroup: Make debug cgroup support v2 and thread mode Besides supporting cgroup v2 and thread mode, the following changes are also made: 1) current_* cgroup files now resides only at the root as we don't need duplicated files of the same function all over the cgroup hierarchy. 2) The cgroup_css_links_read() function is modified to report the number of tasks that are skipped because of overflow. 3) The number of extra unaccounted references are displayed. 4) The current_css_set_read() function now prints out the addresses of the css'es associated with the current css_set. 5) A new cgroup_subsys_states file is added to display the css objects associated with a cgroup. 6) A new cgroup_masks file is added to display the various controller bit masks in the cgroup. tj: Dropped thread mode related information for now so that debug controller changes aren't blocked on the thread mode. Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/debug.c | 170 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 153 insertions(+), 17 deletions(-) (limited to 'kernel/cgroup/debug.c') diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c index cbe77a2087c5..057d9b07f461 100644 --- a/kernel/cgroup/debug.c +++ b/kernel/cgroup/debug.c @@ -36,10 +36,37 @@ static u64 debug_taskcount_read(struct cgroup_subsys_state *css, return cgroup_task_count(css->cgroup); } -static u64 current_css_set_read(struct cgroup_subsys_state *css, - struct cftype *cft) +static int current_css_set_read(struct seq_file *seq, void *v) { - return (u64)(unsigned long)current->cgroups; + struct css_set *cset; + struct cgroup_subsys *ss; + struct cgroup_subsys_state *css; + int i, refcnt; + + mutex_lock(&cgroup_mutex); + spin_lock_irq(&css_set_lock); + rcu_read_lock(); + cset = rcu_dereference(current->cgroups); + refcnt = refcount_read(&cset->refcount); + seq_printf(seq, "css_set %pK %d", cset, refcnt); + if (refcnt > cset->nr_tasks) + seq_printf(seq, " +%d", refcnt - cset->nr_tasks); + seq_puts(seq, "\n"); + + /* + * Print the css'es stored in the current css_set. + */ + for_each_subsys(ss, i) { + css = cset->subsys[ss->id]; + if (!css) + continue; + seq_printf(seq, "%2d: %-4s\t- %lx[%d]\n", ss->id, ss->name, + (unsigned long)css, css->id); + } + rcu_read_unlock(); + spin_unlock_irq(&css_set_lock); + mutex_unlock(&cgroup_mutex); + return 0; } static u64 current_css_set_refcount_read(struct cgroup_subsys_state *css, @@ -84,31 +111,126 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v) { struct cgroup_subsys_state *css = seq_css(seq); struct cgrp_cset_link *link; + int dead_cnt = 0, extra_refs = 0; spin_lock_irq(&css_set_lock); list_for_each_entry(link, &css->cgroup->cset_links, cset_link) { struct css_set *cset = link->cset; struct task_struct *task; int count = 0; + int refcnt = refcount_read(&cset->refcount); - seq_printf(seq, "css_set %pK\n", cset); + seq_printf(seq, " %d", refcnt); + if (refcnt - cset->nr_tasks > 0) { + int extra = refcnt - cset->nr_tasks; + + seq_printf(seq, " +%d", extra); + /* + * Take out the one additional reference in + * init_css_set. + */ + if (cset == &init_css_set) + extra--; + extra_refs += extra; + } + seq_puts(seq, "\n"); list_for_each_entry(task, &cset->tasks, cg_list) { - if (count++ > MAX_TASKS_SHOWN_PER_CSS) - goto overflow; - seq_printf(seq, " task %d\n", task_pid_vnr(task)); + if (count++ <= MAX_TASKS_SHOWN_PER_CSS) + seq_printf(seq, " task %d\n", + task_pid_vnr(task)); } list_for_each_entry(task, &cset->mg_tasks, cg_list) { - if (count++ > MAX_TASKS_SHOWN_PER_CSS) - goto overflow; - seq_printf(seq, " task %d\n", task_pid_vnr(task)); + if (count++ <= MAX_TASKS_SHOWN_PER_CSS) + seq_printf(seq, " task %d\n", + task_pid_vnr(task)); } - continue; - overflow: - seq_puts(seq, " ...\n"); + /* show # of overflowed tasks */ + if (count > MAX_TASKS_SHOWN_PER_CSS) + seq_printf(seq, " ... (%d)\n", + count - MAX_TASKS_SHOWN_PER_CSS); + + if (cset->dead) { + seq_puts(seq, " [dead]\n"); + dead_cnt++; + } + + WARN_ON(count != cset->nr_tasks); } spin_unlock_irq(&css_set_lock); + + if (!dead_cnt && !extra_refs) + return 0; + + seq_puts(seq, "\n"); + if (extra_refs) + seq_printf(seq, "extra references = %d\n", extra_refs); + if (dead_cnt) + seq_printf(seq, "dead css_sets = %d\n", dead_cnt); + + return 0; +} + +static int cgroup_subsys_states_read(struct seq_file *seq, void *v) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + struct cgroup_subsys *ss; + struct cgroup_subsys_state *css; + char pbuf[16]; + int i; + + mutex_lock(&cgroup_mutex); + for_each_subsys(ss, i) { + css = rcu_dereference_check(cgrp->subsys[ss->id], true); + if (!css) + continue; + + pbuf[0] = '\0'; + + /* Show the parent CSS if applicable*/ + if (css->parent) + snprintf(pbuf, sizeof(pbuf) - 1, " P=%d", + css->parent->id); + seq_printf(seq, "%2d: %-4s\t- %lx[%d] %d%s\n", ss->id, ss->name, + (unsigned long)css, css->id, + atomic_read(&css->online_cnt), pbuf); + } + mutex_unlock(&cgroup_mutex); + return 0; +} + +static int cgroup_masks_read(struct seq_file *seq, void *v) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + struct cgroup_subsys *ss; + int i, j; + struct { + u16 *mask; + char *name; + } mask_list[] = { + { &cgrp->subtree_control, "subtree_control" }, + { &cgrp->subtree_ss_mask, "subtree_ss_mask" }, + }; + + mutex_lock(&cgroup_mutex); + for (i = 0; i < ARRAY_SIZE(mask_list); i++) { + u16 mask = *mask_list[i].mask; + bool first = true; + + seq_printf(seq, "%-17s: ", mask_list[i].name); + for_each_subsys(ss, j) { + if (!(mask & (1 << ss->id))) + continue; + if (!first) + seq_puts(seq, ", "); + seq_puts(seq, ss->name); + first = false; + } + seq_putc(seq, '\n'); + } + + mutex_unlock(&cgroup_mutex); return 0; } @@ -126,17 +248,20 @@ static struct cftype debug_files[] = { { .name = "current_css_set", - .read_u64 = current_css_set_read, + .seq_show = current_css_set_read, + .flags = CFTYPE_ONLY_ON_ROOT, }, { .name = "current_css_set_refcount", .read_u64 = current_css_set_refcount_read, + .flags = CFTYPE_ONLY_ON_ROOT, }, { .name = "current_css_set_cg_links", .seq_show = current_css_set_cg_links_read, + .flags = CFTYPE_ONLY_ON_ROOT, }, { @@ -144,6 +269,16 @@ static struct cftype debug_files[] = { .seq_show = cgroup_css_links_read, }, + { + .name = "cgroup_subsys_states", + .seq_show = cgroup_subsys_states_read, + }, + + { + .name = "cgroup_masks", + .seq_show = cgroup_masks_read, + }, + { .name = "releasable", .read_u64 = releasable_read, @@ -153,7 +288,8 @@ static struct cftype debug_files[] = { }; struct cgroup_subsys debug_cgrp_subsys = { - .css_alloc = debug_css_alloc, - .css_free = debug_css_free, - .legacy_cftypes = debug_files, + .css_alloc = debug_css_alloc, + .css_free = debug_css_free, + .legacy_cftypes = debug_files, + .dfl_cftypes = debug_files, }; -- cgit From 8cc38fa7fa317d44710f24475576b1f9ee205da9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 Jun 2017 16:01:32 -0400 Subject: cgroup: make debug an implicit controller on cgroup2 Make debug an implicit controller on cgroup2 which is enabled by "cgroup_debug" boot param. Signed-off-by: Tejun Heo Cc: Waiman Long --- kernel/cgroup/debug.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) (limited to 'kernel/cgroup/debug.c') diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c index 057d9b07f461..d61e692a5338 100644 --- a/kernel/cgroup/debug.c +++ b/kernel/cgroup/debug.c @@ -240,7 +240,7 @@ static u64 releasable_read(struct cgroup_subsys_state *css, struct cftype *cft) !css_has_online_children(&css->cgroup->self)); } -static struct cftype debug_files[] = { +static struct cftype debug_legacy_files[] = { { .name = "taskcount", .read_u64 = debug_taskcount_read, @@ -287,9 +287,62 @@ static struct cftype debug_files[] = { { } /* terminate */ }; +static struct cftype debug_files[] = { + { + .name = "taskcount", + .read_u64 = debug_taskcount_read, + }, + + { + .name = "current_css_set", + .seq_show = current_css_set_read, + .flags = CFTYPE_ONLY_ON_ROOT, + }, + + { + .name = "current_css_set_refcount", + .read_u64 = current_css_set_refcount_read, + .flags = CFTYPE_ONLY_ON_ROOT, + }, + + { + .name = "current_css_set_cg_links", + .seq_show = current_css_set_cg_links_read, + .flags = CFTYPE_ONLY_ON_ROOT, + }, + + { + .name = "css_links", + .seq_show = cgroup_css_links_read, + }, + + { + .name = "csses", + .seq_show = cgroup_subsys_states_read, + }, + + { + .name = "masks", + .seq_show = cgroup_masks_read, + }, + + { } /* terminate */ +}; + struct cgroup_subsys debug_cgrp_subsys = { .css_alloc = debug_css_alloc, .css_free = debug_css_free, - .legacy_cftypes = debug_files, - .dfl_cftypes = debug_files, + .legacy_cftypes = debug_legacy_files, }; + +/* + * On v2, debug is an implicit controller enabled by "cgroup_debug" boot + * parameter. + */ +static int __init enable_cgroup_debug(char *str) +{ + debug_cgrp_subsys.dfl_cftypes = debug_files; + debug_cgrp_subsys.implicit_on_dfl = true; + return 1; +} +__setup("cgroup_debug", enable_cgroup_debug); -- cgit From 2866c0b4cf25274040f0b4cc045ad1f44b885dd8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 Jun 2017 16:01:36 -0400 Subject: cgroup: refactor cgroup_masks_read() in the debug controller Factor out cgroup_masks_read_one() out of cgroup_masks_read() for simplicity. Signed-off-by: Tejun Heo Cc: Waiman Long --- kernel/cgroup/debug.c | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'kernel/cgroup/debug.c') diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c index d61e692a5338..163fdbd7adf6 100644 --- a/kernel/cgroup/debug.c +++ b/kernel/cgroup/debug.c @@ -200,36 +200,32 @@ static int cgroup_subsys_states_read(struct seq_file *seq, void *v) return 0; } -static int cgroup_masks_read(struct seq_file *seq, void *v) +static void cgroup_masks_read_one(struct seq_file *seq, const char *name, + u16 mask) { - struct cgroup *cgrp = seq_css(seq)->cgroup; struct cgroup_subsys *ss; - int i, j; - struct { - u16 *mask; - char *name; - } mask_list[] = { - { &cgrp->subtree_control, "subtree_control" }, - { &cgrp->subtree_ss_mask, "subtree_ss_mask" }, - }; + int ssid; + bool first = true; - mutex_lock(&cgroup_mutex); - for (i = 0; i < ARRAY_SIZE(mask_list); i++) { - u16 mask = *mask_list[i].mask; - bool first = true; - - seq_printf(seq, "%-17s: ", mask_list[i].name); - for_each_subsys(ss, j) { - if (!(mask & (1 << ss->id))) - continue; - if (!first) - seq_puts(seq, ", "); - seq_puts(seq, ss->name); - first = false; - } - seq_putc(seq, '\n'); + seq_printf(seq, "%-17s: ", name); + for_each_subsys(ss, ssid) { + if (!(mask & (1 << ssid))) + continue; + if (!first) + seq_puts(seq, ", "); + seq_puts(seq, ss->name); + first = false; } + seq_putc(seq, '\n'); +} +static int cgroup_masks_read(struct seq_file *seq, void *v) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + + mutex_lock(&cgroup_mutex); + cgroup_masks_read_one(seq, "subtree_control", cgrp->subtree_control); + cgroup_masks_read_one(seq, "subtree_ss_mask", cgrp->subtree_ss_mask); mutex_unlock(&cgroup_mutex); return 0; } -- cgit From b6053d40e3387c916d56d319ba6bfdb2c42a67c7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 Jun 2017 16:01:41 -0400 Subject: cgroup: fix lockdep warning in debug controller The debug controller grabs cgroup_mutex from interface file show functions which can deadlock and triggers lockdep warnings. Fix it by using cgroup_kn_lock_live()/cgroup_kn_unlock() instead. Signed-off-by: Tejun Heo Cc: Waiman Long --- kernel/cgroup/debug.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'kernel/cgroup/debug.c') diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c index 163fdbd7adf6..dac46af22782 100644 --- a/kernel/cgroup/debug.c +++ b/kernel/cgroup/debug.c @@ -38,12 +38,15 @@ static u64 debug_taskcount_read(struct cgroup_subsys_state *css, static int current_css_set_read(struct seq_file *seq, void *v) { + struct kernfs_open_file *of = seq->private; struct css_set *cset; struct cgroup_subsys *ss; struct cgroup_subsys_state *css; int i, refcnt; - mutex_lock(&cgroup_mutex); + if (!cgroup_kn_lock_live(of->kn, false)) + return -ENODEV; + spin_lock_irq(&css_set_lock); rcu_read_lock(); cset = rcu_dereference(current->cgroups); @@ -65,7 +68,7 @@ static int current_css_set_read(struct seq_file *seq, void *v) } rcu_read_unlock(); spin_unlock_irq(&css_set_lock); - mutex_unlock(&cgroup_mutex); + cgroup_kn_unlock(of->kn); return 0; } @@ -174,13 +177,17 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v) static int cgroup_subsys_states_read(struct seq_file *seq, void *v) { - struct cgroup *cgrp = seq_css(seq)->cgroup; + struct kernfs_open_file *of = seq->private; + struct cgroup *cgrp; struct cgroup_subsys *ss; struct cgroup_subsys_state *css; char pbuf[16]; int i; - mutex_lock(&cgroup_mutex); + cgrp = cgroup_kn_lock_live(of->kn, false); + if (!cgrp) + return -ENODEV; + for_each_subsys(ss, i) { css = rcu_dereference_check(cgrp->subsys[ss->id], true); if (!css) @@ -196,7 +203,8 @@ static int cgroup_subsys_states_read(struct seq_file *seq, void *v) (unsigned long)css, css->id, atomic_read(&css->online_cnt), pbuf); } - mutex_unlock(&cgroup_mutex); + + cgroup_kn_unlock(of->kn); return 0; } @@ -221,12 +229,17 @@ static void cgroup_masks_read_one(struct seq_file *seq, const char *name, static int cgroup_masks_read(struct seq_file *seq, void *v) { - struct cgroup *cgrp = seq_css(seq)->cgroup; + struct kernfs_open_file *of = seq->private; + struct cgroup *cgrp; + + cgrp = cgroup_kn_lock_live(of->kn, false); + if (!cgrp) + return -ENODEV; - mutex_lock(&cgroup_mutex); cgroup_masks_read_one(seq, "subtree_control", cgrp->subtree_control); cgroup_masks_read_one(seq, "subtree_ss_mask", cgrp->subtree_ss_mask); - mutex_unlock(&cgroup_mutex); + + cgroup_kn_unlock(of->kn); return 0; } -- cgit