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.c230
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
24static LIST_HEAD(stat_list); 24/* A stat session is the stats output in one file */
25 25struct 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 */
31static 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 32static struct tracer_stat_session **all_stat_sessions;
35 * stat list. 33static int nb_sessions;
36 */ 34static struct dentry *stat_dir, **stat_files;
37static DEFINE_MUTEX(stat_list_mutex);
38 35
39 36
40static void reset_stat_list(void) 37static 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
50void init_tracer_stat(struct tracer *trace) 47/* Called when a tracer is initialized */
48static 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
82free_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
93static 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 */
72static int stat_seq_init(void) 122static 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 }
142exit: 191exit:
143 mutex_unlock(&stat_list_mutex); 192 mutex_unlock(&session->stat_mutex);
144 return ret; 193 return ret;
145 194
146exit_free_list: 195exit_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
153static void *stat_seq_start(struct seq_file *s, loff_t *pos) 202static 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
167static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos) 216static 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
174static void stat_seq_stop(struct seq_file *m, void *p) 223static 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
179static int stat_seq_show(struct seq_file *s, void *v) 229static 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
188static const struct seq_operations trace_stat_seq_ops = { 237static 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 */
195static int tracing_stat_open(struct inode *inode, struct file *file) 245static 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 */
213static int tracing_stat_release(struct inode *i, struct file *f) 265static 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
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
228static int __init tracing_stat_init(void) 339static 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;