blob: 9ab0a1a7ad5ee042dff2e25b3485204cea2e844d [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -08002/*
3 * Infrastructure for statistic tracing (histogram output).
4 *
Frederic Weisbecker8f184f22009-05-16 06:24:36 +02005 * Copyright (C) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com>
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -08006 *
7 * Based on the code from trace_branch.c which is
8 * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
9 *
10 */
11
Steven Rostedt (VMware)17911ff2019-10-11 17:22:50 -040012#include <linux/security.h>
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080013#include <linux/list.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090014#include <linux/slab.h>
Frederic Weisbecker8f184f22009-05-16 06:24:36 +020015#include <linux/rbtree.h>
Steven Rostedt (Red Hat)8434dc92015-01-20 12:13:40 -050016#include <linux/tracefs.h>
Frederic Weisbecker002bb862009-01-10 11:34:13 -080017#include "trace_stat.h"
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080018#include "trace.h"
19
20
Frederic Weisbecker8f184f22009-05-16 06:24:36 +020021/*
22 * List of stat red-black nodes from a tracer
23 * We use a such tree to sort quickly the stat
24 * entries from the tracer.
25 */
26struct stat_node {
27 struct rb_node node;
Ingo Molnar559221732009-01-15 11:31:21 +010028 void *stat;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080029};
30
Frederic Weisbecker034939b2009-01-08 10:03:56 -080031/* A stat session is the stats output in one file */
Frederic Weisbecker0d64f832009-05-16 05:58:49 +020032struct stat_session {
Frederic Weisbecker002bb862009-01-10 11:34:13 -080033 struct list_head session_list;
Ingo Molnar559221732009-01-15 11:31:21 +010034 struct tracer_stat *ts;
Frederic Weisbecker8f184f22009-05-16 06:24:36 +020035 struct rb_root stat_root;
Ingo Molnar559221732009-01-15 11:31:21 +010036 struct mutex stat_mutex;
Frederic Weisbecker002bb862009-01-10 11:34:13 -080037 struct dentry *file;
Frederic Weisbecker034939b2009-01-08 10:03:56 -080038};
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080039
Wenji Huang73d8b8b2009-02-17 01:10:02 -050040/* All of the sessions currently in use. Each stat file embed one session */
Frederic Weisbecker002bb862009-01-10 11:34:13 -080041static LIST_HEAD(all_stat_sessions);
42static DEFINE_MUTEX(all_stat_sessions_mutex);
43
44/* The root directory for all stat files */
Ingo Molnar559221732009-01-15 11:31:21 +010045static struct dentry *stat_dir;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080046
Li Zefan636eace2009-07-23 11:29:47 +080047static void __reset_stat_session(struct stat_session *session)
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080048{
Cody P Schafer9cd804a2013-11-01 15:38:46 -070049 struct stat_node *snode, *n;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080050
Cody P Schafer9cd804a2013-11-01 15:38:46 -070051 rbtree_postorder_for_each_entry_safe(snode, n, &session->stat_root, node) {
52 if (session->ts->stat_release)
53 session->ts->stat_release(snode->stat);
54 kfree(snode);
55 }
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080056
Frederic Weisbecker8f184f22009-05-16 06:24:36 +020057 session->stat_root = RB_ROOT;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080058}
59
Li Zefan636eace2009-07-23 11:29:47 +080060static void reset_stat_session(struct stat_session *session)
61{
62 mutex_lock(&session->stat_mutex);
63 __reset_stat_session(session);
64 mutex_unlock(&session->stat_mutex);
65}
66
Frederic Weisbecker0d64f832009-05-16 05:58:49 +020067static void destroy_session(struct stat_session *session)
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -080068{
Steven Rostedt (Red Hat)8434dc92015-01-20 12:13:40 -050069 tracefs_remove(session->file);
Li Zefan636eace2009-07-23 11:29:47 +080070 __reset_stat_session(session);
Frederic Weisbecker002bb862009-01-10 11:34:13 -080071 mutex_destroy(&session->stat_mutex);
72 kfree(session);
73}
Frederic Weisbecker034939b2009-01-08 10:03:56 -080074
Frederic Weisbecker8f184f22009-05-16 06:24:36 +020075typedef int (*cmp_stat_t)(void *, void *);
76
Li Zefandbd3fbd2009-05-27 11:42:46 +080077static int insert_stat(struct rb_root *root, void *stat, cmp_stat_t cmp)
Frederic Weisbecker8f184f22009-05-16 06:24:36 +020078{
79 struct rb_node **new = &(root->rb_node), *parent = NULL;
Li Zefandbd3fbd2009-05-27 11:42:46 +080080 struct stat_node *data;
81
82 data = kzalloc(sizeof(*data), GFP_KERNEL);
83 if (!data)
84 return -ENOMEM;
85 data->stat = stat;
Frederic Weisbecker8f184f22009-05-16 06:24:36 +020086
87 /*
88 * Figure out where to put new node
89 * This is a descendent sorting
90 */
91 while (*new) {
92 struct stat_node *this;
93 int result;
94
95 this = container_of(*new, struct stat_node, node);
96 result = cmp(data->stat, this->stat);
97
98 parent = *new;
99 if (result >= 0)
100 new = &((*new)->rb_left);
101 else
102 new = &((*new)->rb_right);
103 }
104
105 rb_link_node(&data->node, parent, new);
106 rb_insert_color(&data->node, root);
Li Zefandbd3fbd2009-05-27 11:42:46 +0800107 return 0;
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200108}
109
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800110/*
111 * For tracers that don't provide a stat_cmp callback.
Li Zefandbd3fbd2009-05-27 11:42:46 +0800112 * This one will force an insertion as right-most node
113 * in the rbtree.
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800114 */
115static int dummy_cmp(void *p1, void *p2)
116{
Li Zefanb3dd7ba2009-05-27 11:04:26 +0800117 return -1;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800118}
119
120/*
Li Zefandbd3fbd2009-05-27 11:42:46 +0800121 * Initialize the stat rbtree at each trace_stat file opening.
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800122 * All of these copies and sorting are required on all opening
123 * since the stats could have changed between two file sessions.
124 */
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200125static int stat_seq_init(struct stat_session *session)
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800126{
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800127 struct tracer_stat *ts = session->ts;
Li Zefandbd3fbd2009-05-27 11:42:46 +0800128 struct rb_root *root = &session->stat_root;
Steven Rostedt09833522009-03-21 02:44:50 -0400129 void *stat;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800130 int ret = 0;
131 int i;
132
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800133 mutex_lock(&session->stat_mutex);
Li Zefan636eace2009-07-23 11:29:47 +0800134 __reset_stat_session(session);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800135
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800136 if (!ts->stat_cmp)
137 ts->stat_cmp = dummy_cmp;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800138
Steven Rostedt42548002009-03-24 13:38:36 -0400139 stat = ts->stat_start(ts);
Steven Rostedt09833522009-03-21 02:44:50 -0400140 if (!stat)
141 goto exit;
142
Li Zefandbd3fbd2009-05-27 11:42:46 +0800143 ret = insert_stat(root, stat, ts->stat_cmp);
144 if (ret)
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800145 goto exit;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800146
147 /*
Li Zefandbd3fbd2009-05-27 11:42:46 +0800148 * Iterate over the tracer stat entries and store them in an rbtree.
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800149 */
150 for (i = 1; ; i++) {
Steven Rostedt09833522009-03-21 02:44:50 -0400151 stat = ts->stat_next(stat, i);
152
153 /* End of insertion */
154 if (!stat)
155 break;
156
Li Zefandbd3fbd2009-05-27 11:42:46 +0800157 ret = insert_stat(root, stat, ts->stat_cmp);
158 if (ret)
159 goto exit_free_rbtree;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800160 }
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200161
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800162exit:
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800163 mutex_unlock(&session->stat_mutex);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800164 return ret;
165
Li Zefandbd3fbd2009-05-27 11:42:46 +0800166exit_free_rbtree:
Li Zefan636eace2009-07-23 11:29:47 +0800167 __reset_stat_session(session);
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800168 mutex_unlock(&session->stat_mutex);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800169 return ret;
170}
171
172
173static void *stat_seq_start(struct seq_file *s, loff_t *pos)
174{
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200175 struct stat_session *session = s->private;
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200176 struct rb_node *node;
Li Zefan97d53202009-08-17 16:52:53 +0800177 int n = *pos;
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200178 int i;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800179
Li Zefandbd3fbd2009-05-27 11:42:46 +0800180 /* Prevent from tracer switch or rbtree modification */
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800181 mutex_lock(&session->stat_mutex);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800182
183 /* If we are in the beginning of the file, print the headers */
Li Zefan97d53202009-08-17 16:52:53 +0800184 if (session->ts->stat_headers) {
185 if (n == 0)
186 return SEQ_START_TOKEN;
187 n--;
188 }
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800189
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200190 node = rb_first(&session->stat_root);
Li Zefan97d53202009-08-17 16:52:53 +0800191 for (i = 0; node && i < n; i++)
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200192 node = rb_next(node);
193
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200194 return node;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800195}
196
197static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
198{
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200199 struct stat_session *session = s->private;
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200200 struct rb_node *node = p;
201
202 (*pos)++;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800203
Lai Jiangshane6f48902009-03-25 16:27:17 +0800204 if (p == SEQ_START_TOKEN)
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200205 return rb_first(&session->stat_root);
Lai Jiangshane6f48902009-03-25 16:27:17 +0800206
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200207 return rb_next(node);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800208}
209
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800210static void stat_seq_stop(struct seq_file *s, void *p)
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800211{
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200212 struct stat_session *session = s->private;
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800213 mutex_unlock(&session->stat_mutex);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800214}
215
216static int stat_seq_show(struct seq_file *s, void *v)
217{
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200218 struct stat_session *session = s->private;
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200219 struct stat_node *l = container_of(v, struct stat_node, node);
Steven Rostedte8a9cbf2009-01-06 22:02:35 -0500220
Lai Jiangshane6f48902009-03-25 16:27:17 +0800221 if (v == SEQ_START_TOKEN)
222 return session->ts->stat_headers(s);
223
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800224 return session->ts->stat_show(s, l->stat);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800225}
226
227static const struct seq_operations trace_stat_seq_ops = {
Ingo Molnar559221732009-01-15 11:31:21 +0100228 .start = stat_seq_start,
229 .next = stat_seq_next,
230 .stop = stat_seq_stop,
231 .show = stat_seq_show
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800232};
233
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800234/* The session stat is refilled and resorted at each stat file opening */
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800235static int tracing_stat_open(struct inode *inode, struct file *file)
236{
237 int ret;
Li Zefan636eace2009-07-23 11:29:47 +0800238 struct seq_file *m;
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200239 struct stat_session *session = inode->i_private;
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800240
Steven Rostedt (VMware)17911ff2019-10-11 17:22:50 -0400241 ret = security_locked_down(LOCKDOWN_TRACEFS);
242 if (ret)
243 return ret;
244
Li Zefan636eace2009-07-23 11:29:47 +0800245 ret = stat_seq_init(session);
246 if (ret)
247 return ret;
248
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800249 ret = seq_open(file, &trace_stat_seq_ops);
Li Zefan636eace2009-07-23 11:29:47 +0800250 if (ret) {
251 reset_stat_session(session);
252 return ret;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800253 }
254
Li Zefan636eace2009-07-23 11:29:47 +0800255 m = file->private_data;
256 m->private = session;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800257 return ret;
258}
259
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800260/*
Li Zefandbd3fbd2009-05-27 11:42:46 +0800261 * Avoid consuming memory with our now useless rbtree.
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800262 */
263static int tracing_stat_release(struct inode *i, struct file *f)
264{
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200265 struct stat_session *session = i->i_private;
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800266
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800267 reset_stat_session(session);
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800268
Li Zefan636eace2009-07-23 11:29:47 +0800269 return seq_release(i, f);
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800270}
271
272static const struct file_operations tracing_stat_fops = {
273 .open = tracing_stat_open,
274 .read = seq_read,
275 .llseek = seq_lseek,
276 .release = tracing_stat_release
277};
278
Frederic Weisbecker002bb862009-01-10 11:34:13 -0800279static int tracing_stat_init(void)
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800280{
281 struct dentry *d_tracing;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800282
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800283 d_tracing = tracing_init_dentry();
Steven Rostedt (Red Hat)14a5ae42015-01-20 11:14:16 -0500284 if (IS_ERR(d_tracing))
Namhyung Kimed6f1c92013-04-10 09:18:12 +0900285 return 0;
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800286
Steven Rostedt (Red Hat)8434dc92015-01-20 12:13:40 -0500287 stat_dir = tracefs_create_dir("trace_stat", d_tracing);
Frederic Weisbecker034939b2009-01-08 10:03:56 -0800288 if (!stat_dir)
Joe Perchesa395d6a2016-03-22 14:28:09 -0700289 pr_warn("Could not create tracefs 'trace_stat' entry\n");
Frederic Weisbeckerdbd0b4b2008-12-28 20:44:51 -0800290 return 0;
291}
Frederic Weisbecker002bb862009-01-10 11:34:13 -0800292
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200293static int init_stat_file(struct stat_session *session)
Frederic Weisbecker002bb862009-01-10 11:34:13 -0800294{
295 if (!stat_dir && tracing_stat_init())
296 return -ENODEV;
297
Steven Rostedt (Red Hat)8434dc92015-01-20 12:13:40 -0500298 session->file = tracefs_create_file(session->ts->name, 0644,
Frederic Weisbecker002bb862009-01-10 11:34:13 -0800299 stat_dir,
300 session, &tracing_stat_fops);
301 if (!session->file)
302 return -ENOMEM;
303 return 0;
304}
Ingo Molnar559221732009-01-15 11:31:21 +0100305
306int register_stat_tracer(struct tracer_stat *trace)
307{
Frederic Weisbecker43bd1232009-05-30 04:25:30 +0200308 struct stat_session *session, *node;
Ingo Molnar559221732009-01-15 11:31:21 +0100309 int ret;
310
311 if (!trace)
312 return -EINVAL;
313
314 if (!trace->stat_start || !trace->stat_next || !trace->stat_show)
315 return -EINVAL;
316
317 /* Already registered? */
318 mutex_lock(&all_stat_sessions_mutex);
Frederic Weisbecker43bd1232009-05-30 04:25:30 +0200319 list_for_each_entry(node, &all_stat_sessions, session_list) {
Ingo Molnar559221732009-01-15 11:31:21 +0100320 if (node->ts == trace) {
321 mutex_unlock(&all_stat_sessions_mutex);
322 return -EINVAL;
323 }
324 }
325 mutex_unlock(&all_stat_sessions_mutex);
326
327 /* Init the session */
Frederic Weisbecker8f184f22009-05-16 06:24:36 +0200328 session = kzalloc(sizeof(*session), GFP_KERNEL);
Ingo Molnar559221732009-01-15 11:31:21 +0100329 if (!session)
330 return -ENOMEM;
331
332 session->ts = trace;
333 INIT_LIST_HEAD(&session->session_list);
Ingo Molnar559221732009-01-15 11:31:21 +0100334 mutex_init(&session->stat_mutex);
Ingo Molnar559221732009-01-15 11:31:21 +0100335
336 ret = init_stat_file(session);
337 if (ret) {
338 destroy_session(session);
339 return ret;
340 }
341
342 /* Register */
343 mutex_lock(&all_stat_sessions_mutex);
344 list_add_tail(&session->session_list, &all_stat_sessions);
345 mutex_unlock(&all_stat_sessions_mutex);
346
347 return 0;
348}
349
350void unregister_stat_tracer(struct tracer_stat *trace)
351{
Frederic Weisbecker0d64f832009-05-16 05:58:49 +0200352 struct stat_session *node, *tmp;
Ingo Molnar559221732009-01-15 11:31:21 +0100353
354 mutex_lock(&all_stat_sessions_mutex);
355 list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) {
356 if (node->ts == trace) {
357 list_del(&node->session_list);
358 destroy_session(node);
359 break;
360 }
361 }
362 mutex_unlock(&all_stat_sessions_mutex);
363}