summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@kernel.org>2018-11-05 04:02:08 -0500
committerSteven Rostedt (VMware) <rostedt@goodmis.org>2018-12-08 20:54:09 -0500
commit5448d44c38557fc15d1c53b608a9c9f0e1ca8f86 (patch)
tree8ac0ff173059aedfc79bf1c34b10317d30a3e523
parentd00bbea9456f35fb34310d454e561f05d68d07fe (diff)
tracing: Add unified dynamic event framework
Add unified dynamic event framework for ftrace kprobes, uprobes and synthetic events. Those dynamic events can be co-exist on same file because those syntax doesn't overlap. This introduces a framework part which provides a unified tracefs interface and operations. Link: http://lkml.kernel.org/r/154140852824.17322.12250362185969352095.stgit@devbox Reviewed-by: Tom Zanussi <tom.zanussi@linux.intel.com> Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
-rw-r--r--kernel/trace/Kconfig3
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/trace.c4
-rw-r--r--kernel/trace/trace_dynevent.c210
-rw-r--r--kernel/trace/trace_dynevent.h119
5 files changed, 337 insertions, 0 deletions
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 5e3de28c7677..bf2e8a5a91f1 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -518,6 +518,9 @@ config BPF_EVENTS
518 help 518 help
519 This allows the user to attach BPF programs to kprobe events. 519 This allows the user to attach BPF programs to kprobe events.
520 520
521config DYNAMIC_EVENTS
522 def_bool n
523
521config PROBE_EVENTS 524config PROBE_EVENTS
522 def_bool n 525 def_bool n
523 526
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index c7ade7965464..c2b2148bb1d2 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -79,6 +79,7 @@ endif
79ifeq ($(CONFIG_TRACING),y) 79ifeq ($(CONFIG_TRACING),y)
80obj-$(CONFIG_KGDB_KDB) += trace_kdb.o 80obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
81endif 81endif
82obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o
82obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o 83obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o
83obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o 84obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o
84 85
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 194c01838e3f..7e0332f90ed4 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4604,6 +4604,10 @@ static const char readme_msg[] =
4604 "\t\t\t traces\n" 4604 "\t\t\t traces\n"
4605#endif 4605#endif
4606#endif /* CONFIG_STACK_TRACER */ 4606#endif /* CONFIG_STACK_TRACER */
4607#ifdef CONFIG_DYNAMIC_EVENTS
4608 " dynamic_events\t\t- Add/remove/show the generic dynamic events\n"
4609 "\t\t\t Write into this file to define/undefine new trace events.\n"
4610#endif
4607#ifdef CONFIG_KPROBE_EVENTS 4611#ifdef CONFIG_KPROBE_EVENTS
4608 " kprobe_events\t\t- Add/remove/show the kernel dynamic events\n" 4612 " kprobe_events\t\t- Add/remove/show the kernel dynamic events\n"
4609 "\t\t\t Write into this file to define/undefine new trace events.\n" 4613 "\t\t\t Write into this file to define/undefine new trace events.\n"
diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c
new file mode 100644
index 000000000000..f17a887abb66
--- /dev/null
+++ b/kernel/trace/trace_dynevent.c
@@ -0,0 +1,210 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Generic dynamic event control interface
4 *
5 * Copyright (C) 2018 Masami Hiramatsu <mhiramat@kernel.org>
6 */
7
8#include <linux/debugfs.h>
9#include <linux/kernel.h>
10#include <linux/list.h>
11#include <linux/mm.h>
12#include <linux/mutex.h>
13#include <linux/tracefs.h>
14
15#include "trace.h"
16#include "trace_dynevent.h"
17
18static DEFINE_MUTEX(dyn_event_ops_mutex);
19static LIST_HEAD(dyn_event_ops_list);
20
21int dyn_event_register(struct dyn_event_operations *ops)
22{
23 if (!ops || !ops->create || !ops->show || !ops->is_busy ||
24 !ops->free || !ops->match)
25 return -EINVAL;
26
27 INIT_LIST_HEAD(&ops->list);
28 mutex_lock(&dyn_event_ops_mutex);
29 list_add_tail(&ops->list, &dyn_event_ops_list);
30 mutex_unlock(&dyn_event_ops_mutex);
31 return 0;
32}
33
34int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type)
35{
36 struct dyn_event *pos, *n;
37 char *system = NULL, *event, *p;
38 int ret = -ENOENT;
39
40 if (argv[0][1] != ':')
41 return -EINVAL;
42
43 event = &argv[0][2];
44 p = strchr(event, '/');
45 if (p) {
46 system = event;
47 event = p + 1;
48 *p = '\0';
49 }
50 if (event[0] == '\0')
51 return -EINVAL;
52
53 mutex_lock(&event_mutex);
54 for_each_dyn_event_safe(pos, n) {
55 if (type && type != pos->ops)
56 continue;
57 if (pos->ops->match(system, event, pos)) {
58 ret = pos->ops->free(pos);
59 break;
60 }
61 }
62 mutex_unlock(&event_mutex);
63
64 return ret;
65}
66
67static int create_dyn_event(int argc, char **argv)
68{
69 struct dyn_event_operations *ops;
70 int ret;
71
72 if (argv[0][0] == '-')
73 return dyn_event_release(argc, argv, NULL);
74
75 mutex_lock(&dyn_event_ops_mutex);
76 list_for_each_entry(ops, &dyn_event_ops_list, list) {
77 ret = ops->create(argc, (const char **)argv);
78 if (!ret || ret != -ECANCELED)
79 break;
80 }
81 mutex_unlock(&dyn_event_ops_mutex);
82 if (ret == -ECANCELED)
83 ret = -EINVAL;
84
85 return ret;
86}
87
88/* Protected by event_mutex */
89LIST_HEAD(dyn_event_list);
90
91void *dyn_event_seq_start(struct seq_file *m, loff_t *pos)
92{
93 mutex_lock(&event_mutex);
94 return seq_list_start(&dyn_event_list, *pos);
95}
96
97void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos)
98{
99 return seq_list_next(v, &dyn_event_list, pos);
100}
101
102void dyn_event_seq_stop(struct seq_file *m, void *v)
103{
104 mutex_unlock(&event_mutex);
105}
106
107static int dyn_event_seq_show(struct seq_file *m, void *v)
108{
109 struct dyn_event *ev = v;
110
111 if (ev && ev->ops)
112 return ev->ops->show(m, ev);
113
114 return 0;
115}
116
117static const struct seq_operations dyn_event_seq_op = {
118 .start = dyn_event_seq_start,
119 .next = dyn_event_seq_next,
120 .stop = dyn_event_seq_stop,
121 .show = dyn_event_seq_show
122};
123
124/*
125 * dyn_events_release_all - Release all specific events
126 * @type: the dyn_event_operations * which filters releasing events
127 *
128 * This releases all events which ->ops matches @type. If @type is NULL,
129 * all events are released.
130 * Return -EBUSY if any of them are in use, and return other errors when
131 * it failed to free the given event. Except for -EBUSY, event releasing
132 * process will be aborted at that point and there may be some other
133 * releasable events on the list.
134 */
135int dyn_events_release_all(struct dyn_event_operations *type)
136{
137 struct dyn_event *ev, *tmp;
138 int ret = 0;
139
140 mutex_lock(&event_mutex);
141 for_each_dyn_event(ev) {
142 if (type && ev->ops != type)
143 continue;
144 if (ev->ops->is_busy(ev)) {
145 ret = -EBUSY;
146 goto out;
147 }
148 }
149 for_each_dyn_event_safe(ev, tmp) {
150 if (type && ev->ops != type)
151 continue;
152 ret = ev->ops->free(ev);
153 if (ret)
154 break;
155 }
156out:
157 mutex_unlock(&event_mutex);
158
159 return ret;
160}
161
162static int dyn_event_open(struct inode *inode, struct file *file)
163{
164 int ret;
165
166 if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
167 ret = dyn_events_release_all(NULL);
168 if (ret < 0)
169 return ret;
170 }
171
172 return seq_open(file, &dyn_event_seq_op);
173}
174
175static ssize_t dyn_event_write(struct file *file, const char __user *buffer,
176 size_t count, loff_t *ppos)
177{
178 return trace_parse_run_command(file, buffer, count, ppos,
179 create_dyn_event);
180}
181
182static const struct file_operations dynamic_events_ops = {
183 .owner = THIS_MODULE,
184 .open = dyn_event_open,
185 .read = seq_read,
186 .llseek = seq_lseek,
187 .release = seq_release,
188 .write = dyn_event_write,
189};
190
191/* Make a tracefs interface for controlling dynamic events */
192static __init int init_dynamic_event(void)
193{
194 struct dentry *d_tracer;
195 struct dentry *entry;
196
197 d_tracer = tracing_init_dentry();
198 if (IS_ERR(d_tracer))
199 return 0;
200
201 entry = tracefs_create_file("dynamic_events", 0644, d_tracer,
202 NULL, &dynamic_events_ops);
203
204 /* Event list interface */
205 if (!entry)
206 pr_warn("Could not create tracefs 'dynamic_events' entry\n");
207
208 return 0;
209}
210fs_initcall(init_dynamic_event);
diff --git a/kernel/trace/trace_dynevent.h b/kernel/trace/trace_dynevent.h
new file mode 100644
index 000000000000..8c334064e4d6
--- /dev/null
+++ b/kernel/trace/trace_dynevent.h
@@ -0,0 +1,119 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Common header file for generic dynamic events.
4 */
5
6#ifndef _TRACE_DYNEVENT_H
7#define _TRACE_DYNEVENT_H
8
9#include <linux/kernel.h>
10#include <linux/list.h>
11#include <linux/mutex.h>
12#include <linux/seq_file.h>
13
14#include "trace.h"
15
16struct dyn_event;
17
18/**
19 * struct dyn_event_operations - Methods for each type of dynamic events
20 *
21 * These methods must be set for each type, since there is no default method.
22 * Before using this for dyn_event_init(), it must be registered by
23 * dyn_event_register().
24 *
25 * @create: Parse and create event method. This is invoked when user passes
26 * a event definition to dynamic_events interface. This must not destruct
27 * the arguments and return -ECANCELED if given arguments doesn't match its
28 * command prefix.
29 * @show: Showing method. This is invoked when user reads the event definitions
30 * via dynamic_events interface.
31 * @is_busy: Check whether given event is busy so that it can not be deleted.
32 * Return true if it is busy, otherwides false.
33 * @free: Delete the given event. Return 0 if success, otherwides error.
34 * @match: Check whether given event and system name match this event.
35 * Return true if it matches, otherwides false.
36 *
37 * Except for @create, these methods are called under holding event_mutex.
38 */
39struct dyn_event_operations {
40 struct list_head list;
41 int (*create)(int argc, const char *argv[]);
42 int (*show)(struct seq_file *m, struct dyn_event *ev);
43 bool (*is_busy)(struct dyn_event *ev);
44 int (*free)(struct dyn_event *ev);
45 bool (*match)(const char *system, const char *event,
46 struct dyn_event *ev);
47};
48
49/* Register new dyn_event type -- must be called at first */
50int dyn_event_register(struct dyn_event_operations *ops);
51
52/**
53 * struct dyn_event - Dynamic event list header
54 *
55 * The dyn_event structure encapsulates a list and a pointer to the operators
56 * for making a global list of dynamic events.
57 * User must includes this in each event structure, so that those events can
58 * be added/removed via dynamic_events interface.
59 */
60struct dyn_event {
61 struct list_head list;
62 struct dyn_event_operations *ops;
63};
64
65extern struct list_head dyn_event_list;
66
67static inline
68int dyn_event_init(struct dyn_event *ev, struct dyn_event_operations *ops)
69{
70 if (!ev || !ops)
71 return -EINVAL;
72
73 INIT_LIST_HEAD(&ev->list);
74 ev->ops = ops;
75 return 0;
76}
77
78static inline int dyn_event_add(struct dyn_event *ev)
79{
80 lockdep_assert_held(&event_mutex);
81
82 if (!ev || !ev->ops)
83 return -EINVAL;
84
85 list_add_tail(&ev->list, &dyn_event_list);
86 return 0;
87}
88
89static inline void dyn_event_remove(struct dyn_event *ev)
90{
91 lockdep_assert_held(&event_mutex);
92 list_del_init(&ev->list);
93}
94
95void *dyn_event_seq_start(struct seq_file *m, loff_t *pos);
96void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos);
97void dyn_event_seq_stop(struct seq_file *m, void *v);
98int dyn_events_release_all(struct dyn_event_operations *type);
99int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type);
100
101/*
102 * for_each_dyn_event - iterate over the dyn_event list
103 * @pos: the struct dyn_event * to use as a loop cursor
104 *
105 * This is just a basement of for_each macro. Wrap this for
106 * each actual event structure with ops filtering.
107 */
108#define for_each_dyn_event(pos) \
109 list_for_each_entry(pos, &dyn_event_list, list)
110
111/*
112 * for_each_dyn_event - iterate over the dyn_event list safely
113 * @pos: the struct dyn_event * to use as a loop cursor
114 * @n: the struct dyn_event * to use as temporary storage
115 */
116#define for_each_dyn_event_safe(pos, n) \
117 list_for_each_entry_safe(pos, n, &dyn_event_list, list)
118
119#endif