diff options
Diffstat (limited to 'kernel/trace/trace_stat.c')
-rw-r--r-- | kernel/trace/trace_stat.c | 191 |
1 files changed, 81 insertions, 110 deletions
diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c index 1515f9e7adfc..cb29282b9485 100644 --- a/kernel/trace/trace_stat.c +++ b/kernel/trace/trace_stat.c | |||
@@ -10,28 +10,32 @@ | |||
10 | 10 | ||
11 | 11 | ||
12 | #include <linux/list.h> | 12 | #include <linux/list.h> |
13 | #include <linux/seq_file.h> | ||
14 | #include <linux/debugfs.h> | 13 | #include <linux/debugfs.h> |
14 | #include "trace_stat.h" | ||
15 | #include "trace.h" | 15 | #include "trace.h" |
16 | 16 | ||
17 | 17 | ||
18 | /* List of stat entries from a tracer */ | 18 | /* List of stat entries from a tracer */ |
19 | struct trace_stat_list { | 19 | struct trace_stat_list { |
20 | struct list_head list; | 20 | struct list_head list; |
21 | void *stat; | 21 | void *stat; |
22 | }; | 22 | }; |
23 | 23 | ||
24 | /* A stat session is the stats output in one file */ | 24 | /* A stat session is the stats output in one file */ |
25 | struct tracer_stat_session { | 25 | struct tracer_stat_session { |
26 | struct tracer_stat *ts; | 26 | struct list_head session_list; |
27 | struct list_head stat_list; | 27 | struct tracer_stat *ts; |
28 | struct mutex stat_mutex; | 28 | struct list_head stat_list; |
29 | struct mutex stat_mutex; | ||
30 | struct dentry *file; | ||
29 | }; | 31 | }; |
30 | 32 | ||
31 | /* All of the sessions currently in use. Each stat file embeed one session */ | 33 | /* All of the sessions currently in use. Each stat file embeed one session */ |
32 | static struct tracer_stat_session **all_stat_sessions; | 34 | static LIST_HEAD(all_stat_sessions); |
33 | static int nb_sessions; | 35 | static DEFINE_MUTEX(all_stat_sessions_mutex); |
34 | static struct dentry *stat_dir, **stat_files; | 36 | |
37 | /* The root directory for all stat files */ | ||
38 | static struct dentry *stat_dir; | ||
35 | 39 | ||
36 | 40 | ||
37 | static void reset_stat_session(struct tracer_stat_session *session) | 41 | static void reset_stat_session(struct tracer_stat_session *session) |
@@ -44,66 +48,77 @@ static void reset_stat_session(struct tracer_stat_session *session) | |||
44 | INIT_LIST_HEAD(&session->stat_list); | 48 | INIT_LIST_HEAD(&session->stat_list); |
45 | } | 49 | } |
46 | 50 | ||
47 | /* Called when a tracer is initialized */ | 51 | static void destroy_session(struct tracer_stat_session *session) |
48 | static int init_all_sessions(int nb, struct tracer_stat *ts) | ||
49 | { | 52 | { |
50 | int i, j; | 53 | debugfs_remove(session->file); |
51 | struct tracer_stat_session *session; | 54 | reset_stat_session(session); |
55 | mutex_destroy(&session->stat_mutex); | ||
56 | kfree(session); | ||
57 | } | ||
52 | 58 | ||
53 | nb_sessions = 0; | ||
54 | 59 | ||
55 | if (all_stat_sessions) { | 60 | static int init_stat_file(struct tracer_stat_session *session); |
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 | 61 | ||
68 | for (i = 0; i < nb; i++) { | 62 | int register_stat_tracer(struct tracer_stat *trace) |
69 | session = kmalloc(sizeof(struct tracer_stat_session) * nb, | 63 | { |
70 | GFP_KERNEL); | 64 | struct tracer_stat_session *session, *node, *tmp; |
71 | if (!session) | 65 | int ret; |
72 | goto free_sessions; | 66 | |
67 | if (!trace) | ||
68 | return -EINVAL; | ||
69 | |||
70 | if (!trace->stat_start || !trace->stat_next || !trace->stat_show) | ||
71 | return -EINVAL; | ||
73 | 72 | ||
74 | INIT_LIST_HEAD(&session->stat_list); | 73 | /* Already registered? */ |
75 | mutex_init(&session->stat_mutex); | 74 | mutex_lock(&all_stat_sessions_mutex); |
76 | session->ts = &ts[i]; | 75 | list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) { |
77 | all_stat_sessions[i] = session; | 76 | if (node->ts == trace) |
77 | return -EINVAL; | ||
78 | } | 78 | } |
79 | nb_sessions = nb; | 79 | mutex_unlock(&all_stat_sessions_mutex); |
80 | return 0; | 80 | |
81 | /* Init the session */ | ||
82 | session = kmalloc(sizeof(struct tracer_stat_session), GFP_KERNEL); | ||
83 | if (!session) | ||
84 | return -ENOMEM; | ||
81 | 85 | ||
82 | free_sessions: | 86 | session->ts = trace; |
87 | INIT_LIST_HEAD(&session->session_list); | ||
88 | INIT_LIST_HEAD(&session->stat_list); | ||
89 | mutex_init(&session->stat_mutex); | ||
90 | session->file = NULL; | ||
83 | 91 | ||
84 | for (j = 0; j < i; j++) | 92 | ret = init_stat_file(session); |
85 | kfree(all_stat_sessions[i]); | 93 | if (ret) { |
94 | destroy_session(session); | ||
95 | return ret; | ||
96 | } | ||
86 | 97 | ||
87 | kfree(all_stat_sessions); | 98 | /* Register */ |
88 | all_stat_sessions = NULL; | 99 | mutex_lock(&all_stat_sessions_mutex); |
100 | list_add_tail(&session->session_list, &all_stat_sessions); | ||
101 | mutex_unlock(&all_stat_sessions_mutex); | ||
89 | 102 | ||
90 | return -ENOMEM; | 103 | return 0; |
91 | } | 104 | } |
92 | 105 | ||
93 | static int basic_tracer_stat_checks(struct tracer_stat *ts) | 106 | void unregister_stat_tracer(struct tracer_stat *trace) |
94 | { | 107 | { |
95 | int i; | 108 | struct tracer_stat_session *node, *tmp; |
96 | 109 | ||
97 | if (!ts) | 110 | mutex_lock(&all_stat_sessions_mutex); |
98 | return 0; | 111 | list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) { |
99 | 112 | if (node->ts == trace) { | |
100 | for (i = 0; ts[i].name; i++) { | 113 | list_del(&node->session_list); |
101 | if (!ts[i].stat_start || !ts[i].stat_next || !ts[i].stat_show) | 114 | destroy_session(node); |
102 | return -EBUSY; | 115 | break; |
116 | } | ||
103 | } | 117 | } |
104 | return i; | 118 | mutex_unlock(&all_stat_sessions_mutex); |
105 | } | 119 | } |
106 | 120 | ||
121 | |||
107 | /* | 122 | /* |
108 | * For tracers that don't provide a stat_cmp callback. | 123 | * For tracers that don't provide a stat_cmp callback. |
109 | * This one will force an immediate insertion on tail of | 124 | * This one will force an immediate insertion on tail of |
@@ -280,63 +295,7 @@ static const struct file_operations tracing_stat_fops = { | |||
280 | .release = tracing_stat_release | 295 | .release = tracing_stat_release |
281 | }; | 296 | }; |
282 | 297 | ||
283 | 298 | static int tracing_stat_init(void) | |
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 | |||
339 | static int __init tracing_stat_init(void) | ||
340 | { | 299 | { |
341 | struct dentry *d_tracing; | 300 | struct dentry *d_tracing; |
342 | 301 | ||
@@ -348,4 +307,16 @@ static int __init tracing_stat_init(void) | |||
348 | "'trace_stat' entry\n"); | 307 | "'trace_stat' entry\n"); |
349 | return 0; | 308 | return 0; |
350 | } | 309 | } |
351 | fs_initcall(tracing_stat_init); | 310 | |
311 | static int init_stat_file(struct tracer_stat_session *session) | ||
312 | { | ||
313 | if (!stat_dir && tracing_stat_init()) | ||
314 | return -ENODEV; | ||
315 | |||
316 | session->file = debugfs_create_file(session->ts->name, 0644, | ||
317 | stat_dir, | ||
318 | session, &tracing_stat_fops); | ||
319 | if (!session->file) | ||
320 | return -ENOMEM; | ||
321 | return 0; | ||
322 | } | ||