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