diff options
Diffstat (limited to 'driver/gator_ebs.c')
-rw-r--r-- | driver/gator_ebs.c | 255 |
1 files changed, 135 insertions, 120 deletions
diff --git a/driver/gator_ebs.c b/driver/gator_ebs.c index 8b4b5ff..8c2997c 100644 --- a/driver/gator_ebs.c +++ b/driver/gator_ebs.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011. All rights reserved. + * Copyright (C) ARM Limited 2012. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -7,171 +7,186 @@ * */ -/****************************************************************************** - * event based sampling handling - ******************************************************************************/ - -#if defined (__arm__) -#include "gator_events_armv7.h" +#if defined(__arm__) && (GATOR_PERF_PMU_SUPPORT) #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/irq.h> -#if LINUX_PMU_SUPPORT #include <asm/pmu.h> -static struct platform_device *pmu_device; +extern int pmnc_counters; +extern int ccnt; +extern unsigned long pmnc_enabled[]; +extern unsigned long pmnc_event[]; +extern unsigned long pmnc_count[]; +extern unsigned long pmnc_key[]; + +static DEFINE_PER_CPU(struct perf_event *, pevent); +static DEFINE_PER_CPU(struct perf_event_attr *, pevent_attr); +static DEFINE_PER_CPU(int, key); +static DEFINE_PER_CPU(unsigned int, prev_value); -static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif { - unsigned int cnt, cpu = smp_processor_id(), buftype = EVENT_BUF; - struct pt_regs * const regs = get_irq_regs(); - u32 flags; - - // Stop irq generation - armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); - - // Get and reset overflow status flags - flags = armv7_pmnc_reset_interrupt(); - - // Counters header - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); // type - gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); // time - - // Cycle counter - if (flags & (1 << 31)) { - int value = armv7_ccnt_read(pmnc_count[CCNT]); // overrun - gator_buffer_write_packed_int(cpu, buftype, 2); // length - gator_buffer_write_packed_int(cpu, buftype, pmnc_key[CCNT]); // key - gator_buffer_write_packed_int(cpu, buftype, value); // value - } + unsigned int value, delta, cpu = smp_processor_id(), buftype = EVENT_BUF; - // PMNC counters - for (cnt = CNT0; cnt < CNTMAX; cnt++) { - if (flags & (1 << (cnt - CNT0))) { - int value = armv7_cntn_read(cnt, pmnc_count[cnt]); // overrun - gator_buffer_write_packed_int(cpu, buftype, 2); // length - gator_buffer_write_packed_int(cpu, buftype, pmnc_key[cnt]); // key - gator_buffer_write_packed_int(cpu, buftype, value); // value - } - } + if (event != per_cpu(pevent, cpu)) + return; - // End Counters, length of zero - gator_buffer_write_packed_int(cpu, buftype, 0); + if (buffer_check_space(cpu, buftype, 5 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + value = local64_read(&event->count); + delta = value - per_cpu(prev_value, cpu); + per_cpu(prev_value, cpu) = value; + + // Counters header + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); // type + gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); // time + + // Output counter + gator_buffer_write_packed_int(cpu, buftype, 2); // length + gator_buffer_write_packed_int(cpu, buftype, per_cpu(key, cpu)); // key + gator_buffer_write_packed_int(cpu, buftype, delta); // delta + + // End Counters, length of zero + gator_buffer_write_packed_int(cpu, buftype, 0); + } // Output backtrace - gator_add_sample(cpu, buftype, regs); + if (buffer_check_space(cpu, buftype, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) + gator_add_sample(cpu, buftype, regs); // Check and commit; commit is set to occur once buffer is 3/4 full - event_buffer_check(cpu); + buffer_check(cpu, buftype); +} + +static void gator_event_sampling_online(void) +{ + int cpu = smp_processor_id(), buftype = EVENT_BUF; - // Allow irq generation - armv7_pmnc_write(armv7_pmnc_read() | PMNC_E); + // read the counter and toss the invalid data, return zero instead + struct perf_event * ev = per_cpu(pevent, cpu); + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + ev->pmu->read(ev); + per_cpu(prev_value, cpu) = local64_read(&ev->count); - return IRQ_HANDLED; + // Counters header + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); // type + gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); // time + + // Output counter + gator_buffer_write_packed_int(cpu, buftype, 2); // length + gator_buffer_write_packed_int(cpu, buftype, per_cpu(key, cpu)); // key + gator_buffer_write_packed_int(cpu, buftype, 0); // delta - zero for initialization + + // End Counters, length of zero + gator_buffer_write_packed_int(cpu, buftype, 0); + } } + +static void gator_event_sampling_online_dispatch(int cpu) +{ + struct perf_event * ev; + + if (!event_based_sampling) + return; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler); +#else + ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler, 0); #endif + if (IS_ERR(ev)) { + pr_err("gator: unable to start event-based-sampling"); + return; + } + + if (ev->state != PERF_EVENT_STATE_ACTIVE) { + pr_err("gator: unable to start event-based-sampling"); + perf_event_release_kernel(ev); + return; + } + + ev->pmu->read(ev); + per_cpu(prev_value, cpu) = local64_read(&ev->count); +} + +static void gator_event_sampling_offline_dispatch(int cpu) +{ + if (per_cpu(pevent, cpu)) { + perf_event_release_kernel(per_cpu(pevent, cpu)); + per_cpu(pevent, cpu) = NULL; + } +} + static int gator_event_sampling_start(void) { - int cnt; + int cnt, event = 0, count = 0, ebs_key = 0, cpu; + + for_each_present_cpu(cpu) { + per_cpu(pevent, cpu) = NULL; + per_cpu(pevent_attr, cpu) = NULL; + } event_based_sampling = false; - for (cnt = CCNT; cnt < CNTMAX; cnt++) { + for (cnt = 0; cnt < pmnc_counters; cnt++) { if (pmnc_count[cnt] > 0) { event_based_sampling = true; + event = pmnc_event[cnt]; + count = pmnc_count[cnt]; + ebs_key = pmnc_key[cnt]; break; } } -#if LINUX_PMU_SUPPORT - pmu_device = reserve_pmu(ARM_PMU_DEVICE_CPU); - if (IS_ERR(pmu_device) && (unsigned int)pmu_device != -ENODEV) { - pr_err("gator: unable to reserve the pmu\n"); - return -1; - } - - if (event_based_sampling) { - int irq, i; + if (!event_based_sampling) + return 0; - if (IS_ERR(pmu_device)) { - pr_err("gator: event based sampling is not supported as the kernel function reserve_pmu() failed\n"); + for_each_present_cpu(cpu) { + u32 size = sizeof(struct perf_event_attr); + per_cpu(pevent_attr, cpu) = kmalloc(size, GFP_KERNEL); + if (!per_cpu(pevent_attr, cpu)) return -1; - } - // init_pmu sets the irq affinity, therefore we do not care if it fails for single core - if (init_pmu(ARM_PMU_DEVICE_CPU) != 0 && gator_cpu_cores > 1) { - pr_err("gator: unable to initialize the pmu\n"); - goto out_ebs_start; + memset(per_cpu(pevent_attr, cpu), 0, size); + per_cpu(pevent_attr, cpu)->type = PERF_TYPE_RAW; + per_cpu(pevent_attr, cpu)->size = size; + per_cpu(pevent_attr, cpu)->config = event; + per_cpu(pevent_attr, cpu)->sample_period = count; + per_cpu(pevent_attr, cpu)->pinned = 1; + + // handle special case for ccnt + if (cnt == ccnt) { + per_cpu(pevent_attr, cpu)->type = PERF_TYPE_HARDWARE; + per_cpu(pevent_attr, cpu)->config = PERF_COUNT_HW_CPU_CYCLES; } - if (pmu_device->num_resources == 0) { - pr_err("gator: no irqs for PMUs defined\n"); - goto out_ebs_start; - } - - for (i = 0; i < pmu_device->num_resources; ++i) { - irq = platform_get_irq(pmu_device, i); - if (irq < 0) - continue; - - if (request_irq(irq, armv7_pmnc_interrupt, IRQF_DISABLED | IRQF_NOBALANCING, "armpmu", NULL)) { - pr_err("gator: unable to request IRQ%d for ARM perf counters\n", irq); - - // clean up and exit - for (i = i - 1; i >= 0; --i) { - irq = platform_get_irq(pmu_device, i); - if (irq >= 0) - free_irq(irq, NULL); - } - goto out_ebs_start; - } - } + per_cpu(key, cpu) = ebs_key; } -#else - if (event_based_sampling) { - pr_err("gator: event based sampling only supported in kernel versions 2.6.35 and higher and CONFIG_CPU_HAS_PMU=y\n"); - return -1; - } -#endif return 0; - -#if LINUX_PMU_SUPPORT -out_ebs_start: -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - release_pmu(pmu_device); -#else - release_pmu(ARM_PMU_DEVICE_CPU); -#endif - pmu_device = NULL; - return -1; -#endif } static void gator_event_sampling_stop(void) { -#if LINUX_PMU_SUPPORT - if (event_based_sampling) { - int i, irq; - for (i = pmu_device->num_resources - 1; i >= 0; --i) { - irq = platform_get_irq(pmu_device, i); - if (irq >= 0) - free_irq(irq, NULL); + int cpu; + + for_each_present_cpu(cpu) { + if (per_cpu(pevent_attr, cpu)) { + kfree(per_cpu(pevent_attr, cpu)); + per_cpu(pevent_attr, cpu) = NULL; } } - if (!IS_ERR(pmu_device)) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - release_pmu(pmu_device); -#else - release_pmu(ARM_PMU_DEVICE_CPU); -#endif - } - pmu_device = NULL; -#endif } #else +static void gator_event_sampling_online(void) {} +static void gator_event_sampling_online_dispatch(int cpu) {} +static void gator_event_sampling_offline_dispatch(int cpu) {} static int gator_event_sampling_start(void) {return 0;} static void gator_event_sampling_stop(void) {} #endif |