Re: contention on profile_lock

From: Jesse Barnes
Date: Thu Nov 04 2004 - 17:13:49 EST


On Thursday, November 4, 2004 12:49 pm, Jesse Barnes wrote:
> John pointed out that this breaks modules. Would registering and
> unregistering a function pointer thus be module safe? Dipankar, hopefully
> you have something better?
>
> static int timer_start(void)
> {
> /* Setup the callback pointer */
> oprofile_timer_notify = oprofile_timer;
> return 0;
> }
>
>
> static void timer_stop(void)
> {
> /* Tear down the callback pointer after sync_kernel */
> synchronize_kernel();
> oprofile_timer_notify = NULL;
> }

Ok, here's another one that uses this approach. Hopefully it satisfies all
parties? (Compile tested only so far.)

Thanks,
Jesse
===== drivers/oprofile/timer_int.c 1.8 vs edited =====
--- 1.8/drivers/oprofile/timer_int.c 2004-09-03 16:55:27 -07:00
+++ edited/drivers/oprofile/timer_int.c 2004-11-04 13:54:13 -08:00
@@ -13,33 +13,31 @@
#include <linux/oprofile.h>
#include <linux/profile.h>
#include <linux/init.h>
+#include <linux/rcupdate.h>
#include <asm/ptrace.h>

-static int timer_notify(struct notifier_block * self, unsigned long val, void * data)
+static int timer_notify(struct pt_regs *regs)
{
- struct pt_regs * regs = (struct pt_regs *)data;
int cpu = smp_processor_id();
unsigned long eip = profile_pc(regs);

oprofile_add_sample(eip, !user_mode(regs), 0, cpu);
return 0;
}
-
-
-static struct notifier_block timer_notifier = {
- .notifier_call = timer_notify,
-};
-

static int timer_start(void)
{
- return register_profile_notifier(&timer_notifier);
+ /* Setup the callback pointer */
+ oprofile_timer_notify = timer_notify;
+ return 0;
}


static void timer_stop(void)
{
- unregister_profile_notifier(&timer_notifier);
+ /* Tear down the callback pointer after sync_kernel */
+ synchronize_kernel();
+ oprofile_timer_notify = NULL;
}


===== include/linux/oprofile.h 1.10 vs edited =====
--- 1.10/include/linux/oprofile.h 2004-06-24 01:56:02 -07:00
+++ edited/include/linux/oprofile.h 2004-11-04 13:54:04 -08:00
@@ -105,5 +105,8 @@

/** lock for read/write safety */
extern spinlock_t oprofilefs_lock;
+
+/* Timer based profiling hook */
+extern int (*oprofile_timer_notify)(struct pt_regs *);

#endif /* OPROFILE_H */
===== kernel/profile.c 1.14 vs edited =====
--- 1.14/kernel/profile.c 2004-10-19 02:40:31 -07:00
+++ edited/kernel/profile.c 2004-11-04 13:50:36 -08:00
@@ -22,6 +22,7 @@
#include <linux/cpumask.h>
#include <linux/cpu.h>
#include <linux/profile.h>
+#include <linux/oprofile.h>
#include <linux/highmem.h>
#include <asm/sections.h>
#include <asm/semaphore.h>
@@ -34,6 +35,9 @@
#define NR_PROFILE_HIT (PAGE_SIZE/sizeof(struct profile_hit))
#define NR_PROFILE_GRP (NR_PROFILE_HIT/PROFILE_GRPSZ)

+/* Oprofile timer tick hook */
+int (*oprofile_timer_notify)(struct pt_regs *);
+
static atomic_t *prof_buffer;
static unsigned long prof_len, prof_shift;
static int prof_on;
@@ -168,38 +172,6 @@
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 err;
- write_lock_irq(&profile_lock);
- err = notifier_chain_unregister(&profile_listeners, nb);
- write_unlock_irq(&profile_lock);
- return err;
-}
-
-
-void profile_hook(struct pt_regs * regs)
-{
- read_lock(&profile_lock);
- notifier_call_chain(&profile_listeners, 0, regs);
- read_unlock(&profile_lock);
-}
-
-EXPORT_SYMBOL_GPL(register_profile_notifier);
-EXPORT_SYMBOL_GPL(unregister_profile_notifier);
EXPORT_SYMBOL_GPL(task_handoff_register);
EXPORT_SYMBOL_GPL(task_handoff_unregister);

@@ -394,8 +366,8 @@

void profile_tick(int type, struct pt_regs *regs)
{
- if (type == CPU_PROFILING)
- profile_hook(regs);
+ if (type == CPU_PROFILING && oprofile_timer_notify)
+ oprofile_timer_notify(regs);
if (!user_mode(regs) && cpu_isset(smp_processor_id(), prof_cpu_mask))
profile_hit(type, (void *)profile_pc(regs));
}