aboutsummaryrefslogtreecommitdiff
path: root/driver/gator_ebs.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/gator_ebs.c')
-rw-r--r--driver/gator_ebs.c255
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