aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt (VMware) <rostedt@goodmis.org>2018-07-09 17:48:54 -0400
committerTejun Heo <tj@kernel.org>2018-07-11 13:48:47 -0400
commite4f8d81c738db6d3ffdabfb8329aa2feaa310699 (patch)
tree97153959e7c625bc49850709149a1faa5146a70e
parent1e09177acae32a61586af26d83ca5ef591cdcaf5 (diff)
cgroup/tracing: Move taking of spin lock out of trace event handlers
It is unwise to take spin locks from the handlers of trace events. Mainly, because they can introduce lockups, because it introduces locks in places that are normally not tested. Worse yet, because trace events are tucked away in the include/trace/events/ directory, locks that are taken there are forgotten about. As a general rule, I tell people never to take any locks in a trace event handler. Several cgroup trace event handlers call cgroup_path() which eventually takes the kernfs_rename_lock spinlock. This injects the spinlock in the code without people realizing it. It also can cause issues for the PREEMPT_RT patch, as the spinlock becomes a mutex, and the trace event handlers are called with preemption disabled. By moving the calculation of the cgroup_path() out of the trace event handlers and into a macro (surrounded by a trace_cgroup_##type##_enabled()), then we could place the cgroup_path into a string, and pass that to the trace event. Not only does this remove the taking of the spinlock out of the trace event handler, but it also means that the cgroup_path() only needs to be called once (it is currently called twice, once to get the length to reserver the buffer for, and once again to get the path itself. Now it only needs to be done once. Reported-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org> Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r--include/trace/events/cgroup.h47
-rw-r--r--kernel/cgroup/cgroup-internal.h26
-rw-r--r--kernel/cgroup/cgroup-v1.c4
-rw-r--r--kernel/cgroup/cgroup.c12
4 files changed, 58 insertions, 31 deletions
diff --git a/include/trace/events/cgroup.h b/include/trace/events/cgroup.h
index d74722c2ac8b..a401ff5e7847 100644
--- a/include/trace/events/cgroup.h
+++ b/include/trace/events/cgroup.h
@@ -53,24 +53,22 @@ DEFINE_EVENT(cgroup_root, cgroup_remount,
53 53
54DECLARE_EVENT_CLASS(cgroup, 54DECLARE_EVENT_CLASS(cgroup,
55 55
56 TP_PROTO(struct cgroup *cgrp), 56 TP_PROTO(struct cgroup *cgrp, const char *path),
57 57
58 TP_ARGS(cgrp), 58 TP_ARGS(cgrp, path),
59 59
60 TP_STRUCT__entry( 60 TP_STRUCT__entry(
61 __field( int, root ) 61 __field( int, root )
62 __field( int, id ) 62 __field( int, id )
63 __field( int, level ) 63 __field( int, level )
64 __dynamic_array(char, path, 64 __string( path, path )
65 cgroup_path(cgrp, NULL, 0) + 1)
66 ), 65 ),
67 66
68 TP_fast_assign( 67 TP_fast_assign(
69 __entry->root = cgrp->root->hierarchy_id; 68 __entry->root = cgrp->root->hierarchy_id;
70 __entry->id = cgrp->id; 69 __entry->id = cgrp->id;
71 __entry->level = cgrp->level; 70 __entry->level = cgrp->level;
72 cgroup_path(cgrp, __get_dynamic_array(path), 71 __assign_str(path, path);
73 __get_dynamic_array_len(path));
74 ), 72 ),
75 73
76 TP_printk("root=%d id=%d level=%d path=%s", 74 TP_printk("root=%d id=%d level=%d path=%s",
@@ -79,45 +77,45 @@ DECLARE_EVENT_CLASS(cgroup,
79 77
80DEFINE_EVENT(cgroup, cgroup_mkdir, 78DEFINE_EVENT(cgroup, cgroup_mkdir,
81 79
82 TP_PROTO(struct cgroup *cgroup), 80 TP_PROTO(struct cgroup *cgrp, const char *path),
83 81
84 TP_ARGS(cgroup) 82 TP_ARGS(cgrp, path)
85); 83);
86 84
87DEFINE_EVENT(cgroup, cgroup_rmdir, 85DEFINE_EVENT(cgroup, cgroup_rmdir,
88 86
89 TP_PROTO(struct cgroup *cgroup), 87 TP_PROTO(struct cgroup *cgrp, const char *path),
90 88
91 TP_ARGS(cgroup) 89 TP_ARGS(cgrp, path)
92); 90);
93 91
94DEFINE_EVENT(cgroup, cgroup_release, 92DEFINE_EVENT(cgroup, cgroup_release,
95 93
96 TP_PROTO(struct cgroup *cgroup), 94 TP_PROTO(struct cgroup *cgrp, const char *path),
97 95
98 TP_ARGS(cgroup) 96 TP_ARGS(cgrp, path)
99); 97);
100 98
101DEFINE_EVENT(cgroup, cgroup_rename, 99DEFINE_EVENT(cgroup, cgroup_rename,
102 100
103 TP_PROTO(struct cgroup *cgroup), 101 TP_PROTO(struct cgroup *cgrp, const char *path),
104 102
105 TP_ARGS(cgroup) 103 TP_ARGS(cgrp, path)
106); 104);
107 105
108DECLARE_EVENT_CLASS(cgroup_migrate, 106DECLARE_EVENT_CLASS(cgroup_migrate,
109 107
110 TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup), 108 TP_PROTO(struct cgroup *dst_cgrp, const char *path,
109 struct task_struct *task, bool threadgroup),
111 110
112 TP_ARGS(dst_cgrp, task, threadgroup), 111 TP_ARGS(dst_cgrp, path, task, threadgroup),
113 112
114 TP_STRUCT__entry( 113 TP_STRUCT__entry(
115 __field( int, dst_root ) 114 __field( int, dst_root )
116 __field( int, dst_id ) 115 __field( int, dst_id )
117 __field( int, dst_level ) 116 __field( int, dst_level )
118 __dynamic_array(char, dst_path,
119 cgroup_path(dst_cgrp, NULL, 0) + 1)
120 __field( int, pid ) 117 __field( int, pid )
118 __string( dst_path, path )
121 __string( comm, task->comm ) 119 __string( comm, task->comm )
122 ), 120 ),
123 121
@@ -125,8 +123,7 @@ DECLARE_EVENT_CLASS(cgroup_migrate,
125 __entry->dst_root = dst_cgrp->root->hierarchy_id; 123 __entry->dst_root = dst_cgrp->root->hierarchy_id;
126 __entry->dst_id = dst_cgrp->id; 124 __entry->dst_id = dst_cgrp->id;
127 __entry->dst_level = dst_cgrp->level; 125 __entry->dst_level = dst_cgrp->level;
128 cgroup_path(dst_cgrp, __get_dynamic_array(dst_path), 126 __assign_str(dst_path, path);
129 __get_dynamic_array_len(dst_path));
130 __entry->pid = task->pid; 127 __entry->pid = task->pid;
131 __assign_str(comm, task->comm); 128 __assign_str(comm, task->comm);
132 ), 129 ),
@@ -138,16 +135,18 @@ DECLARE_EVENT_CLASS(cgroup_migrate,
138 135
139DEFINE_EVENT(cgroup_migrate, cgroup_attach_task, 136DEFINE_EVENT(cgroup_migrate, cgroup_attach_task,
140 137
141 TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup), 138 TP_PROTO(struct cgroup *dst_cgrp, const char *path,
139 struct task_struct *task, bool threadgroup),
142 140
143 TP_ARGS(dst_cgrp, task, threadgroup) 141 TP_ARGS(dst_cgrp, path, task, threadgroup)
144); 142);
145 143
146DEFINE_EVENT(cgroup_migrate, cgroup_transfer_tasks, 144DEFINE_EVENT(cgroup_migrate, cgroup_transfer_tasks,
147 145
148 TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup), 146 TP_PROTO(struct cgroup *dst_cgrp, const char *path,
147 struct task_struct *task, bool threadgroup),
149 148
150 TP_ARGS(dst_cgrp, task, threadgroup) 149 TP_ARGS(dst_cgrp, path, task, threadgroup)
151); 150);
152 151
153#endif /* _TRACE_CGROUP_H */ 152#endif /* _TRACE_CGROUP_H */
diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h
index 77ff1cd6a252..75568fcf2180 100644
--- a/kernel/cgroup/cgroup-internal.h
+++ b/kernel/cgroup/cgroup-internal.h
@@ -8,6 +8,32 @@
8#include <linux/list.h> 8#include <linux/list.h>
9#include <linux/refcount.h> 9#include <linux/refcount.h>
10 10
11#define TRACE_CGROUP_PATH_LEN 1024
12extern spinlock_t trace_cgroup_path_lock;
13extern char trace_cgroup_path[TRACE_CGROUP_PATH_LEN];
14
15/*
16 * cgroup_path() takes a spin lock. It is good practice not to take
17 * spin locks within trace point handlers, as they are mostly hidden
18 * from normal view. As cgroup_path() can take the kernfs_rename_lock
19 * spin lock, it is best to not call that function from the trace event
20 * handler.
21 *
22 * Note: trace_cgroup_##type##_enabled() is a static branch that will only
23 * be set when the trace event is enabled.
24 */
25#define TRACE_CGROUP_PATH(type, cgrp, ...) \
26 do { \
27 if (trace_cgroup_##type##_enabled()) { \
28 spin_lock(&trace_cgroup_path_lock); \
29 cgroup_path(cgrp, trace_cgroup_path, \
30 TRACE_CGROUP_PATH_LEN); \
31 trace_cgroup_##type(cgrp, trace_cgroup_path, \
32 ##__VA_ARGS__); \
33 spin_unlock(&trace_cgroup_path_lock); \
34 } \
35 } while (0)
36
11/* 37/*
12 * A cgroup can be associated with multiple css_sets as different tasks may 38 * A cgroup can be associated with multiple css_sets as different tasks may
13 * belong to different cgroups on different hierarchies. In the other 39 * belong to different cgroups on different hierarchies. In the other
diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
index 8b4f0768efd6..51063e7a93c2 100644
--- a/kernel/cgroup/cgroup-v1.c
+++ b/kernel/cgroup/cgroup-v1.c
@@ -135,7 +135,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
135 if (task) { 135 if (task) {
136 ret = cgroup_migrate(task, false, &mgctx); 136 ret = cgroup_migrate(task, false, &mgctx);
137 if (!ret) 137 if (!ret)
138 trace_cgroup_transfer_tasks