X-Git-Url: http://www.pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Foprofile%2Fcpu_buffer.c;h=01d38e78cde18bd341fca22940249e4a80068217;hb=92fb83afd6664a6f8a05f990d264c998f9b99f69;hp=7ba78e6d210e72ac9b41395ccc25fb6b4bfcd658;hpb=42e7b09e688bf86c032ca36ddb65cc548bb6147f;p=linux-2.6-omap-h63xx.git diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 7ba78e6d210..01d38e78cde 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. @@ -21,7 +22,7 @@ #include #include #include - + #include "event_buffer.h" #include "cpu_buffer.h" #include "buffer_sync.h" @@ -37,27 +38,40 @@ static int work_enabled; 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; } } +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; - + 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, cpu_to_node(i)); if (!b->buffer) goto fail; - + b->last_task = NULL; b->last_is_kernel = -1; b->tracing = 0; @@ -111,7 +125,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 @@ -122,7 +136,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; @@ -133,7 +147,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; @@ -148,17 +162,17 @@ 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) +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); } @@ -172,10 +186,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++; @@ -204,12 +218,12 @@ 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; } -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++; @@ -221,7 +235,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; } @@ -254,6 +268,57 @@ 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 + +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; + } + + /* 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_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_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 (backtrace_depth) + 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); @@ -293,11 +358,16 @@ 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("WQ on CPU%d, prefer CPU%d\n", + 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);