Disable block rwbs bitwise enum in default build
[lttng-modules.git] / lttng-tracepoint.c
1 /* SPDX-License-Identifier: (GPL-2.0 or LGPL-2.1)
2 *
3 * lttng-tracepoint.c
4 *
5 * LTTng adaptation layer for Linux kernel 3.15+ tracepoints.
6 *
7 * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
8 */
9
10 #include <linux/mutex.h>
11 #include <linux/err.h>
12 #include <linux/notifier.h>
13 #include <linux/tracepoint.h>
14 #include <linux/slab.h>
15 #include <linux/jhash.h>
16 #include <linux/module.h>
17
18 #include <lttng-tracepoint.h>
19 #include <wrapper/list.h>
20 #include <wrapper/tracepoint.h>
21
22 /*
23 * Protect the tracepoint table. lttng_tracepoint_mutex nests within
24 * kernel/tracepoint.c tp_modlist_mutex. kernel/tracepoint.c
25 * tracepoint_mutex nests within lttng_tracepoint_mutex.
26 */
27 static
28 DEFINE_MUTEX(lttng_tracepoint_mutex);
29
30 #define TRACEPOINT_HASH_BITS 6
31 #define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
32 static
33 struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
34
35 /*
36 * The tracepoint entry is the node contained within the hash table. It
37 * is a mapping from the "string" key to the struct tracepoint pointer.
38 */
39 struct tracepoint_entry {
40 struct hlist_node hlist;
41 struct tracepoint *tp;
42 int refcount;
43 struct list_head probes;
44 char name[0];
45 };
46
47 struct lttng_tp_probe {
48 struct tracepoint_func tp_func;
49 struct list_head list;
50 };
51
52 static
53 int add_probe(struct tracepoint_entry *e, void *probe, void *data)
54 {
55 struct lttng_tp_probe *p;
56 int found = 0;
57
58 list_for_each_entry(p, &e->probes, list) {
59 if (p->tp_func.func == probe && p->tp_func.data == data) {
60 found = 1;
61 break;
62 }
63 }
64 if (found)
65 return -EEXIST;
66 p = kmalloc(sizeof(struct lttng_tp_probe), GFP_KERNEL);
67 if (!p)
68 return -ENOMEM;
69 p->tp_func.func = probe;
70 p->tp_func.data = data;
71 list_add(&p->list, &e->probes);
72 return 0;
73 }
74
75 static
76 int remove_probe(struct tracepoint_entry *e, void *probe, void *data)
77 {
78 struct lttng_tp_probe *p;
79 int found = 0;
80
81 list_for_each_entry(p, &e->probes, list) {
82 if (p->tp_func.func == probe && p->tp_func.data == data) {
83 found = 1;
84 break;
85 }
86 }
87 if (found) {
88 list_del(&p->list);
89 kfree(p);
90 return 0;
91 } else {
92 WARN_ON(1);
93 return -ENOENT;
94 }
95 }
96
97 /*
98 * Get tracepoint if the tracepoint is present in the tracepoint hash table.
99 * Must be called with lttng_tracepoint_mutex held.
100 * Returns NULL if not present.
101 */
102 static
103 struct tracepoint_entry *get_tracepoint(const char *name)
104 {
105 struct hlist_head *head;
106 struct tracepoint_entry *e;
107 u32 hash = jhash(name, strlen(name), 0);
108
109 head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
110 lttng_hlist_for_each_entry(e, head, hlist) {
111 if (!strcmp(name, e->name))
112 return e;
113 }
114 return NULL;
115 }
116
117 /*
118 * Add the tracepoint to the tracepoint hash table. Must be called with
119 * lttng_tracepoint_mutex held.
120 */
121 static
122 struct tracepoint_entry *add_tracepoint(const char *name)
123 {
124 struct hlist_head *head;
125 struct tracepoint_entry *e;
126 size_t name_len = strlen(name) + 1;
127 u32 hash = jhash(name, name_len - 1, 0);
128
129 head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
130 lttng_hlist_for_each_entry(e, head, hlist) {
131 if (!strcmp(name, e->name)) {
132 printk(KERN_NOTICE
133 "tracepoint %s busy\n", name);
134 return ERR_PTR(-EEXIST); /* Already there */
135 }
136 }
137 /*
138 * Using kmalloc here to allocate a variable length element. Could
139 * cause some memory fragmentation if overused.
140 */
141 e = kmalloc(sizeof(struct tracepoint_entry) + name_len, GFP_KERNEL);
142 if (!e)
143 return ERR_PTR(-ENOMEM);
144 memcpy(&e->name[0], name, name_len);
145 e->tp = NULL;
146 e->refcount = 0;
147 INIT_LIST_HEAD(&e->probes);
148 hlist_add_head(&e->hlist, head);
149 return e;
150 }
151
152 /*
153 * Remove the tracepoint from the tracepoint hash table. Must be called
154 * with lttng_tracepoint_mutex held.
155 */
156 static
157 void remove_tracepoint(struct tracepoint_entry *e)
158 {
159 hlist_del(&e->hlist);
160 kfree(e);
161 }
162
163 int lttng_tracepoint_probe_register(const char *name, void *probe, void *data)
164 {
165 struct tracepoint_entry *e;
166 int ret = 0;
167
168 mutex_lock(&lttng_tracepoint_mutex);
169 e = get_tracepoint(name);
170 if (!e) {
171 e = add_tracepoint(name);
172 if (IS_ERR(e)) {
173 ret = PTR_ERR(e);
174 goto end;
175 }
176 }
177 /* add (probe, data) to entry */
178 ret = add_probe(e, probe, data);
179 if (ret)
180 goto end;
181 e->refcount++;
182 if (e->tp) {
183 ret = tracepoint_probe_register(e->tp, probe, data);
184 WARN_ON_ONCE(ret);
185 ret = 0;
186 }
187 end:
188 mutex_unlock(&lttng_tracepoint_mutex);
189 return ret;
190 }
191
192 int lttng_tracepoint_probe_unregister(const char *name, void *probe, void *data)
193 {
194 struct tracepoint_entry *e;
195 int ret = 0;
196
197 mutex_lock(&lttng_tracepoint_mutex);
198 e = get_tracepoint(name);
199 if (!e) {
200 ret = -ENOENT;
201 goto end;
202 }
203 /* remove (probe, data) from entry */
204 ret = remove_probe(e, probe, data);
205 if (ret)
206 goto end;
207 if (e->tp) {
208 ret = tracepoint_probe_unregister(e->tp, probe, data);
209 WARN_ON_ONCE(ret);
210 ret = 0;
211 }
212 if (!--e->refcount)
213 remove_tracepoint(e);
214 end:
215 mutex_unlock(&lttng_tracepoint_mutex);
216 return ret;
217 }
218
219 #ifdef CONFIG_MODULES
220
221 static
222 int lttng_tracepoint_coming(struct tp_module *tp_mod)
223 {
224 int i;
225
226 mutex_lock(&lttng_tracepoint_mutex);
227 for (i = 0; i < tp_mod->mod->num_tracepoints; i++) {
228 struct tracepoint *tp;
229 struct tracepoint_entry *e;
230 struct lttng_tp_probe *p;
231
232 tp = lttng_tracepoint_ptr_deref(&tp_mod->mod->tracepoints_ptrs[i]);
233 e = get_tracepoint(tp->name);
234 if (!e) {
235 e = add_tracepoint(tp->name);
236 if (IS_ERR(e)) {
237 pr_warn("LTTng: error (%ld) adding tracepoint\n",
238 PTR_ERR(e));
239 continue;
240 }
241 }
242 /* If already enabled, just check consistency */
243 if (e->tp) {
244 WARN_ON(e->tp != tp);
245 continue;
246 }
247 e->tp = tp;
248 e->refcount++;
249 /* register each (probe, data) */
250 list_for_each_entry(p, &e->probes, list) {
251 int ret;
252
253 ret = tracepoint_probe_register(e->tp,
254 p->tp_func.func, p->tp_func.data);
255 WARN_ON_ONCE(ret);
256 }
257 }
258 mutex_unlock(&lttng_tracepoint_mutex);
259 return NOTIFY_OK;
260 }
261
262 static
263 int lttng_tracepoint_going(struct tp_module *tp_mod)
264 {
265 int i;
266
267 mutex_lock(&lttng_tracepoint_mutex);
268 for (i = 0; i < tp_mod->mod->num_tracepoints; i++) {
269 struct tracepoint *tp;
270 struct tracepoint_entry *e;
271 struct lttng_tp_probe *p;
272
273 tp = lttng_tracepoint_ptr_deref(&tp_mod->mod->tracepoints_ptrs[i]);
274 e = get_tracepoint(tp->name);
275 if (!e || !e->tp)
276 continue;
277 /* unregister each (probe, data) */
278 list_for_each_entry(p, &e->probes, list) {
279 int ret;
280
281 ret = tracepoint_probe_unregister(e->tp,
282 p->tp_func.func, p->tp_func.data);
283 WARN_ON_ONCE(ret);
284 }
285 e->tp = NULL;
286 if (!--e->refcount)
287 remove_tracepoint(e);
288 }
289 mutex_unlock(&lttng_tracepoint_mutex);
290 return 0;
291 }
292
293 static
294 int lttng_tracepoint_notify(struct notifier_block *self,
295 unsigned long val, void *data)
296 {
297 struct tp_module *tp_mod = data;
298 int ret = 0;
299
300 switch (val) {
301 case MODULE_STATE_COMING:
302 ret = lttng_tracepoint_coming(tp_mod);
303 break;
304 case MODULE_STATE_GOING:
305 ret = lttng_tracepoint_going(tp_mod);
306 break;
307 default:
308 break;
309 }
310 return ret;
311 }
312
313 static
314 struct notifier_block lttng_tracepoint_notifier = {
315 .notifier_call = lttng_tracepoint_notify,
316 .priority = 0,
317 };
318
319 static
320 int lttng_tracepoint_module_init(void)
321 {
322 return register_tracepoint_module_notifier(&lttng_tracepoint_notifier);
323 }
324
325 static
326 void lttng_tracepoint_module_exit(void)
327 {
328 WARN_ON(unregister_tracepoint_module_notifier(&lttng_tracepoint_notifier));
329 }
330
331 #else /* #ifdef CONFIG_MODULES */
332
333 static
334 int lttng_tracepoint_module_init(void)
335 {
336 return 0;
337 }
338
339 static
340 void lttng_tracepoint_module_exit(void)
341 {
342 }
343
344 #endif /* #else #ifdef CONFIG_MODULES */
345
346 static
347 void lttng_kernel_tracepoint_add(struct tracepoint *tp, void *priv)
348 {
349 struct tracepoint_entry *e;
350 struct lttng_tp_probe *p;
351 int *ret = priv;
352
353 mutex_lock(&lttng_tracepoint_mutex);
354 e = get_tracepoint(tp->name);
355 if (!e) {
356 e = add_tracepoint(tp->name);
357 if (IS_ERR(e)) {
358 pr_warn("LTTng: error (%ld) adding tracepoint\n",
359 PTR_ERR(e));
360 *ret = (int) PTR_ERR(e);
361 goto end;
362 }
363 }
364 /* If already enabled, just check consistency */
365 if (e->tp) {
366 WARN_ON(e->tp != tp);
367 goto end;
368 }
369 e->tp = tp;
370 e->refcount++;
371 /* register each (probe, data) */
372 list_for_each_entry(p, &e->probes, list) {
373 int ret;
374
375 ret = tracepoint_probe_register(e->tp,
376 p->tp_func.func, p->tp_func.data);
377 WARN_ON_ONCE(ret);
378 }
379 end:
380 mutex_unlock(&lttng_tracepoint_mutex);
381 }
382
383 static
384 void lttng_kernel_tracepoint_remove(struct tracepoint *tp, void *priv)
385 {
386 struct tracepoint_entry *e;
387 int *ret = priv;
388
389 mutex_lock(&lttng_tracepoint_mutex);
390 e = get_tracepoint(tp->name);
391 if (!e || e->refcount != 1 || !list_empty(&e->probes)) {
392 *ret = -EINVAL;
393 goto end;
394 }
395 remove_tracepoint(e);
396 end:
397 mutex_unlock(&lttng_tracepoint_mutex);
398 }
399
400 int __init lttng_tracepoint_init(void)
401 {
402 int ret = 0;
403
404 for_each_kernel_tracepoint(lttng_kernel_tracepoint_add, &ret);
405 if (ret)
406 goto error;
407 ret = lttng_tracepoint_module_init();
408 if (ret)
409 goto error_module;
410 return 0;
411
412 error_module:
413 {
414 int error_ret = 0;
415
416 for_each_kernel_tracepoint(lttng_kernel_tracepoint_remove,
417 &error_ret);
418 WARN_ON(error_ret);
419 }
420 error:
421 return ret;
422 }
423
424 void lttng_tracepoint_exit(void)
425 {
426 int i, ret = 0;
427
428 lttng_tracepoint_module_exit();
429 for_each_kernel_tracepoint(lttng_kernel_tracepoint_remove, &ret);
430 WARN_ON(ret);
431 mutex_lock(&lttng_tracepoint_mutex);
432 for (i = 0; i < TRACEPOINT_TABLE_SIZE; i++) {
433 struct hlist_head *head = &tracepoint_table[i];
434
435 /* All tracepoints should be removed */
436 WARN_ON(!hlist_empty(head));
437 }
438 mutex_unlock(&lttng_tracepoint_mutex);
439 }
This page took 0.04277 seconds and 4 git commands to generate.