aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/tracepoint.h4
-rw-r--r--kernel/tracepoint.c170
2 files changed, 136 insertions, 38 deletions
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index c5bb39c7a770..63064e9403f2 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -112,6 +112,10 @@ extern int tracepoint_probe_register(const char *name, void *probe);
112 */ 112 */
113extern int tracepoint_probe_unregister(const char *name, void *probe); 113extern int tracepoint_probe_unregister(const char *name, void *probe);
114 114
115extern int tracepoint_probe_register_noupdate(const char *name, void *probe);
116extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe);
117extern void tracepoint_probe_update_all(void);
118
115struct tracepoint_iter { 119struct tracepoint_iter {
116 struct module *module; 120 struct module *module;
117 struct tracepoint *tracepoint; 121 struct tracepoint *tracepoint;
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index 3e22867184e3..e96590f17de1 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -59,7 +59,10 @@ struct tracepoint_entry {
59}; 59};
60 60
61struct tp_probes { 61struct tp_probes {
62 struct rcu_head rcu; 62 union {
63 struct rcu_head rcu;
64 struct list_head list;
65 } u;
63 void *probes[0]; 66 void *probes[0];
64}; 67};
65 68
@@ -72,7 +75,7 @@ static inline void *allocate_probes(int count)
72 75
73static void rcu_free_old_probes(struct rcu_head *head) 76static void rcu_free_old_probes(struct rcu_head *head)
74{ 77{
75 kfree(container_of(head, struct tp_probes, rcu)); 78 kfree(container_of(head, struct tp_probes, u.rcu));
76} 79}
77 80
78static inline void release_probes(void *old) 81static inline void release_probes(void *old)
@@ -80,7 +83,7 @@ static inline void release_probes(void *old)
80 if (old) { 83 if (old) {
81 struct tp_probes *tp_probes = container_of(old, 84 struct tp_probes *tp_probes = container_of(old,
82 struct tp_probes, probes[0]); 85 struct tp_probes, probes[0]);
83 call_rcu(&tp_probes->rcu, rcu_free_old_probes); 86 call_rcu_sched(&tp_probes->u.rcu, rcu_free_old_probes);
84 } 87 }
85} 88}
86 89
@@ -299,6 +302,23 @@ static void tracepoint_update_probes(void)
299 module_update_tracepoints(); 302 module_update_tracepoints();
300} 303}
301 304
305static void *tracepoint_add_probe(const char *name, void *probe)
306{
307 struct tracepoint_entry *entry;
308 void *old;
309
310 entry = get_tracepoint(name);
311 if (!entry) {
312 entry = add_tracepoint(name);
313 if (IS_ERR(entry))
314 return entry;
315 }
316 old = tracepoint_entry_add_probe(entry, probe);
317 if (IS_ERR(old) && !entry->refcount)
318 remove_tracepoint(entry);
319 return old;
320}
321
302/** 322/**
303 * tracepoint_probe_register - Connect a probe to a tracepoint 323 * tracepoint_probe_register - Connect a probe to a tracepoint
304 * @name: tracepoint name 324 * @name: tracepoint name
@@ -309,36 +329,36 @@ static void tracepoint_update_probes(void)
309 */ 329 */
310int tracepoint_probe_register(const char *name, void *probe) 330int tracepoint_probe_register(const char *name, void *probe)
311{ 331{
312 struct tracepoint_entry *entry;
313 int ret = 0;
314 void *old; 332 void *old;
315 333
316 mutex_lock(&tracepoints_mutex); 334 mutex_lock(&tracepoints_mutex);
317 entry = get_tracepoint(name); 335 old = tracepoint_add_probe(name, probe);
318 if (!entry) {
319 entry = add_tracepoint(name);
320 if (IS_ERR(entry)) {
321 ret = PTR_ERR(entry);
322 goto end;
323 }
324 }
325 old = tracepoint_entry_add_probe(entry, probe);
326 if (IS_ERR(old)) {
327 if (!entry->refcount)
328 remove_tracepoint(entry);
329 ret = PTR_ERR(old);
330 goto end;
331 }
332 mutex_unlock(&tracepoints_mutex); 336 mutex_unlock(&tracepoints_mutex);
337 if (IS_ERR(old))
338 return PTR_ERR(old);
339
333 tracepoint_update_probes(); /* may update entry */ 340 tracepoint_update_probes(); /* may update entry */
334 release_probes(old); 341 release_probes(old);
335 return 0; 342 return 0;
336end:
337 mutex_unlock(&tracepoints_mutex);
338 return ret;
339} 343}
340EXPORT_SYMBOL_GPL(tracepoint_probe_register); 344EXPORT_SYMBOL_GPL(tracepoint_probe_register);
341 345
346static void *tracepoint_remove_probe(const char *name, void *probe)
347{
348 struct tracepoint_entry *entry;
349 void *old;
350
351 entry = get_tracepoint(name);
352 if (!entry)
353 return ERR_PTR(-ENOENT);
354 old = tracepoint_entry_remove_probe(entry, probe);
355 if (IS_ERR(old))
356 return old;
357 if (!entry->refcount)
358 remove_tracepoint(entry);
359 return old;
360}
361
342/** 362/**
343 * tracepoint_probe_unregister - Disconnect a probe from a tracepoint 363 * tracepoint_probe_unregister - Disconnect a probe from a tracepoint
344 * @name: tracepoint name 364 * @name: tracepoint name
@@ -351,31 +371,105 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_register);
351 */ 371 */
352int tracepoint_probe_unregister(const char *name, void *probe) 372int tracepoint_probe_unregister(const char *name, void *probe)
353{ 373{
354 struct tracepoint_entry *entry;
355 void *old; 374 void *old;
356 int ret = -ENOENT;
357 375
358 mutex_lock(&tracepoints_mutex); 376 mutex_lock(&tracepoints_mutex);
359 entry = get_tracepoint(name); 377 old = tracepoint_remove_probe(name, probe);
360 if (!entry)
361 goto end;
362 old = tracepoint_entry_remove_probe(entry, probe);
363 if (IS_ERR(old)) {
364 ret = PTR_ERR(old);
365 goto end;
366 }
367 if (!entry->refcount)
368 remove_tracepoint(entry);
369 mutex_unlock(&tracepoints_mutex); 378 mutex_unlock(&tracepoints_mutex);
379 if (IS_ERR(old))
380 return PTR_ERR(old);
381
370 tracepoint_update_probes(); /* may update entry */ 382 tracepoint_update_probes(); /* may update entry */
371 release_probes(old); 383 release_probes(old);
372 return 0; 384 return 0;
373end:
374 mutex_unlock(&tracepoints_mutex);
375 return ret;
376} 385}
377EXPORT_SYMBOL_GPL(tracepoint_probe_unregister); 386EXPORT_SYMBOL_GPL(tracepoint_probe_unregister);
378 387
388static LIST_HEAD(old_probes);
389static int need_update;
390
391static void tracepoint_add_old_probes(void *old)
392{
393 need_update = 1;
394 if (old) {
395 struct tp_probes *tp_probes = container_of(old,
396 struct tp_probes, probes[0]);
397 list_add(&tp_probes->u.list, &old_probes);
398 }
399}
400
401/**
402 * tracepoint_probe_register_noupdate - register a probe but not connect
403 * @name: tracepoint name
404 * @probe: probe handler
405 *
406 * caller must call tracepoint_probe_update_all()
407 */
408int tracepoint_probe_register_noupdate(const char *name, void *probe)
409{
410 void *old;
411
412 mutex_lock(&tracepoints_mutex);
413 old = tracepoint_add_probe(name, probe);
414 if (IS_ERR(old)) {
415 mutex_unlock(&tracepoints_mutex);
416 return PTR_ERR(old);
417 }
418 tracepoint_add_old_probes(old);
419 mutex_unlock(&tracepoints_mutex);
420 return 0;
421}
422EXPORT_SYMBOL_GPL(tracepoint_probe_register_noupdate);
423
424/**
425 * tracepoint_probe_unregister_noupdate - remove a probe but not disconnect
426 * @name: tracepoint name
427 * @probe: probe function pointer
428 *
429 * caller must call tracepoint_probe_update_all()
430 */
431int tracepoint_probe_unregister_noupdate(const char *name, void *probe)
432{
433 void *old;
434
435 mutex_lock(&tracepoints_mutex);
436 old = tracepoint_remove_probe(name, probe);
437 if (IS_ERR(old)) {
438 mutex_unlock(&tracepoints_mutex);
439 return PTR_ERR(old);
440 }
441 tracepoint_add_old_probes(old);
442 mutex_unlock(&tracepoints_mutex);
443 return 0;
444}
445EXPORT_SYMBOL_GPL(tracepoint_probe_unregister_noupdate);
446
447/**
448 * tracepoint_probe_update_all - update tracepoints
449 */
450void tracepoint_probe_update_all(void)
451{
452 LIST_HEAD(release_probes);
453 struct tp_probes *pos, *next;
454
455 mutex_lock(&tracepoints_mutex);
456 if (!need_update) {
457 mutex_unlock(&tracepoints_mutex);
458 return;
459 }
460 if (!list_empty(&old_probes))
461 list_replace_init(&old_probes, &release_probes);
462 need_update = 0;
463 mutex_unlock(&tracepoints_mutex);
464
465 tracepoint_update_probes();
466 list_for_each_entry_safe(pos, next, &release_probes, u.list) {
467 list_del(&pos->u.list);
468 call_rcu_sched(&pos->u.rcu, rcu_free_old_probes);
469 }
470}
471EXPORT_SYMBOL_GPL(tracepoint_probe_update_all);
472
379/** 473/**
380 * tracepoint_get_iter_range - Get a next tracepoint iterator given a range. 474 * tracepoint_get_iter_range - Get a next tracepoint iterator given a range.
381 * @tracepoint: current tracepoints (in), next tracepoint (out) 475 * @tracepoint: current tracepoints (in), next tracepoint (out)