diff options
author | Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> | 2008-10-01 12:03:25 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-10-14 04:38:38 -0400 |
commit | ed86a59071a4ccd0e9bcefc61e013759a21eda78 (patch) | |
tree | 013abe0cb9e3cd4d7e6c2a9655f86517423a2623 /kernel/marker.c | |
parent | 5b9261d93e5fa3db4995d5b77b5ed365166e001c (diff) |
markers: re-enable fast batch registration
Lai Jiangshan discovered a reentrancy issue with markers and fixed it by
adding synchronize_sched() calls at each registration/unregistraiton.
It works, but it removes the ability to do batch
registration/unregistration and can cause registration of ~100 markers
to take about 30 seconds on a loaded machine (synchronize_sched() is
much slower on such workloads).
This patch implements a version of the fix which won't slow down marker batch
registration/unregistration. It also go back to the original non-synchronized
reg/unreg.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/marker.c')
-rw-r--r-- | kernel/marker.c | 58 |
1 files changed, 52 insertions, 6 deletions
diff --git a/kernel/marker.c b/kernel/marker.c index fe5ca72f9659..05a25776f71f 100644 --- a/kernel/marker.c +++ b/kernel/marker.c | |||
@@ -60,6 +60,9 @@ struct marker_entry { | |||
60 | struct marker_probe_closure single; | 60 | struct marker_probe_closure single; |
61 | struct marker_probe_closure *multi; | 61 | struct marker_probe_closure *multi; |
62 | int refcount; /* Number of times armed. 0 if disarmed. */ | 62 | int refcount; /* Number of times armed. 0 if disarmed. */ |
63 | struct rcu_head rcu; | ||
64 | void *oldptr; | ||
65 | unsigned char rcu_pending:1; | ||
63 | unsigned char ptype:1; | 66 | unsigned char ptype:1; |
64 | char name[0]; /* Contains name'\0'format'\0' */ | 67 | char name[0]; /* Contains name'\0'format'\0' */ |
65 | }; | 68 | }; |
@@ -196,6 +199,16 @@ void marker_probe_cb_noarg(const struct marker *mdata, void *call_private, ...) | |||
196 | } | 199 | } |
197 | EXPORT_SYMBOL_GPL(marker_probe_cb_noarg); | 200 | EXPORT_SYMBOL_GPL(marker_probe_cb_noarg); |
198 | 201 | ||
202 | static void free_old_closure(struct rcu_head *head) | ||
203 | { | ||
204 | struct marker_entry *entry = container_of(head, | ||
205 | struct marker_entry, rcu); | ||
206 | kfree(entry->oldptr); | ||
207 | /* Make sure we free the data before setting the pending flag to 0 */ | ||
208 | smp_wmb(); | ||
209 | entry->rcu_pending = 0; | ||
210 | } | ||
211 | |||
199 | static void debug_print_probes(struct marker_entry *entry) | 212 | static void debug_print_probes(struct marker_entry *entry) |
200 | { | 213 | { |
201 | int i; | 214 | int i; |
@@ -404,6 +417,7 @@ static struct marker_entry *add_marker(const char *name, const char *format) | |||
404 | e->multi = NULL; | 417 | e->multi = NULL; |
405 | e->ptype = 0; | 418 | e->ptype = 0; |
406 | e->refcount = 0; | 419 | e->refcount = 0; |
420 | e->rcu_pending = 0; | ||
407 | hlist_add_head(&e->hlist, head); | 421 | hlist_add_head(&e->hlist, head); |
408 | return e; | 422 | return e; |
409 | } | 423 | } |
@@ -433,6 +447,9 @@ static int remove_marker(const char *name) | |||
433 | if (e->single.func != __mark_empty_function) | 447 | if (e->single.func != __mark_empty_function) |
434 | return -EBUSY; | 448 | return -EBUSY; |
435 | hlist_del(&e->hlist); | 449 | hlist_del(&e->hlist); |
450 | /* Make sure the call_rcu has been executed */ | ||
451 | if (e->rcu_pending) | ||
452 | rcu_barrier_sched(); | ||
436 | kfree(e); | 453 | kfree(e); |
437 | return 0; | 454 | return 0; |
438 | } | 455 | } |
@@ -462,8 +479,12 @@ static int marker_set_format(struct marker_entry **entry, const char *format) | |||
462 | e->multi = (*entry)->multi; | 479 | e->multi = (*entry)->multi; |
463 | e->ptype = (*entry)->ptype; | 480 | e->ptype = (*entry)->ptype; |
464 | e->refcount = (*entry)->refcount; | 481 | e->refcount = (*entry)->refcount; |
482 | e->rcu_pending = 0; | ||
465 | hlist_add_before(&e->hlist, &(*entry)->hlist); | 483 | hlist_add_before(&e->hlist, &(*entry)->hlist); |
466 | hlist_del(&(*entry)->hlist); | 484 | hlist_del(&(*entry)->hlist); |
485 | /* Make sure the call_rcu has been executed */ | ||
486 | if ((*entry)->rcu_pending) | ||
487 | rcu_barrier_sched(); | ||
467 | kfree(*entry); | 488 | kfree(*entry); |
468 | *entry = e; | 489 | *entry = e; |
469 | trace_mark(core_marker_format, "name %s format %s", | 490 | trace_mark(core_marker_format, "name %s format %s", |
@@ -637,6 +658,12 @@ int marker_probe_register(const char *name, const char *format, | |||
637 | goto end; | 658 | goto end; |
638 | } | 659 | } |
639 | } | 660 | } |
661 | /* | ||
662 | * If we detect that a call_rcu is pending for this marker, | ||
663 | * make sure it's executed now. | ||
664 | */ | ||
665 | if (entry->rcu_pending) | ||
666 | rcu_barrier_sched(); | ||
640 | old = marker_entry_add_probe(entry, probe, probe_private); | 667 | old = marker_entry_add_probe(entry, probe, probe_private); |
641 | if (IS_ERR(old)) { | 668 | if (IS_ERR(old)) { |
642 | ret = PTR_ERR(old); | 669 | ret = PTR_ERR(old); |
@@ -644,11 +671,16 @@ int marker_probe_register(const char *name, const char *format, | |||
644 | } | 671 | } |
645 | mutex_unlock(&markers_mutex); | 672 | mutex_unlock(&markers_mutex); |
646 | marker_update_probes(); /* may update entry */ | 673 | marker_update_probes(); /* may update entry */ |
647 | synchronize_sched(); | ||
648 | kfree(old); | ||
649 | mutex_lock(&markers_mutex); | 674 | mutex_lock(&markers_mutex); |
650 | entry = get_marker(name); | 675 | entry = get_marker(name); |
651 | WARN_ON(!entry); | 676 | WARN_ON(!entry); |
677 | if (entry->rcu_pending) | ||
678 | rcu_barrier_sched(); | ||
679 | entry->oldptr = old; | ||
680 | entry->rcu_pending = 1; | ||
681 | /* write rcu_pending before calling the RCU callback */ | ||
682 | smp_wmb(); | ||
683 | call_rcu_sched(&entry->rcu, free_old_closure); | ||
652 | end: | 684 | end: |
653 | mutex_unlock(&markers_mutex); | 685 | mutex_unlock(&markers_mutex); |
654 | return ret; | 686 | return ret; |
@@ -678,15 +710,22 @@ int marker_probe_unregister(const char *name, | |||
678 | entry = get_marker(name); | 710 | entry = get_marker(name); |
679 | if (!entry) | 711 | if (!entry) |
680 | goto end; | 712 | goto end; |
713 | if (entry->rcu_pending) | ||
714 | rcu_barrier_sched(); | ||
681 | old = marker_entry_remove_probe(entry, probe, probe_private); | 715 | old = marker_entry_remove_probe(entry, probe, probe_private); |
682 | mutex_unlock(&markers_mutex); | 716 | mutex_unlock(&markers_mutex); |
683 | marker_update_probes(); /* may update entry */ | 717 | marker_update_probes(); /* may update entry */ |
684 | synchronize_sched(); | ||
685 | kfree(old); | ||
686 | mutex_lock(&markers_mutex); | 718 | mutex_lock(&markers_mutex); |
687 | entry = get_marker(name); | 719 | entry = get_marker(name); |
688 | if (!entry) | 720 | if (!entry) |
689 | goto end; | 721 | goto end; |
722 | if (entry->rcu_pending) | ||
723 | rcu_barrier_sched(); | ||
724 | entry->oldptr = old; | ||
725 | entry->rcu_pending = 1; | ||
726 | /* write rcu_pending before calling the RCU callback */ | ||
727 | smp_wmb(); | ||
728 | call_rcu_sched(&entry->rcu, free_old_closure); | ||
690 | remove_marker(name); /* Ignore busy error message */ | 729 | remove_marker(name); /* Ignore busy error message */ |
691 | ret = 0; | 730 | ret = 0; |
692 | end: | 731 | end: |
@@ -752,14 +791,21 @@ int marker_probe_unregister_private_data(marker_probe_func *probe, | |||
752 | ret = -ENOENT; | 791 | ret = -ENOENT; |
753 | goto end; | 792 | goto end; |
754 | } | 793 | } |
794 | if (entry->rcu_pending) | ||
795 | rcu_barrier_sched(); | ||
755 | old = marker_entry_remove_probe(entry, NULL, probe_private); | 796 | old = marker_entry_remove_probe(entry, NULL, probe_private); |
756 | mutex_unlock(&markers_mutex); | 797 | mutex_unlock(&markers_mutex); |
757 | marker_update_probes(); /* may update entry */ | 798 | marker_update_probes(); /* may update entry */ |
758 | synchronize_sched(); | ||
759 | kfree(old); | ||
760 | mutex_lock(&markers_mutex); | 799 | mutex_lock(&markers_mutex); |
761 | entry = get_marker_from_private_data(probe, probe_private); | 800 | entry = get_marker_from_private_data(probe, probe_private); |
762 | WARN_ON(!entry); | 801 | WARN_ON(!entry); |
802 | if (entry->rcu_pending) | ||
803 | rcu_barrier_sched(); | ||
804 | entry->oldptr = old; | ||
805 | entry->rcu_pending = 1; | ||
806 | /* write rcu_pending before calling the RCU callback */ | ||
807 | smp_wmb(); | ||
808 | call_rcu_sched(&entry->rcu, free_old_closure); | ||
763 | remove_marker(entry->name); /* Ignore busy error message */ | 809 | remove_marker(entry->name); /* Ignore busy error message */ |
764 | end: | 810 | end: |
765 | mutex_unlock(&markers_mutex); | 811 | mutex_unlock(&markers_mutex); |