diff options
Diffstat (limited to 'kernel/trace/trace_stat.c')
-rw-r--r-- | kernel/trace/trace_stat.c | 230 |
1 files changed, 169 insertions, 61 deletions
diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c index f110ce9ce7fb..1515f9e7adfc 100644 --- a/kernel/trace/trace_stat.c +++ b/kernel/trace/trace_stat.c | |||
@@ -21,37 +21,87 @@ struct trace_stat_list { | |||
21 | void *stat; | 21 | void *stat; |
22 | }; | 22 | }; |
23 | 23 | ||
24 | static LIST_HEAD(stat_list); | 24 | /* A stat session is the stats output in one file */ |
25 | 25 | struct tracer_stat_session { | |
26 | /* | 26 | struct tracer_stat *ts; |
27 | * This is a copy of the current tracer to avoid racy | 27 | struct list_head stat_list; |
28 | * and dangerous output while the current tracer is | 28 | struct mutex stat_mutex; |
29 | * switched. | 29 | }; |
30 | */ | ||
31 | static struct tracer current_tracer; | ||
32 | 30 | ||
33 | /* | 31 | /* All of the sessions currently in use. Each stat file embeed one session */ |
34 | * Protect both the current tracer and the global | 32 | static struct tracer_stat_session **all_stat_sessions; |
35 | * stat list. | 33 | static int nb_sessions; |
36 | */ | 34 | static struct dentry *stat_dir, **stat_files; |
37 | static DEFINE_MUTEX(stat_list_mutex); | ||
38 | 35 | ||
39 | 36 | ||
40 | static void reset_stat_list(void) | 37 | static void reset_stat_session(struct tracer_stat_session *session) |
41 | { | 38 | { |
42 | struct trace_stat_list *node, *next; | 39 | struct trace_stat_list *node, *next; |
43 | 40 | ||
44 | list_for_each_entry_safe(node, next, &stat_list, list) | 41 | list_for_each_entry_safe(node, next, &session->stat_list, list) |
45 | kfree(node); | 42 | kfree(node); |
46 | 43 | ||
47 | INIT_LIST_HEAD(&stat_list); | 44 | INIT_LIST_HEAD(&session->stat_list); |
48 | } | 45 | } |
49 | 46 | ||
50 | void init_tracer_stat(struct tracer *trace) | 47 | /* Called when a tracer is initialized */ |
48 | static int init_all_sessions(int nb, struct tracer_stat *ts) | ||
51 | { | 49 | { |
52 | mutex_lock(&stat_list_mutex); | 50 | int i, j; |
53 | current_tracer = *trace; | 51 | struct tracer_stat_session *session; |
54 | mutex_unlock(&stat_list_mutex); | 52 | |
53 | nb_sessions = 0; | ||
54 | |||
55 | if (all_stat_sessions) { | ||
56 | for (i = 0; i < nb_sessions; i++) { | ||
57 | session = all_stat_sessions[i]; | ||
58 | reset_stat_session(session); | ||
59 | mutex_destroy(&session->stat_mutex); | ||
60 | kfree(session); | ||
61 | } | ||
62 | } | ||
63 | all_stat_sessions = kmalloc(sizeof(struct tracer_stat_session *) * nb, | ||
64 | GFP_KERNEL); | ||
65 | if (!all_stat_sessions) | ||
66 | return -ENOMEM; | ||
67 | |||
68 | for (i = 0; i < nb; i++) { | ||
69 | session = kmalloc(sizeof(struct tracer_stat_session) * nb, | ||
70 | GFP_KERNEL); | ||
71 | if (!session) | ||
72 | goto free_sessions; | ||
73 | |||
74 | INIT_LIST_HEAD(&session->stat_list); | ||
75 | mutex_init(&session->stat_mutex); | ||
76 | session->ts = &ts[i]; | ||
77 | all_stat_sessions[i] = session; | ||
78 | } | ||
79 | nb_sessions = nb; | ||
80 | return 0; | ||
81 | |||
82 | free_sessions: | ||
83 | |||
84 | for (j = 0; j < i; j++) | ||
85 | kfree(all_stat_sessions[i]); | ||
86 | |||
87 | kfree(all_stat_sessions); | ||
88 | all_stat_sessions = NULL; | ||
89 | |||
90 | return -ENOMEM; | ||
91 | } | ||
92 | |||
93 | static int basic_tracer_stat_checks(struct tracer_stat *ts) | ||
94 | { | ||
95 | int i; | ||
96 | |||
97 | if (!ts) | ||
98 | return 0; | ||
99 | |||
100 | for (i = 0; ts[i].name; i++) { | ||
101 | if (!ts[i].stat_start || !ts[i].stat_next || !ts[i].stat_show) | ||
102 | return -EBUSY; | ||
103 | } | ||
104 | return i; | ||
55 | } | 105 | } |
56 | 106 | ||
57 | /* | 107 | /* |
@@ -69,22 +119,19 @@ static int dummy_cmp(void *p1, void *p2) | |||
69 | * All of these copies and sorting are required on all opening | 119 | * All of these copies and sorting are required on all opening |
70 | * since the stats could have changed between two file sessions. | 120 | * since the stats could have changed between two file sessions. |
71 | */ | 121 | */ |
72 | static int stat_seq_init(void) | 122 | static int stat_seq_init(struct tracer_stat_session *session) |
73 | { | 123 | { |
74 | struct trace_stat_list *iter_entry, *new_entry; | 124 | struct trace_stat_list *iter_entry, *new_entry; |
125 | struct tracer_stat *ts = session->ts; | ||
75 | void *prev_stat; | 126 | void *prev_stat; |
76 | int ret = 0; | 127 | int ret = 0; |
77 | int i; | 128 | int i; |
78 | 129 | ||
79 | mutex_lock(&stat_list_mutex); | 130 | mutex_lock(&session->stat_mutex); |
80 | reset_stat_list(); | 131 | reset_stat_session(session); |
81 | |||
82 | if (!current_tracer.stat_start || !current_tracer.stat_next || | ||
83 | !current_tracer.stat_show) | ||
84 | goto exit; | ||
85 | 132 | ||
86 | if (!current_tracer.stat_cmp) | 133 | if (!ts->stat_cmp) |
87 | current_tracer.stat_cmp = dummy_cmp; | 134 | ts->stat_cmp = dummy_cmp; |
88 | 135 | ||
89 | /* | 136 | /* |
90 | * The first entry. Actually this is the second, but the first | 137 | * The first entry. Actually this is the second, but the first |
@@ -97,9 +144,10 @@ static int stat_seq_init(void) | |||
97 | } | 144 | } |
98 | 145 | ||
99 | INIT_LIST_HEAD(&new_entry->list); | 146 | INIT_LIST_HEAD(&new_entry->list); |
100 | list_add(&new_entry->list, &stat_list); | ||
101 | new_entry->stat = current_tracer.stat_start(); | ||
102 | 147 | ||
148 | list_add(&new_entry->list, &session->stat_list); | ||
149 | |||
150 | new_entry->stat = ts->stat_start(); | ||
103 | prev_stat = new_entry->stat; | 151 | prev_stat = new_entry->stat; |
104 | 152 | ||
105 | /* | 153 | /* |
@@ -114,15 +162,16 @@ static int stat_seq_init(void) | |||
114 | } | 162 | } |
115 | 163 | ||
116 | INIT_LIST_HEAD(&new_entry->list); | 164 | INIT_LIST_HEAD(&new_entry->list); |
117 | new_entry->stat = current_tracer.stat_next(prev_stat, i); | 165 | new_entry->stat = ts->stat_next(prev_stat, i); |
118 | 166 | ||
119 | /* End of insertion */ | 167 | /* End of insertion */ |
120 | if (!new_entry->stat) | 168 | if (!new_entry->stat) |
121 | break; | 169 | break; |
122 | 170 | ||
123 | list_for_each_entry(iter_entry, &stat_list, list) { | 171 | list_for_each_entry(iter_entry, &session->stat_list, list) { |
172 | |||
124 | /* Insertion with a descendent sorting */ | 173 | /* Insertion with a descendent sorting */ |
125 | if (current_tracer.stat_cmp(new_entry->stat, | 174 | if (ts->stat_cmp(new_entry->stat, |
126 | iter_entry->stat) > 0) { | 175 | iter_entry->stat) > 0) { |
127 | 176 | ||
128 | list_add_tail(&new_entry->list, | 177 | list_add_tail(&new_entry->list, |
@@ -131,7 +180,7 @@ static int stat_seq_init(void) | |||
131 | 180 | ||
132 | /* The current smaller value */ | 181 | /* The current smaller value */ |
133 | } else if (list_is_last(&iter_entry->list, | 182 | } else if (list_is_last(&iter_entry->list, |
134 | &stat_list)) { | 183 | &session->stat_list)) { |
135 | list_add(&new_entry->list, &iter_entry->list); | 184 | list_add(&new_entry->list, &iter_entry->list); |
136 | break; | 185 | break; |
137 | } | 186 | } |
@@ -140,49 +189,49 @@ static int stat_seq_init(void) | |||
140 | prev_stat = new_entry->stat; | 189 | prev_stat = new_entry->stat; |
141 | } | 190 | } |
142 | exit: | 191 | exit: |
143 | mutex_unlock(&stat_list_mutex); | 192 | mutex_unlock(&session->stat_mutex); |
144 | return ret; | 193 | return ret; |
145 | 194 | ||
146 | exit_free_list: | 195 | exit_free_list: |
147 | reset_stat_list(); | 196 | reset_stat_session(session); |
148 | mutex_unlock(&stat_list_mutex); | 197 | mutex_unlock(&session->stat_mutex); |
149 | return ret; | 198 | return ret; |
150 | } | 199 | } |
151 | 200 | ||
152 | 201 | ||
153 | static void *stat_seq_start(struct seq_file *s, loff_t *pos) | 202 | static void *stat_seq_start(struct seq_file *s, loff_t *pos) |
154 | { | 203 | { |
155 | struct list_head *l = (struct list_head *)s->private; | 204 | struct tracer_stat_session *session = s->private; |
156 | 205 | ||
157 | /* Prevent from tracer switch or stat_list modification */ | 206 | /* Prevent from tracer switch or stat_list modification */ |
158 | mutex_lock(&stat_list_mutex); | 207 | mutex_lock(&session->stat_mutex); |
159 | 208 | ||
160 | /* If we are in the beginning of the file, print the headers */ | 209 | /* If we are in the beginning of the file, print the headers */ |
161 | if (!*pos && current_tracer.stat_headers) | 210 | if (!*pos && session->ts->stat_headers) |
162 | current_tracer.stat_headers(s); | 211 | session->ts->stat_headers(s); |
163 | 212 | ||
164 | return seq_list_start(l, *pos); | 213 | return seq_list_start(&session->stat_list, *pos); |
165 | } | 214 | } |
166 | 215 | ||
167 | static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos) | 216 | static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos) |
168 | { | 217 | { |
169 | struct list_head *l = (struct list_head *)s->private; | 218 | struct tracer_stat_session *session = s->private; |
170 | 219 | ||
171 | return seq_list_next(p, l, pos); | 220 | return seq_list_next(p, &session->stat_list, pos); |
172 | } | 221 | } |
173 | 222 | ||
174 | static void stat_seq_stop(struct seq_file *m, void *p) | 223 | static void stat_seq_stop(struct seq_file *s, void *p) |
175 | { | 224 | { |
176 | mutex_unlock(&stat_list_mutex); | 225 | struct tracer_stat_session *session = s->private; |
226 | mutex_unlock(&session->stat_mutex); | ||
177 | } | 227 | } |
178 | 228 | ||
179 | static int stat_seq_show(struct seq_file *s, void *v) | 229 | static int stat_seq_show(struct seq_file *s, void *v) |
180 | { | 230 | { |
181 | struct trace_stat_list *entry; | 231 | struct tracer_stat_session *session = s->private; |
182 | 232 | struct trace_stat_list *l = list_entry(v, struct trace_stat_list, list); | |
183 | entry = list_entry(v, struct trace_stat_list, list); | ||
184 | 233 | ||
185 | return current_tracer.stat_show(s, entry->stat); | 234 | return session->ts->stat_show(s, l->stat); |
186 | } | 235 | } |
187 | 236 | ||
188 | static const struct seq_operations trace_stat_seq_ops = { | 237 | static const struct seq_operations trace_stat_seq_ops = { |
@@ -192,15 +241,18 @@ static const struct seq_operations trace_stat_seq_ops = { | |||
192 | .show = stat_seq_show | 241 | .show = stat_seq_show |
193 | }; | 242 | }; |
194 | 243 | ||
244 | /* The session stat is refilled and resorted at each stat file opening */ | ||
195 | static int tracing_stat_open(struct inode *inode, struct file *file) | 245 | static int tracing_stat_open(struct inode *inode, struct file *file) |
196 | { | 246 | { |
197 | int ret; | 247 | int ret; |
198 | 248 | ||
249 | struct tracer_stat_session *session = inode->i_private; | ||
250 | |||
199 | ret = seq_open(file, &trace_stat_seq_ops); | 251 | ret = seq_open(file, &trace_stat_seq_ops); |
200 | if (!ret) { | 252 | if (!ret) { |
201 | struct seq_file *m = file->private_data; | 253 | struct seq_file *m = file->private_data; |
202 | m->private = &stat_list; | 254 | m->private = session; |
203 | ret = stat_seq_init(); | 255 | ret = stat_seq_init(session); |
204 | } | 256 | } |
205 | 257 | ||
206 | return ret; | 258 | return ret; |
@@ -212,9 +264,12 @@ static int tracing_stat_open(struct inode *inode, struct file *file) | |||
212 | */ | 264 | */ |
213 | static int tracing_stat_release(struct inode *i, struct file *f) | 265 | static int tracing_stat_release(struct inode *i, struct file *f) |
214 | { | 266 | { |
215 | mutex_lock(&stat_list_mutex); | 267 | struct tracer_stat_session *session = i->i_private; |
216 | reset_stat_list(); | 268 | |
217 | mutex_unlock(&stat_list_mutex); | 269 | mutex_lock(&session->stat_mutex); |
270 | reset_stat_session(session); | ||
271 | mutex_unlock(&session->stat_mutex); | ||
272 | |||
218 | return 0; | 273 | return 0; |
219 | } | 274 | } |
220 | 275 | ||
@@ -225,17 +280,70 @@ static const struct file_operations tracing_stat_fops = { | |||
225 | .release = tracing_stat_release | 280 | .release = tracing_stat_release |
226 | }; | 281 | }; |
227 | 282 | ||
283 | |||
284 | static void destroy_trace_stat_files(void) | ||
285 | { | ||
286 | int i; | ||
287 | |||
288 | if (stat_files) { | ||
289 | for (i = 0; i < nb_sessions; i++) | ||
290 | debugfs_remove(stat_files[i]); | ||
291 | kfree(stat_files); | ||
292 | stat_files = NULL; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | static void init_trace_stat_files(void) | ||
297 | { | ||
298 | int i; | ||
299 | |||
300 | if (!stat_dir || !nb_sessions) | ||
301 | return; | ||
302 | |||
303 | stat_files = kmalloc(sizeof(struct dentry *) * nb_sessions, GFP_KERNEL); | ||
304 | |||
305 | if (!stat_files) { | ||
306 | pr_warning("trace stat: not enough memory\n"); | ||
307 | return; | ||
308 | } | ||
309 | |||
310 | for (i = 0; i < nb_sessions; i++) { | ||
311 | struct tracer_stat_session *session = all_stat_sessions[i]; | ||
312 | stat_files[i] = debugfs_create_file(session->ts->name, 0644, | ||
313 | stat_dir, | ||
314 | session, &tracing_stat_fops); | ||
315 | if (!stat_files[i]) | ||
316 | pr_warning("cannot create %s entry\n", | ||
317 | session->ts->name); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | void init_tracer_stat(struct tracer *trace) | ||
322 | { | ||
323 | int nb = basic_tracer_stat_checks(trace->stats); | ||
324 | |||
325 | destroy_trace_stat_files(); | ||
326 | |||
327 | if (nb < 0) { | ||
328 | pr_warning("stat tracing: missing stat callback on %s\n", | ||
329 | trace->name); | ||
330 | return; | ||
331 | } | ||
332 | if (!nb) | ||
333 | return; | ||
334 | |||
335 | init_all_sessions(nb, trace->stats); | ||
336 | init_trace_stat_files(); | ||
337 | } | ||
338 | |||
228 | static int __init tracing_stat_init(void) | 339 | static int __init tracing_stat_init(void) |
229 | { | 340 | { |
230 | struct dentry *d_tracing; | 341 | struct dentry *d_tracing; |
231 | struct dentry *entry; | ||
232 | 342 | ||
233 | d_tracing = tracing_init_dentry(); | 343 | d_tracing = tracing_init_dentry(); |
234 | 344 | ||
235 | entry = debugfs_create_file("trace_stat", 0444, d_tracing, | 345 | stat_dir = debugfs_create_dir("trace_stat", d_tracing); |
236 | NULL, | 346 | if (!stat_dir) |
237 | &tracing_stat_fops); | ||
238 | if (!entry) | ||
239 | pr_warning("Could not create debugfs " | 347 | pr_warning("Could not create debugfs " |
240 | "'trace_stat' entry\n"); | 348 | "'trace_stat' entry\n"); |
241 | return 0; | 349 | return 0; |