aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace/trace_stat.c')
-rw-r--r--kernel/trace/trace_stat.c191
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 */
19struct trace_stat_list { 19struct 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 */
25struct tracer_stat_session { 25struct 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 */
32static struct tracer_stat_session **all_stat_sessions; 34static LIST_HEAD(all_stat_sessions);
33static int nb_sessions; 35static DEFINE_MUTEX(all_stat_sessions_mutex);
34static struct dentry *stat_dir, **stat_files; 36
37/* The root directory for all stat files */
38static struct dentry *stat_dir;
35 39
36 40
37static void reset_stat_session(struct tracer_stat_session *session) 41static 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 */ 51static void destroy_session(struct tracer_stat_session *session)
48static 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) { 60static 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++) { 62int 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
82free_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
93static int basic_tracer_stat_checks(struct tracer_stat *ts) 106void 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 298static int tracing_stat_init(void)
284static 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
296static 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
321void 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
339static 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}
351fs_initcall(tracing_stat_init); 310
311static 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}