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