From: John Levon Implements a simple notifier so oprofile can notice removed and added modules properly. This is needed to cope with: modprobe /lib/modules/`uname -r`/kernel/whatever/mydriver.ko rmmod mydriver modprobe supposedlyfasterversion/mydriver.ko The daemon will not know otherwise that these events have happened, and will attribute samples to the wrong place, thinking that samples in the range still belong to the first binary image. Same happens if you insert a different module. Requiring the user to restart the daemon on every module insertion is not an acceptable solution (how would you profile the startup of some code that inserted a kernel module ?). Additionally, it's a performance thing: without the notification, we would have to scan /proc/modules *every* time we get a kernel-side sample that doesn't fit into the vmlinux or a module address space. Finally, it brings it on par to the 2.4 oprofile code finally (except for some changed module things which we handle in userspace). It goes without saying that the /proc/modules scan is racy, but it is only information loss (if someone inserts and rmmods a module fast enough before oprofiled can catch up), and unavoidable, and unrelated to whether this patch goes in or not. 25-akpm/drivers/oprofile/buffer_sync.c | 24 ++++++++++++++++++++++++ 25-akpm/drivers/oprofile/event_buffer.h | 3 ++- 25-akpm/include/linux/module.h | 16 ++++++++++++++++ 25-akpm/kernel/module.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff -puN drivers/oprofile/buffer_sync.c~module_load_notification drivers/oprofile/buffer_sync.c --- 25/drivers/oprofile/buffer_sync.c~module_load_notification Mon Mar 31 16:09:07 2003 +++ 25-akpm/drivers/oprofile/buffer_sync.c Mon Mar 31 16:09:07 2003 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "oprofile_stats.h" @@ -67,6 +68,19 @@ static int mm_notify(struct notifier_blo } +static int module_load_notify(struct notifier_block * self, unsigned long val, void * data) +{ + if (val != MODULE_STATE_COMING) + return 0; + + sync_cpu_buffers(); + down(&buffer_sem); + add_event_entry(ESCAPE_CODE); + add_event_entry(MODULE_LOADED_CODE); + up(&buffer_sem); + return 0; +} + static struct notifier_block exit_task_nb = { .notifier_call = exit_task_notify, }; @@ -79,6 +93,10 @@ static struct notifier_block exit_mmap_n .notifier_call = mm_notify, }; +static struct notifier_block module_load_nb = { + .notifier_call = module_load_notify, +}; + int sync_start(void) { @@ -98,9 +116,14 @@ int sync_start(void) err = profile_event_register(EXEC_UNMAP, &exec_unmap_nb); if (err) goto out3; + err = register_module_notifier(&module_load_nb); + if (err) + goto out4; out: return err; +out4: + profile_event_unregister(EXEC_UNMAP, &exec_unmap_nb); out3: profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); out2: @@ -113,6 +136,7 @@ out1: void sync_stop(void) { + unregister_module_notifier(&module_load_nb); profile_event_unregister(EXIT_TASK, &exit_task_nb); profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); profile_event_unregister(EXEC_UNMAP, &exec_unmap_nb); diff -puN drivers/oprofile/event_buffer.h~module_load_notification drivers/oprofile/event_buffer.h --- 25/drivers/oprofile/event_buffer.h~module_load_notification Mon Mar 31 16:09:07 2003 +++ 25-akpm/drivers/oprofile/event_buffer.h Mon Mar 31 16:09:07 2003 @@ -24,12 +24,13 @@ void wake_up_buffer_waiter(void); * then one of the following codes, then the * relevant data. */ -#define ESCAPE_CODE ~0UL +#define ESCAPE_CODE ~0UL #define CTX_SWITCH_CODE 1 #define CPU_SWITCH_CODE 2 #define COOKIE_SWITCH_CODE 3 #define KERNEL_ENTER_SWITCH_CODE 4 #define KERNEL_EXIT_SWITCH_CODE 5 +#define MODULE_LOADED_CODE 6 /* add data to the event buffer */ void add_event_entry(unsigned long data); diff -puN include/linux/module.h~module_load_notification include/linux/module.h --- 25/include/linux/module.h~module_load_notification Mon Mar 31 16:09:07 2003 +++ 25-akpm/include/linux/module.h Mon Mar 31 16:09:07 2003 @@ -138,6 +138,7 @@ struct exception_table const struct exception_table_entry *entry; }; +struct notifier_block; #ifdef CONFIG_MODULES @@ -348,6 +349,9 @@ const char *module_address_lookup(unsign /* For extable.c to search modules' exception tables. */ const struct exception_table_entry *search_module_extables(unsigned long addr); +int register_module_notifier(struct notifier_block * nb); +int unregister_module_notifier(struct notifier_block * nb); + #else /* !CONFIG_MODULES... */ #define EXPORT_SYMBOL(sym) #define EXPORT_SYMBOL_GPL(sym) @@ -392,6 +396,18 @@ static inline const char *module_address { return NULL; } + +static inline int register_module_notifier(struct notifier_block * nb) +{ + /* no events will happen anyway, so this can always succeed */ + return 0; +} + +static inline int unregister_module_notifier(struct notifier_block * nb) +{ + return 0; +} + #endif /* CONFIG_MODULES */ #ifdef MODULE diff -puN kernel/module.c~module_load_notification kernel/module.c --- 25/kernel/module.c~module_load_notification Mon Mar 31 16:09:07 2003 +++ 25-akpm/kernel/module.c Mon Mar 31 16:09:07 2003 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,29 @@ static LIST_HEAD(modules); static LIST_HEAD(symbols); static LIST_HEAD(extables); +static DECLARE_MUTEX(notify_mutex); +static struct notifier_block * module_notify_list; + +int register_module_notifier(struct notifier_block * nb) +{ + int err; + down(¬ify_mutex); + err = notifier_chain_register(&module_notify_list, nb); + up(¬ify_mutex); + return err; +} +EXPORT_SYMBOL(register_module_notifier); + +int unregister_module_notifier(struct notifier_block * nb) +{ + int err; + down(¬ify_mutex); + err = notifier_chain_unregister(&module_notify_list, nb); + up(¬ify_mutex); + return err; +} +EXPORT_SYMBOL(unregister_module_notifier); + /* We require a truly strong try_module_get() */ static inline int strong_try_module_get(struct module *mod) { @@ -1368,6 +1392,10 @@ sys_init_module(void *umod, /* Drop lock so they can recurse */ up(&module_mutex); + down(¬ify_mutex); + notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); + up(¬ify_mutex); + /* Start the module */ ret = mod->init(); if (ret < 0) { _