cgroup: Make debug controller useful for debugging
PATCH 1/1 cgroup: Make debug controller useful for debugging
Hello, Waiman.
I dropped the thread mode part of the patches so that these aren't blocked on thread mode, made the debug controller an implicit one on cgroup2 which is enabled by "cgroup_debug" boot param (we can't mask it on cgroup1 with the boot param at this point), and fixed the lockdep warnings.
I applied the updated patches + follow up changes to cgroup/for-4.13.
git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git for-4.13
I'd really appreciate if you can go over the patches and check that I didn't do anything stupid.
Thanks.
--
tejun
Tejun Heo
maintainerPATCH 1/7 cgroup: Keep accurate count of tasks in each css_set
From: Waiman Long <[email protected]>
The reference count in the css_set data structure was used as a proxy of the number of tasks attached to that css_set. However, that count is actually not an accurate measure especially with thread mode support. So a new variable nr_tasks is added to the css_set to keep track of the actual task count. This new variable is protected by the css_set_lock. Functions that require the actual task count are updated to use the new variable.
tj: s/task_count/nr_tasks/ for consistency with cgroup_root->nr_cgrps.
Refreshed on top of cgroup/for-v4.13 which dropped on css_set_populated() -> nr_tasks conversion.
--- include/linux/cgroup-defs.h | 3 +++ kernel/cgroup/cgroup-v1.c | 6 +----- kernel/cgroup/cgroup.c | 10 ++++++++++ 3 files changed, 14 insertions(+), 5 deletions(-)
include/linux/cgroup-defs.h +3 -0
@@ -166,6 +166,9 @@ struct css_set {/* the default cgroup associated with this css_set */ struct cgroup *dfl_cgrp; +/* internal task count, protected by css_set_lock */ +int nr_tasks; +/* * Lists running through all tasks using this cgroup group. * mg_tasks lists tasks which belong to this cset but are in the
kernel/cgroup/cgroup-v1.c +1 -5
@@ -334,10 +334,6 @@ static struct cgroup_pidlist *cgroup_pidlist_find_create(struct cgroup *cgrp,/** * cgroup_task_count - count the number of tasks in a cgroup. * @cgrp: the cgroup in question -* -* Return the number of tasks in the cgroup. The returned number can be -* higher than the actual number of tasks due to css_set references from -* namespace roots and temporary usages. */ static int cgroup_task_count (const struct cgroup *cgrp) {
@@ -346,7 +342,7 @@ static int cgroup_task_count(const struct cgroup *cgrp)spin_lock_irq (&css_set_lock);list_for_each_entry (link, &cgrp->cset_links, cset_link) - count +=refcount_read (&link->cset->refcount); + count += link->cset->nr_tasks;spin_unlock_irq (&css_set_lock);return count; }
kernel/cgroup/cgroup.c +10 -0
@@ -573,6 +573,11 @@ static int css_set_count = 1; /* 1 for init_css_set *//** * css_set_populated - does a css_set contain any tasks? * @cset: target css_set +* +* css_set_populated() should be the same as !!cset->nr_tasks at steady +* state. However, css_set_populated() can be called while a task is being +* added to or removed from the linked list before the nr_tasks is +* properly updated. Hence, we can't just look at ->nr_tasks here. */ static bool css_set_populated (struct css_set *cset) {
@@ -1598,6 +1603,7 @@ static void cgroup_enable_task_cg_lists(void)css_set_update_populated (cset, true);list_add_tail (&p->cg_list, &cset->tasks);get_css_set (cset); + cset->nr_tasks++; }spin_unlock (&p->sighand->siglock); }while_each_thread (g, p);
@@ -2064,8 +2070,10 @@ static int cgroup_migrate_execute(struct cgroup_mgctx *mgctx)struct css_set *to_cset = cset->mg_dst_cset;get_css_set (to_cset); + to_cset->nr_tasks++;css_set_move_task (task, from_cset, to_cset, true);put_css_set_locked (from_cset); + from_cset->nr_tasks--; } }spin_unlock_irq (&css_set_lock);
@@ -4789,6 +4797,7 @@ void cgroup_post_fork(struct task_struct *child) cset =task_css_set (current);if (list_empty (&child->cg_list)) {get_css_set (cset); + cset->nr_tasks++;css_set_move_task (child,NULL , cset, false); }spin_unlock_irq (&css_set_lock);
@@ -4838,6 +4847,7 @@ void cgroup_exit(struct task_struct *tsk)if (!list_empty (&tsk->cg_list)) {spin_lock_irq (&css_set_lock);css_set_move_task (tsk, cset,NULL , false); + cset->nr_tasks--;spin_unlock_irq (&css_set_lock); }else {get_css_set (cset);
Signature · 1 line
2.13.0
Tejun Heo
maintainerPATCH 2/7 cgroup: Move debug cgroup to its own file
In reply to Tejun HeoFrom: Waiman Long <[email protected]>
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.
--- kernel/cgroup/Makefile | 1 + kernel/cgroup/cgroup-internal.h | 2 + kernel/cgroup/cgroup-v1.c | 149 +------------------------------------- kernel/cgroup/debug.c | 153 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 148 deletions(-) create mode 100644 kernel/cgroup/debug.c
kernel/cgroup/Makefile +1 -0
@@ -4,3 +4,4 @@ obj-$(CONFIG_CGROUP_FREEZER) += freezer.o obj-$(CONFIG_CGROUP_PIDS) += pids.o obj-$(CONFIG_CGROUP_RDMA) += rdma.o obj-$(CONFIG_CPUSETS) += cpuset.o +obj-$(CONFIG_CGROUP_DEBUG) += debug.o
kernel/cgroup/cgroup-internal.h +2 -0
@@ -192,6 +192,8 @@ int cgroup_rmdir(struct kernfs_node *kn);int cgroup_show_path (struct seq_file *sf,struct kernfs_node *kf_node,struct kernfs_root *kf_root); +int cgroup_task_count (const struct cgroup *cgrp); +/* * namespace.c */
kernel/cgroup/cgroup-v1.c +1 -148
158 lines not on this page · raw message
kernel/cgroup/debug.c new file +153 -0
153 lines not on this page · raw message
Signature · 1 line
2.13.0
Tejun Heo
maintainerPATCH 3/7 cgroup: Make Kconfig prompt of debug cgroup more accurate
In reply to Tejun HeoFrom: Waiman Long <[email protected]>
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.
--- init/Kconfig | 7 +++++-- kernel/cgroup/debug.c | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-)
init/Kconfig +5 -2
@@ -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.
kernel/cgroup/debug.c +6 -0
@@ -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 <linux/ctype.h> #include <linux/mm.h> #include <linux/slab.h>
Signature · 1 line
2.13.0
Tejun Heo
maintainerPATCH 4/7 cgroup: Make debug cgroup support v2 and thread mode
In reply to Tejun HeoFrom: Waiman Long <[email protected]>
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.
--- kernel/cgroup/debug.c | 170 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 153 insertions(+), 17 deletions(-)
kernel/cgroup/debug.c +153 -17
@@ -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, }, {
@@ -145,6 +270,16 @@ static struct cftype debug_files[] = { }, { + .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, };
Signature · 1 line
2.13.0
Tejun Heo
maintainerPATCH 5/7 cgroup: make debug an implicit controller on cgroup2
In reply to Tejun HeoMake debug an implicit controller on cgroup2 which is enabled by "cgroup_debug" boot param.
--- kernel/cgroup/debug.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-)
kernel/cgroup/debug.c +56 -3
@@ -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);
Signature · 1 line
2.13.0
Tejun Heo
maintainerPATCH 6/7 cgroup: refactor cgroup_masks_read() in the debug controller
In reply to Tejun HeoFactor out cgroup_masks_read_one() out of cgroup_masks_read() for simplicity.
--- kernel/cgroup/debug.c | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-)
kernel/cgroup/debug.c +21 -25
@@ -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 ; }
Signature · 1 line
2.13.0
Tejun Heo
maintainerPATCH 7/7 cgroup: fix lockdep warning in debug controller
In reply to Tejun HeoThe 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.
--- kernel/cgroup/debug.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
kernel/cgroup/debug.c +21 -8
@@ -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 ; }
Signature · 1 line
2.13.0
Quoted 12 lines · Tejun Heo
Hello, Waiman.
I dropped the thread mode part of the patches so that these aren't blocked on thread mode, made the debug controller an implicit one on cgroup2 which is enabled by "
cgroup_debug" boot param (we can't mask it on cgroup1 with the boot param at this point), and fixed the lockdep warnings.I applied the updated patches + follow up changes to cgroup/for-4.13.
git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.gitfor-4.13
Quoted 7 lines
I'd really appreciate if you can go over the patches and check that I didn't do anything stupid.
Thanks.
--
tejun
Thanks for fixing issues in my debug controller patchset. I am fine with the changes.
-Longman