diff options
| author | Jesse Barnes <jbarnes@engr.sgi.com> | 2004-11-10 21:29:32 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-11-10 21:29:32 -0800 |
| commit | a370025d1ed83b2b8202c4fefe4ea465bf22f10a (patch) | |
| tree | c18274d669421dc47eaedfbeab4a113f92172426 /kernel/profile.c | |
| parent | 6998fe2529d33f5441dbfed6e924c990d052c167 (diff) | |
[PATCH] remove contention on profile_lock
profile_hook unconditionally takes a read lock on profile_lock if kernel
profiling is enabled. The lock protects the profile_hook notifier chain
from being written while it's being called. The routine profile_hook is
called in a very hot path though: every timer tick on every CPU. As you
can imagine, on a large system, this makes the cacheline containing
profile_lock pretty hot. Since oprofile was the only user of the
profile_hook, I removed the notifier chain altogether in favor of a simple
function pointer with the help of John Levon. This removes all of the
contention in the hot path since the variable is very seldom written and
simplifies things a little to boot.
Acked-by: John Levon <levon@movementarian.org>
Signed-off-by: Jesse Barnes <jbarnes@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/profile.c')
| -rw-r--r-- | kernel/profile.c | 45 |
1 files changed, 17 insertions, 28 deletions
diff --git a/kernel/profile.c b/kernel/profile.c index e7ff9b32d822..ff62fa98328a 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -34,6 +34,9 @@ struct profile_hit { #define NR_PROFILE_HIT (PAGE_SIZE/sizeof(struct profile_hit)) #define NR_PROFILE_GRP (NR_PROFILE_HIT/PROFILE_GRPSZ) +/* Oprofile timer tick hook */ +int (*timer_hook)(struct pt_regs *); + static atomic_t *prof_buffer; static unsigned long prof_len, prof_shift; static int prof_on; @@ -168,38 +171,24 @@ int profile_event_unregister(enum profile_type type, struct notifier_block * n) return err; } -static struct notifier_block * profile_listeners; -static rwlock_t profile_lock = RW_LOCK_UNLOCKED; - -int register_profile_notifier(struct notifier_block * nb) -{ - int err; - write_lock_irq(&profile_lock); - err = notifier_chain_register(&profile_listeners, nb); - write_unlock_irq(&profile_lock); - return err; -} - - -int unregister_profile_notifier(struct notifier_block * nb) +int register_timer_hook(int (*hook)(struct pt_regs *)) { - int err; - write_lock_irq(&profile_lock); - err = notifier_chain_unregister(&profile_listeners, nb); - write_unlock_irq(&profile_lock); - return err; + if (timer_hook) + return -EBUSY; + timer_hook = hook; + return 0; } - -void profile_hook(struct pt_regs * regs) +void unregister_timer_hook(int (*hook)(struct pt_regs *)) { - read_lock(&profile_lock); - notifier_call_chain(&profile_listeners, 0, regs); - read_unlock(&profile_lock); + WARN_ON(hook != timer_hook); + timer_hook = NULL; + /* make sure all CPUs see the NULL hook */ + synchronize_kernel(); } -EXPORT_SYMBOL_GPL(register_profile_notifier); -EXPORT_SYMBOL_GPL(unregister_profile_notifier); +EXPORT_SYMBOL_GPL(register_timer_hook); +EXPORT_SYMBOL_GPL(unregister_timer_hook); EXPORT_SYMBOL_GPL(task_handoff_register); EXPORT_SYMBOL_GPL(task_handoff_unregister); @@ -394,8 +383,8 @@ void profile_hit(int type, void *__pc) void profile_tick(int type, struct pt_regs *regs) { - if (type == CPU_PROFILING) - profile_hook(regs); + if (type == CPU_PROFILING && timer_hook) + timer_hook(regs); if (!user_mode(regs) && cpu_isset(smp_processor_id(), prof_cpu_mask)) profile_hit(type, (void *)profile_pc(regs)); } |
