Perf: keep events across hotplug
Keep event list alive across a CPU hotplug so that perf
can resume when the CPU comes back online. Bring a CPU
online when exiting a perf session so it can be cleaned
up properly.
Change-Id: Ie0e4a43f751beb77afdc84e9d52b21780f279d80
Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 5b99903..4c0aac5 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -661,6 +661,7 @@
armpmu->pmu.start = armpmu_start;
armpmu->pmu.stop = armpmu_stop;
armpmu->pmu.read = armpmu_read;
+ armpmu->pmu.events_across_hotplug = 1;
}
int armpmu_register(struct arm_pmu *armpmu, char *name, int type)
@@ -776,62 +777,6 @@
disable_percpu_irq(irq);
}
-/*
- * PMU hardware loses all context when a CPU goes offline.
- * When a CPU is hotplugged back in, since some hardware registers are
- * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading
- * junk values out of them.
- */
-static int __cpuinit pmu_cpu_notify(struct notifier_block *b,
- unsigned long action, void *hcpu)
-{
- int irq;
-
- if (cpu_has_active_perf((int)hcpu)) {
- switch ((action & ~CPU_TASKS_FROZEN)) {
-
- case CPU_DOWN_PREPARE:
- /*
- * If this is on a multicore CPU, we need
- * to disarm the PMU IRQ before disappearing.
- */
- if (cpu_pmu &&
- cpu_pmu->plat_device->dev.platform_data) {
- irq = platform_get_irq(cpu_pmu->plat_device, 0);
- smp_call_function_single((int)hcpu,
- disable_irq_callback, &irq, 1);
- }
- return NOTIFY_DONE;
-
- case CPU_UP_PREPARE:
- /*
- * If this is on a multicore CPU, we need
- * to arm the PMU IRQ before appearing.
- */
- if (cpu_pmu &&
- cpu_pmu->plat_device->dev.platform_data) {
- irq = platform_get_irq(cpu_pmu->plat_device, 0);
- smp_call_function_single((int)hcpu,
- enable_irq_callback, &irq, 1);
- }
- return NOTIFY_DONE;
-
- case CPU_STARTING:
- if (cpu_pmu && cpu_pmu->reset) {
- cpu_pmu->reset(NULL);
- return NOTIFY_OK;
- }
- default:
- return NOTIFY_DONE;
- }
- }
-
- if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
- return NOTIFY_DONE;
-
- return NOTIFY_OK;
-}
-
static void armpmu_update_counters(void)
{
struct pmu_hw_events *hw_events;
@@ -852,6 +797,64 @@
}
}
+/*
+ * PMU hardware loses all context when a CPU goes offline.
+ * When a CPU is hotplugged back in, since some hardware registers are
+ * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading
+ * junk values out of them.
+ */
+static int __cpuinit pmu_cpu_notify(struct notifier_block *b,
+ unsigned long action, void *hcpu)
+{
+ int irq;
+ struct pmu *pmu;
+
+ if (cpu_has_active_perf((int)hcpu)) {
+ switch ((action & ~CPU_TASKS_FROZEN)) {
+
+ case CPU_DOWN_PREPARE:
+ armpmu_update_counters();
+ /*
+ * If this is on a multicore CPU, we need
+ * to disarm the PMU IRQ before disappearing.
+ */
+ if (cpu_pmu &&
+ cpu_pmu->plat_device->dev.platform_data) {
+ irq = platform_get_irq(cpu_pmu->plat_device, 0);
+ smp_call_function_single((int)hcpu,
+ disable_irq_callback, &irq, 1);
+ }
+ return NOTIFY_DONE;
+
+ case CPU_STARTING:
+ /*
+ * If this is on a multicore CPU, we need
+ * to arm the PMU IRQ before appearing.
+ */
+ if (cpu_pmu &&
+ cpu_pmu->plat_device->dev.platform_data) {
+ irq = platform_get_irq(cpu_pmu->plat_device, 0);
+ enable_irq_callback(&irq);
+ }
+
+ if (cpu_pmu && cpu_pmu->reset) {
+ __get_cpu_var(from_idle) = 1;
+ cpu_pmu->reset(NULL);
+ pmu = &cpu_pmu->pmu;
+ pmu->pmu_enable(pmu);
+ return NOTIFY_OK;
+ }
+ default:
+ return NOTIFY_DONE;
+ }
+ }
+
+ if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
+ return NOTIFY_DONE;
+
+ return NOTIFY_OK;
+}
+
static struct notifier_block __cpuinitdata pmu_cpu_notifier = {
.notifier_call = pmu_cpu_notify,
};
diff --git a/arch/arm/mach-msm/perf_debug.c b/arch/arm/mach-msm/perf_debug.c
index 70c8c68..478848d 100644
--- a/arch/arm/mach-msm/perf_debug.c
+++ b/arch/arm/mach-msm/perf_debug.c
@@ -31,6 +31,7 @@
"10 Perf: Fix counts across power collapse\n"
"12 Perf: Make per-process counters configurable\n"
"13 msm: perf: Add L2 support for tracecounters\n"
+ "14 Perf: keep events across hotplug\n"
;
static ssize_t desc_read(struct file *fp, char __user *buf,