summaryrefslogtreecommitdiff
path: root/kernel/profile.c
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@engr.sgi.com>2004-11-10 21:29:32 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-11-10 21:29:32 -0800
commita370025d1ed83b2b8202c4fefe4ea465bf22f10a (patch)
treec18274d669421dc47eaedfbeab4a113f92172426 /kernel/profile.c
parent6998fe2529d33f5441dbfed6e924c990d052c167 (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.c45
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));
}