diff options
Diffstat (limited to 'kernel/tracepoint.c')
-rw-r--r-- | kernel/tracepoint.c | 170 |
1 files changed, 132 insertions, 38 deletions
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 | ||
61 | struct tp_probes { | 61 | struct 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 | ||
73 | static void rcu_free_old_probes(struct rcu_head *head) | 76 | static 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 | ||
78 | static inline void release_probes(void *old) | 81 | static 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 | ||
305 | static 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 | */ |
310 | int tracepoint_probe_register(const char *name, void *probe) | 330 | int 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; |
336 | end: | ||
337 | mutex_unlock(&tracepoints_mutex); | ||
338 | return ret; | ||
339 | } | 343 | } |
340 | EXPORT_SYMBOL_GPL(tracepoint_probe_register); | 344 | EXPORT_SYMBOL_GPL(tracepoint_probe_register); |
341 | 345 | ||
346 | static 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 | */ |
352 | int tracepoint_probe_unregister(const char *name, void *probe) | 372 | int 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; |
373 | end: | ||
374 | mutex_unlock(&tracepoints_mutex); | ||
375 | return ret; | ||
376 | } | 385 | } |
377 | EXPORT_SYMBOL_GPL(tracepoint_probe_unregister); | 386 | EXPORT_SYMBOL_GPL(tracepoint_probe_unregister); |
378 | 387 | ||
388 | static LIST_HEAD(old_probes); | ||
389 | static int need_update; | ||
390 | |||
391 | static 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 | */ | ||
408 | int 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 | } | ||
422 | EXPORT_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 | */ | ||
431 | int 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 | } | ||
445 | EXPORT_SYMBOL_GPL(tracepoint_probe_unregister_noupdate); | ||
446 | |||
447 | /** | ||
448 | * tracepoint_probe_update_all - update tracepoints | ||
449 | */ | ||
450 | void 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 | } | ||
471 | EXPORT_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) |