From 345c25730d085c45622ac779da4dbd97dc3a10fe Mon Sep 17 00:00:00 2001 From: Barry Kasindorf Date: Tue, 22 Jul 2008 21:08:54 +0200 Subject: x86/oprofile: add IBS support for AMD CPUs, IBS buffer handling routines This patchset supports the new profiling hardware available in the latest AMD CPUs in the oProfile driver. Signed-off-by: Barry Kasindorf Signed-off-by: Robert Richter Cc: oprofile-list Signed-off-by: Ingo Molnar --- drivers/oprofile/cpu_buffer.c | 68 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 2450b3a393ff..c9ac4e156918 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -5,6 +5,7 @@ * @remark Read the file COPYING * * @author John Levon + * @author Barry Kasindorf * * Each CPU has a local buffer that stores PC value/event * pairs. We also log context switches when we notice them. @@ -207,7 +208,7 @@ static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, return 1; } -static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) +static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) { if (nr_available_slots(cpu_buf) < 4) { cpu_buf->sample_lost_overflow++; @@ -252,6 +253,71 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) oprofile_add_ext_sample(pc, regs, event, is_kernel); } +#define MAX_IBS_SAMPLE_SIZE 14 +static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, + unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) +{ + struct task_struct *task; + + cpu_buf->sample_received++; + + if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) { + cpu_buf->sample_lost_overflow++; + return 0; + } + + is_kernel = !!is_kernel; + + /* notice a switch from user->kernel or vice versa */ + if (cpu_buf->last_is_kernel != is_kernel) { + cpu_buf->last_is_kernel = is_kernel; + add_code(cpu_buf, is_kernel); + } + + /* notice a task switch */ + if (!is_kernel) { + task = current; + + if (cpu_buf->last_task != task) { + cpu_buf->last_task = task; + add_code(cpu_buf, (unsigned long)task); + } + } + + add_code(cpu_buf, ibs_code); + add_sample(cpu_buf, ibs[0], ibs[1]); + add_sample(cpu_buf, ibs[2], ibs[3]); + add_sample(cpu_buf, ibs[4], ibs[5]); + + if (ibs_code == IBS_OP_BEGIN) { + add_sample(cpu_buf, ibs[6], ibs[7]); + add_sample(cpu_buf, ibs[8], ibs[9]); + add_sample(cpu_buf, ibs[10], ibs[11]); + } + + return 1; +} + +void oprofile_add_ibs_sample(struct pt_regs *const regs, + unsigned int * const ibs_sample, u8 code) +{ + int is_kernel = !user_mode(regs); + unsigned long pc = profile_pc(regs); + + struct oprofile_cpu_buffer *cpu_buf = + &per_cpu(cpu_buffer, smp_processor_id()); + + if (!backtrace_depth) { + log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code); + return; + } + + /* if log_sample() fails we can't backtrace since we lost the source + * of this event */ + if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code)) + oprofile_ops.backtrace(regs, backtrace_depth); +} + void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); -- cgit v1.2.3 From 852402cc27bfa1200164e9e8dc7f6e5f0a4fbd46 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 22 Jul 2008 21:09:06 +0200 Subject: x86/oprofile: add CONFIG_OPROFILE_IBS option Signed-off-by: Robert Richter Cc: oprofile-list Cc: Robert Richter Cc: Barry Kasindorf Signed-off-by: Ingo Molnar --- drivers/oprofile/cpu_buffer.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index c9ac4e156918..aba905b3afb8 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -253,6 +253,8 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) oprofile_add_ext_sample(pc, regs, event, is_kernel); } +#ifdef CONFIG_OPROFILE_IBS + #define MAX_IBS_SAMPLE_SIZE 14 static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) @@ -318,6 +320,8 @@ void oprofile_add_ibs_sample(struct pt_regs *const regs, oprofile_ops.backtrace(regs, backtrace_depth); } +#endif + void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); -- cgit v1.2.3 From bd17b625c09d1ed14c4d98604186b0bbb314f6b2 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 22 Jul 2008 21:09:07 +0200 Subject: oprofile: fix printk in cpu_buffer.c Signed-off-by: Robert Richter Cc: oprofile-list Cc: Robert Richter Cc: Barry Kasindorf Signed-off-by: Ingo Molnar --- drivers/oprofile/cpu_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index aba905b3afb8..4decab624e76 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -364,7 +364,7 @@ static void wq_sync_buffer(struct work_struct *work) struct oprofile_cpu_buffer * b = container_of(work, struct oprofile_cpu_buffer, work.work); if (b->cpu != smp_processor_id()) { - printk("WQ on CPU%d, prefer CPU%d\n", + printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n", smp_processor_id(), b->cpu); } sync_buffer(b->cpu); -- cgit v1.2.3 From e2fee2761ad1df2d29b9d502a3cefc87a17b32ca Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 18 Jul 2008 17:36:20 +0200 Subject: OProfile: Rework oprofile_add_ibs_sample() function Code looks much more cleaner now. Signed-off-by: Robert Richter --- drivers/oprofile/cpu_buffer.c | 48 ++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index e1bd5a937f6c..d6c68270b3da 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -257,21 +257,23 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) #ifdef CONFIG_OPROFILE_IBS -#define MAX_IBS_SAMPLE_SIZE 14 -static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, - unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) +#define MAX_IBS_SAMPLE_SIZE 14 + +void oprofile_add_ibs_sample(struct pt_regs *const regs, + unsigned int * const ibs_sample, int ibs_code) { + int is_kernel = !user_mode(regs); + struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); struct task_struct *task; cpu_buf->sample_received++; if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) { + /* we can't backtrace since we lost the source of this event */ cpu_buf->sample_lost_overflow++; - return 0; + return; } - is_kernel = !!is_kernel; - /* notice a switch from user->kernel or vice versa */ if (cpu_buf->last_is_kernel != is_kernel) { cpu_buf->last_is_kernel = is_kernel; @@ -281,7 +283,6 @@ static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, /* notice a task switch */ if (!is_kernel) { task = current; - if (cpu_buf->last_task != task) { cpu_buf->last_task = task; add_code(cpu_buf, (unsigned long)task); @@ -289,36 +290,17 @@ static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, } add_code(cpu_buf, ibs_code); - add_sample(cpu_buf, ibs[0], ibs[1]); - add_sample(cpu_buf, ibs[2], ibs[3]); - add_sample(cpu_buf, ibs[4], ibs[5]); + add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]); + add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]); + add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]); if (ibs_code == IBS_OP_BEGIN) { - add_sample(cpu_buf, ibs[6], ibs[7]); - add_sample(cpu_buf, ibs[8], ibs[9]); - add_sample(cpu_buf, ibs[10], ibs[11]); - } - - return 1; -} - -void oprofile_add_ibs_sample(struct pt_regs *const regs, - unsigned int * const ibs_sample, u8 code) -{ - int is_kernel = !user_mode(regs); - unsigned long pc = profile_pc(regs); - - struct oprofile_cpu_buffer *cpu_buf = - &per_cpu(cpu_buffer, smp_processor_id()); - - if (!backtrace_depth) { - log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code); - return; + add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]); + add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]); + add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]); } - /* if log_sample() fails we can't backtrace since we lost the source - * of this event */ - if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code)) + if (backtrace_depth) oprofile_ops.backtrace(regs, backtrace_depth); } -- cgit v1.2.3 From 25ad2913cae9c9e3ed28075caeb2eefccd636f4f Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 5 Sep 2008 17:12:36 +0200 Subject: oprofile: more whitespace fixes Signed-off-by: Robert Richter --- drivers/oprofile/cpu_buffer.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index d6c68270b3da..b47ce038490f 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -112,7 +112,7 @@ void end_cpu_work(void) } /* Resets the cpu buffer to a sane state. */ -void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf) +void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf) { /* reset these to invalid values; the next sample * collected will populate the buffer with proper @@ -123,7 +123,7 @@ void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf) } /* compute number of available slots in cpu_buffer queue */ -static unsigned long nr_available_slots(struct oprofile_cpu_buffer const * b) +static unsigned long nr_available_slots(struct oprofile_cpu_buffer const *b) { unsigned long head = b->head_pos; unsigned long tail = b->tail_pos; @@ -134,7 +134,7 @@ static unsigned long nr_available_slots(struct oprofile_cpu_buffer const * b) return tail + (b->buffer_size - head) - 1; } -static void increment_head(struct oprofile_cpu_buffer * b) +static void increment_head(struct oprofile_cpu_buffer *b) { unsigned long new_head = b->head_pos + 1; @@ -149,17 +149,17 @@ static void increment_head(struct oprofile_cpu_buffer * b) } static inline void -add_sample(struct oprofile_cpu_buffer * cpu_buf, +add_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, unsigned long event) { - struct op_sample * entry = &cpu_buf->buffer[cpu_buf->head_pos]; + struct op_sample *entry = &cpu_buf->buffer[cpu_buf->head_pos]; entry->eip = pc; entry->event = event; increment_head(cpu_buf); } static inline void -add_code(struct oprofile_cpu_buffer * buffer, unsigned long value) +add_code(struct oprofile_cpu_buffer *buffer, unsigned long value) { add_sample(buffer, ESCAPE_CODE, value); } @@ -173,10 +173,10 @@ add_code(struct oprofile_cpu_buffer * buffer, unsigned long value) * pc. We tag this in the buffer by generating kernel enter/exit * events whenever is_kernel changes */ -static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, +static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, int is_kernel, unsigned long event) { - struct task_struct * task; + struct task_struct *task; cpu_buf->sample_received++; @@ -222,7 +222,7 @@ static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) return 1; } -static void oprofile_end_trace(struct oprofile_cpu_buffer * cpu_buf) +static void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf) { cpu_buf->tracing = 0; } @@ -260,7 +260,7 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) #define MAX_IBS_SAMPLE_SIZE 14 void oprofile_add_ibs_sample(struct pt_regs *const regs, - unsigned int * const ibs_sample, int ibs_code) + unsigned int *const ibs_sample, int ibs_code) { int is_kernel = !user_mode(regs); struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); @@ -345,7 +345,7 @@ void oprofile_add_trace(unsigned long pc) */ static void wq_sync_buffer(struct work_struct *work) { - struct oprofile_cpu_buffer * b = + struct oprofile_cpu_buffer *b = container_of(work, struct oprofile_cpu_buffer, work.work); if (b->cpu != smp_processor_id()) { printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n", -- cgit v1.2.3 From 6a18037d4165f691063b43816be3152e9006eb06 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 16 Oct 2008 15:01:40 +0200 Subject: oprofile: fixing whitespaces in drivers/oprofile/* Signed-off-by: Robert Richter --- drivers/oprofile/cpu_buffer.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index b47ce038490f..5a178065cfa0 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -22,7 +22,7 @@ #include #include #include - + #include "event_buffer.h" #include "cpu_buffer.h" #include "buffer_sync.h" @@ -38,7 +38,7 @@ static int work_enabled; void free_cpu_buffers(void) { int i; - + for_each_online_cpu(i) { vfree(per_cpu(cpu_buffer, i).buffer); per_cpu(cpu_buffer, i).buffer = NULL; @@ -48,17 +48,17 @@ void free_cpu_buffers(void) int alloc_cpu_buffers(void) { int i; - + unsigned long buffer_size = fs_cpu_buffer_size; - + for_each_online_cpu(i) { struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i); - + b->buffer = vmalloc_node(sizeof(struct op_sample) * buffer_size, cpu_to_node(i)); if (!b->buffer) goto fail; - + b->last_task = NULL; b->last_is_kernel = -1; b->tracing = 0; @@ -150,7 +150,7 @@ static void increment_head(struct oprofile_cpu_buffer *b) static inline void add_sample(struct oprofile_cpu_buffer *cpu_buf, - unsigned long pc, unsigned long event) + unsigned long pc, unsigned long event) { struct op_sample *entry = &cpu_buf->buffer[cpu_buf->head_pos]; entry->eip = pc; @@ -205,7 +205,7 @@ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, cpu_buf->last_task = task; add_code(cpu_buf, (unsigned long)task); } - + add_sample(cpu_buf, pc, event); return 1; } -- cgit v1.2.3 From 4bd9b9dc97e344670e9e5762399a07dcd5f15311 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 15 Oct 2008 11:03:39 -0500 Subject: oprofile: hotplug cpu fix This patch addresses problems when hotplugging cpus while profiling. Instead of allocating only online cpus, all possible cpu buffers are allocated, which allows cpus to be onlined during operation. If a cpu is offlined before profiling is shutdown wq_sync_buffer checks for this condition then cancels this work and does not sync this buffer. Signed-off-by: Chris J Arges Signed-off-by: Robert Richter --- drivers/oprofile/cpu_buffer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 5a178065cfa0..67bcc1c95e60 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -39,7 +39,7 @@ void free_cpu_buffers(void) { int i; - for_each_online_cpu(i) { + for_each_possible_cpu(i) { vfree(per_cpu(cpu_buffer, i).buffer); per_cpu(cpu_buffer, i).buffer = NULL; } @@ -51,7 +51,7 @@ int alloc_cpu_buffers(void) unsigned long buffer_size = fs_cpu_buffer_size; - for_each_online_cpu(i) { + for_each_possible_cpu(i) { struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i); b->buffer = vmalloc_node(sizeof(struct op_sample) * buffer_size, @@ -350,6 +350,11 @@ static void wq_sync_buffer(struct work_struct *work) if (b->cpu != smp_processor_id()) { printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n", smp_processor_id(), b->cpu); + + if (!cpu_online(b->cpu)) { + cancel_delayed_work(&b->work); + return; + } } sync_buffer(b->cpu); -- cgit v1.2.3 From a5598ca0d49821912a5053c05f07fd650671eb6d Mon Sep 17 00:00:00 2001 From: Carl Love Date: Tue, 14 Oct 2008 23:37:01 +0000 Subject: powerpc/oprofile: Fix mutex locking for cell spu-oprofile The issue is the SPU code is not holding the kernel mutex lock while adding samples to the kernel buffer. This patch creates per SPU buffers to hold the data. Data is added to the buffers from in interrupt context. The data is periodically pushed to the kernel buffer via a new Oprofile function oprofile_put_buff(). The oprofile_put_buff() function is called via a work queue enabling the funtion to acquire the mutex lock. The existing user controls for adjusting the per CPU buffer size is used to control the size of the per SPU buffers. Similarly, overflows of the SPU buffers are reported by incrementing the per CPU buffer stats. This eliminates the need to have architecture specific controls for the per SPU buffers which is not acceptable to the OProfile user tool maintainer. The export of the oprofile add_event_entry() is removed as it is no longer needed given this patch. Note, this patch has not addressed the issue of indexing arrays by the spu number. This still needs to be fixed as the spu numbering is not guarenteed to be 0 to max_num_spus-1. Signed-off-by: Carl Love Signed-off-by: Maynard Johnson Signed-off-by: Arnd Bergmann Acked-by: Acked-by: Robert Richter Signed-off-by: Benjamin Herrenschmidt --- drivers/oprofile/cpu_buffer.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/oprofile/cpu_buffer.c') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index e1bd5a937f6c..7ba39fe20a8a 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -38,13 +38,26 @@ static int work_enabled; void free_cpu_buffers(void) { int i; - + for_each_online_cpu(i) { vfree(per_cpu(cpu_buffer, i).buffer); per_cpu(cpu_buffer, i).buffer = NULL; } } +unsigned long oprofile_get_cpu_buffer_size(void) +{ + return fs_cpu_buffer_size; +} + +void oprofile_cpu_buffer_inc_smpl_lost(void) +{ + struct oprofile_cpu_buffer *cpu_buf + = &__get_cpu_var(cpu_buffer); + + cpu_buf->sample_lost_overflow++; +} + int alloc_cpu_buffers(void) { int i; -- cgit v1.2.3