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