OProfile update
Subsystems (2)
PATCH 1/8 OProfile update
The next 8 patches change the following files :
arch/alpha/oprofile/Makefile | 3 + arch/alpha/oprofile/common.c | 2 - arch/i386/oprofile/Makefile | 5 +- arch/i386/oprofile/init.c | 11 ++---- arch/i386/oprofile/nmi_int.c | 14 +++---- arch/i386/oprofile/timer_int.c | 58 -------------------------------- arch/parisc/oprofile/Makefile | 5 +- arch/parisc/oprofile/init.c | 3 - arch/parisc/oprofile/timer_int.c | 58 -------------------------------- arch/ppc64/oprofile/Makefile | 5 +- arch/ppc64/oprofile/init.c | 3 - arch/ppc64/oprofile/timer_int.c | 59 --------------------------------- arch/sparc64/oprofile/Makefile | 5 +- arch/sparc64/oprofile/init.c | 3 - arch/sparc64/oprofile/timer_int.c | 59 --------------------------------- arch/x86_64/oprofile/Makefile | 9 ++--- drivers/oprofile/buffer_sync.c | 67 +++++++++++++++++++++++++------------- drivers/oprofile/event_buffer.c | 6 ++- drivers/oprofile/oprof.c | 23 +++++++++---- drivers/oprofile/oprofile_stats.c | 6 +-- drivers/oprofile/oprofile_stats.h | 2 - drivers/oprofile/timer_int.c | 56 +++++++++++++++++++++++++++++++ 22 files changed, 159 insertions(+), 303 deletions(-)
Convention is that error returns are negative.
arch/alpha/oprofile/common.c +1 -1
@@ -175,7 +175,7 @@ }if (!lmodel) -return ENODEV ; +return -ENODEV ; model = lmodel; oprof_axp_ops.cpu_type = lmodel->cpu_type;
PATCH 2/8 OProfile update
Consolidate all the arch copies of timer_int.c into one place. Also fixes a problem with PA-RISC not using instruction_pointer() when it should.
arch/alpha/oprofile/Makefile +2 -1
@@ -5,7 +5,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprof.o cpu_buffer.o buffer_sync.o \ event_buffer.o oprofile_files.o \ - oprofilefs.o oprofile_stats.o ) + oprofilefs.o oprofile_stats.o \ + timer_int.o ) oprofile-y := $(DRIVER_OBJS) common.o oprofile-$(CONFIG_ALPHA_GENERIC) += op_model_ev4.o \
arch/i386/oprofile/init.c +4 -7
@@ -11,22 +11,19 @@#include <linux/init.h> /* We support CPUs that have performance counters like the Pentium Pro -* with NMI mode samples. Other x86 CPUs use a simple interrupt keyed -* off the timer interrupt, which cannot profile interrupts-disabled -* code unlike the NMI-based code. +* with the NMI mode driver. */ extern int nmi_init (struct oprofile_operations ** ops);extern void nmi_exit (void ); -extern void timer_init (struct oprofile_operations ** ops);int __init oprofile_arch_init (struct oprofile_operations ** ops) {#ifdef CONFIG_X86_LOCAL_APIC - if (!nmi_init(ops)) +return nmi_init (ops); +#else +return -ENODEV ;#endif -timer_init (ops); -return 0 ; }
arch/i386/oprofile/Makefile +3 -2
@@ -3,8 +3,9 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprof.o cpu_buffer.o buffer_sync.o \ event_buffer.o oprofile_files.o \ - oprofilefs.o oprofile_stats.o ) + oprofilefs.o oprofile_stats.o \ + timer_int.o ) -oprofile-y := $(DRIVER_OBJS) init.o timer_int.o +oprofile-y := $(DRIVER_OBJS) init.o oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o \ op_model_ppro.o op_model_p4.o
arch/i386/oprofile/nmi_int.c +7 -7
@@ -314,13 +314,13 @@__u8 family = current_cpu_data.x86;if (!cpu_has_apic) -return 0 ; +return -ENODEV ;switch (vendor) {case X86_VENDOR_AMD :/* Needs to be at least an Athlon (or hammer in 32bit mode) */ if (family <6 ) -return 0 ; +return -ENODEV ; model = &op_athlon_spec; nmi_ops.cpu_type ="i386/athlon" ;break ;
@@ -331,30 +331,30 @@/* Pentium IV */ case 0xf :if (!p4_init ()) -return 0 ; +return -ENODEV ;break ;/* A P6-class processor */ case 6 :if (!ppro_init ()) -return 0 ; +return -ENODEV ;break ;default : - return 0; + return -ENODEV; }break ;#endif /* !CONFIG_X86_64 */ default : -return 0 ; +return -ENODEV ; }init_driverfs (); using_nmi =1 ; *ops = &nmi_ops;printk (KERN_INFO "oprofile: using NMI interrupt.\n" ); -return 1 ; +return 0 ; }
arch/i386/oprofile/timer_int.c +0 -58
58 lines not on this page · raw message
arch/parisc/oprofile/init.c +1 -2
@@ -15,8 +15,7 @@int __init oprofile_arch_init (struct oprofile_operations ** ops) { -timer_init (ops); -return 0 ; +return -ENODEV ; }
arch/parisc/oprofile/Makefile +3 -2
@@ -3,6 +3,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprof.o cpu_buffer.o buffer_sync.o \ event_buffer.o oprofile_files.o \ - oprofilefs.o oprofile_stats.o ) + oprofilefs.o oprofile_stats.o \ + timer_int.o ) -oprofile-y := $(DRIVER_OBJS) init.o timer_int.o +oprofile-y := $(DRIVER_OBJS) init.o
arch/parisc/oprofile/timer_int.c +0 -58
58 lines not on this page · raw message
arch/ppc64/oprofile/init.c +1 -2
@@ -15,8 +15,7 @@int __init oprofile_arch_init (struct oprofile_operations ** ops) { -timer_init (ops); -return 0 ; +return -ENODEV ; }
arch/ppc64/oprofile/Makefile +3 -2
@@ -3,6 +3,7 @@ DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \ oprof.o cpu_buffer.o buffer_sync.o \ event_buffer.o oprofile_files.o \ - oprofilefs.o oprofile_stats.o ) + oprofilefs.o oprofile_stats.o \ + timer_int.o ) -oprofile-y := $(DRIVER_OBJS) init.o timer_int.o +oprofile-y := $(DRIVER_OBJS) init.o
arch/ppc64/oprofile/timer_int.c +0 -59
59 lines not on this page · raw message
arch/sparc64/oprofile/init.c +1 -2
@@ -15,8 +15,7 @@int __init oprofile_arch_init (struct oprofile_operations ** ops) { -timer_init (ops); -return 0 ; +return -ENODEV ; }
arch/sparc64/oprofile/Makefile +3 -2
@@ -3,6 +3,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprof.o cpu_buffer.o buffer_sync.o \ event_buffer.o oprofile_files.o \ - oprofilefs.o oprofile_stats.o ) + oprofilefs.o oprofile_stats.o \ + timer_int.o ) -oprofile-y := $(DRIVER_OBJS) init.o timer_int.o +oprofile-y := $(DRIVER_OBJS) init.o
arch/sparc64/oprofile/timer_int.c +0 -59
59 lines not on this page · raw message
arch/x86_64/oprofile/Makefile +4 -5
@@ -9,9 +9,10 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprof.o cpu_buffer.o buffer_sync.o \ event_buffer.o oprofile_files.o \ - oprofilefs.o oprofile_stats.o ) + oprofilefs.o oprofile_stats.o \ + timer_int.o ) -oprofile-objs := $(DRIVER_OBJS) init.o timer_int.o +oprofile-objs := $(DRIVER_OBJS) init.o oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o
@@ -23,11 +24,9 @@ @ln -sf ../../i386/oprofile/op_model_athlon.c $(obj)/op_model_athlon.c $(obj)/init.c: ${INCL} @ln -sf ../../i386/oprofile/init.c $(obj)/init.c -$(obj)/timer_int.c: ${INCL} - @ln -sf ../../i386/oprofile/timer_int.c $(obj)/timer_int.c $(obj)/op_counter.h: @ln -sf ../../i386/oprofile/op_counter.h $(obj)/op_counter.h $(obj)/op_x86_model.h: @ln -sf ../../i386/oprofile/op_x86_model.h $(obj)/op_x86_model.h -clean-files += op_x86_model.h op_counter.h timer_int.c init.c \ +clean-files += op_x86_model.h op_counter.h init.c \ op_model_athlon.c nmi_int.c
drivers/oprofile/timer_int.c +56 -0
56 lines not on this page · raw message
drivers/oprofile/oprof.c +10 -1
@@ -119,14 +119,23 @@ } +extern void timer_init (struct oprofile_operations ** ops); + +static int __init oprofile_init (void ) {int err;/* Architecture must fill in the interrupt ops and the -* logical CPU type. +* logical CPU type, or we can fall back to the timer +* interrupt profiler. */ err =oprofile_arch_init (&oprofile_ops); +if (err == -ENODEV ) { +timer_init (&oprofile_ops); + err =0 ; + } +if (err) goto out;
PATCH 3/8 OProfile update
Clear up the code around start_sem so it's more obvious, and remove a pointless down/up pair on buffer_sem (shutdown is already synchronised in sync_stop()).
drivers/oprofile/oprof.c +7 -7
@@ -28,6 +28,8 @@ {int err; +down (&start_sem); +if ((err =alloc_cpu_buffers ())) goto out;
@@ -45,7 +47,6 @@if ((err =sync_start ())) goto out3; -down (&start_sem); is_setup =1 ;up (&start_sem);return 0 ;
@@ -58,6 +59,7 @@ out1:free_cpu_buffers (); out: +up (&start_sem);return err; }
@@ -106,22 +108,20 @@void oprofile_shutdown (void ) { +down (&start_sem);sync_stop ();if (oprofile_ops->shutdown) oprofile_ops->shutdown (); -/* down() is also necessary to synchronise all pending events -* before freeing */ -down (&buffer_sem); is_setup =0 ; -up (&buffer_sem);free_event_buffer ();free_cpu_buffers (); +up (&start_sem); } - +extern void timer_init (struct oprofile_operations ** ops); - +static int __init oprofile_init (void ) {int err;
PATCH 4/8 OProfile update
If there's are multiple tasks sleeping inside the read routine ever, this is necessary.
drivers/oprofile/event_buffer.c +5 -1
@@ -151,11 +151,15 @@if (count != max || *offset)return -EINVAL ; -/* wait for the event buffer to fill up with some data */ wait_event_interruptible (buffer_wait,atomic_read (&buffer_ready)); +if (signal_pending (current))return -EINTR ; +/* can't currently happen */ +if (!atomic_read (&buffer_ready)) +return -EAGAIN ; +down (&buffer_sem);atomic_set (&buffer_ready,0 );
PATCH 5/8 OProfile update
Now we don't do buffer syncs whilst holding current's mmap_sem ever, we can wait to hold it. Also take task_lock and try to improve the docs a bit.
drivers/oprofile/buffer_sync.c +15 -19
@@ -310,26 +337,23 @@/* Take the task's mmap_sem to protect ourselves from * races when we do lookup_dcookie(). */ -static struct mm_struct *take_task_mm (struct task_struct * task) +static struct mm_struct *take_tasks_mm (struct task_struct * task) { -struct mm_struct * mm = task->mm; - -/* if task->mm !NULL, mm_count must be at least 1. It cannot -* drop to 0 without the task exiting, which will have to sleep -* on buffer_sem first. So we do not need to mark mm_count -* ourselves. +struct mm_struct * mm; + +/* Subtle. We don't need to keep a reference to this task's mm, +* because, for the mm to be freed on another CPU, that would have +* to go through the task exit notifier, which ends up sleeping +* on the buffer_sem we hold, so we end up with mutual exclusion +* anyway. */ +task_lock (task); + mm = task->mm; +task_unlock (task); +if (mm) { -/* More ugliness. If a task took its mmap -* sem then came to sleep on buffer_sem we -* will deadlock waiting for it. So we can -* but try. This will lose samples :/ -*/ -if (!down_read_trylock (&mm->mmap_sem)) { -/* FIXME: this underestimates samples lost */ -atomic_inc (&oprofile_stats.sample_lost_mmap_sem); - mm =NULL ; - } +/* needed to walk the task's VMAs */ +down_read (&mm->mmap_sem); }return mm;
@@ -399,7 +423,7 @@ new = (struct task_struct *)s->event;release_mm (mm); - mm =take_task_mm (new); + mm =take_tasks_mm (new); cookie =get_exec_dcookie (mm);add_user_ctx_switch (new->pid, cookie);
@@ -460,4 +484,3 @@schedule_work (&sync_wq);/* timer is re-added by the scheduled task */ } -
PATCH 6/8 OProfile update
We were doing del_timer_sync() on shutdown, but not ensuring that any queued work was done as well.
drivers/oprofile/buffer_sync.c +2 -0
@@ -147,6 +170,8 @@profile_event_unregister (EXIT_MMAP , &exit_mmap_nb);profile_event_unregister (EXEC_UNMAP , &exec_unmap_nb);del_timer_sync (&sync_timer); +/* timer might have queued work, make sure it's completed. */ +flush_scheduled_work (); }
PATCH 7/8 OProfile update
Change the lost_mmap_sem stat to lost_no_mm, and account it.
drivers/oprofile/buffer_sync.c +2 -0
@@ -296,6 +321,8 @@add_sample_entry (s->eip, s->event); }else if (mm) {add_us_sample (mm, s); + }else { +atomic_inc (&oprofile_stats.sample_lost_no_mm); } }
drivers/oprofile/oprofile_stats.c +3 -3
@@ -31,7 +31,7 @@ cpu_buf->sample_lost_task_exit =0 ; } -atomic_set (&oprofile_stats.sample_lost_mmap_sem,0 ); +atomic_set (&oprofile_stats.sample_lost_no_mm,0 );atomic_set (&oprofile_stats.event_lost_overflow,0 ); }
@@ -68,8 +68,8 @@ &cpu_buf->sample_lost_task_exit); } -oprofilefs_create_ro_atomic (sb, dir,"sample_lost_mmap_sem" , - &oprofile_stats.sample_lost_mmap_sem); +oprofilefs_create_ro_atomic (sb, dir,"sample_lost_no_mm" , + &oprofile_stats.sample_lost_no_mm);oprofilefs_create_ro_atomic (sb, dir,"event_lost_overflow" , &oprofile_stats.event_lost_overflow); }
drivers/oprofile/oprofile_stats.h +1 -1
@@ -13,7 +13,7 @@#include <asm/atomic.h> struct oprofile_stat_struct { -atomic_t sample_lost_mmap_sem; +atomic_t sample_lost_no_mm;atomic_t event_lost_overflow; };
PATCH 8/8 OProfile update
Schedule work away on an unmap, instead of calling it directly. Should result in less lost samples, and it fixes a lock ordering problem buffer_sem <-> mmap_sem
drivers/oprofile/buffer_sync.c +26 -3
@@ -58,8 +58,8 @@* must concern ourselves with. First, when a task is about to * exit (exit_mmap()), we should process the buffer to deal with * any samples in the CPU buffer, before we lose the ->mmap information -* we need. Second, a task may unmap (part of) an executable mmap, -* so we want to process samples before that happens too +* we need. It is vital to get this case correct, otherwise we can +* end up trying to access a freed task_struct. */ static int mm_notify (struct notifier_block * self,unsigned long val,void * data) {
@@ -67,6 +67,29 @@return 0 ; } + +/* Second, a task may unmap (part of) an executable mmap, +* so we want to process samples before that happens too. This is merely +* a QOI issue not a correctness one. +*/ +static int munmap_notify (struct notifier_block * self,unsigned long val,void * data) +{ +/* Note that we cannot sync the buffers directly, because we might end up +* taking the the mmap_sem that we hold now inside of event_buffer_read() +* on a page fault, whilst holding buffer_sem - deadlock. +* +* This would mean a threaded reader of the event buffer, but we should +* prevent it anyway. +* +* Delaying the work in a context that doesn't hold the mmap_sem means +* that we won't lose samples from other mappings that current() may +* have. Note that either way, we lose any pending samples for what is +* being unmapped. +*/ +schedule_work (&sync_wq); +return 0 ; +} +/* We need to be told about new modules so we don't attribute to a previously * loaded module, or drop the samples on the floor.
@@ -92,7 +115,7 @@ };static struct notifier_block exec_unmap_nb = { - .notifier_call = mm_notify, + .notifier_call = munmap_notify, };static struct notifier_block exit_mmap_nb = {