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