Fix: timer_expire_entry changed in 4.19.312
[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
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 */
26 static
27 DEFINE_MUTEX(lttng_tracepoint_mutex);
28
29 #define TRACEPOINT_HASH_BITS 6
30 #define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
31 static
32 struct 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 */
38 struct 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
46 struct lttng_tp_probe {
47 struct tracepoint_func tp_func;
48 struct list_head list;
49 };
50
51 static
52 int 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
74 static
75 int 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 */
101 static
102 struct 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)];
109 lttng_hlist_for_each_entry(e, head, hlist) {
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 */
120 static
121 struct 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)];
129 lttng_hlist_for_each_entry(e, head, hlist) {
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 */
155 static
156 void remove_tracepoint(struct tracepoint_entry *e)
157 {
158 hlist_del(&e->hlist);
159 kfree(e);
160 }
161
162 int 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 }
186 end:
187 mutex_unlock(&lttng_tracepoint_mutex);
188 return ret;
189 }
190
191 int 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);
213 end:
214 mutex_unlock(&lttng_tracepoint_mutex);
215 return ret;
216 }
217
218 #ifdef CONFIG_MODULES
219
220 static
221 int 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
261 static
262 int 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
292 static
293 int 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
312 static
313 struct notifier_block lttng_tracepoint_notifier = {
314 .notifier_call = lttng_tracepoint_notify,
315 .priority = 0,
316 };
317
318 static
319 int lttng_tracepoint_module_init(void)
320 {
321 return register_tracepoint_module_notifier(&lttng_tracepoint_notifier);
322 }
323
324 static
325 void lttng_tracepoint_module_exit(void)
326 {
327 WARN_ON(unregister_tracepoint_module_notifier(&lttng_tracepoint_notifier));
328 }
329
330 #else /* #ifdef CONFIG_MODULES */
331
332 static
333 int lttng_tracepoint_module_init(void)
334 {
335 return 0;
336 }
337
338 static
339 void lttng_tracepoint_module_exit(void)
340 {
341 }
342
343 #endif /* #else #ifdef CONFIG_MODULES */
344
345 static
346 void 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 }
378 end:
379 mutex_unlock(&lttng_tracepoint_mutex);
380 }
381
382 static
383 void 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);
395 end:
396 mutex_unlock(&lttng_tracepoint_mutex);
397 }
398
399 int __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
411 error_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 }
419 error:
420 return ret;
421 }
422
423 void 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.037243 seconds and 4 git commands to generate.