summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-08-16 16:35:11 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-08-16 16:35:11 -0700
commit2d546ee279b7e90a33100d05e5f01b17d582d465 (patch)
treebb70343cc7f11232e5fb830cbdc72d32a113ddc1
parentcca095ddea6115ac8ffd35893bf85c9c6fc55b61 (diff)
parent1f0f95c5fe9ef3cf90cdd5f4686e3ea1be2a2545 (diff)
Merge "perf: add hotplug support so that perf continues after hotplug"
-rw-r--r--drivers/perf/arm_pmu.c128
1 files changed, 93 insertions, 35 deletions
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 53e3af9052a9..71154ec99d3c 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -550,6 +550,7 @@ static void armpmu_init(struct arm_pmu *armpmu)
.stop = armpmu_stop,
.read = armpmu_read,
.filter_match = armpmu_filter_match,
+ .events_across_hotplug = 1,
};
}
@@ -686,31 +687,12 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
return 0;
}
-/*
- * 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 cpu_pmu_notify(struct notifier_block *b, unsigned long action,
- void *hcpu)
-{
- int cpu = (unsigned long)hcpu;
- struct arm_pmu *pmu = container_of(b, struct arm_pmu, hotplug_nb);
-
- if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
- return NOTIFY_DONE;
-
- if (!cpumask_test_cpu(cpu, &pmu->supported_cpus))
- return NOTIFY_DONE;
-
- if (pmu->reset)
- pmu->reset(pmu);
- else
- return NOTIFY_DONE;
-
- return NOTIFY_OK;
-}
+struct cpu_pm_pmu_args {
+ struct arm_pmu *armpmu;
+ unsigned long cmd;
+ int cpu;
+ int ret;
+};
#ifdef CONFIG_CPU_PM
static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd)
@@ -758,15 +740,19 @@ static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd)
}
}
-static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
- void *v)
+static void cpu_pm_pmu_common(void *info)
{
- struct arm_pmu *armpmu = container_of(b, struct arm_pmu, cpu_pm_nb);
+ struct cpu_pm_pmu_args *data = info;
+ struct arm_pmu *armpmu = data->armpmu;
+ unsigned long cmd = data->cmd;
+ int cpu = data->cpu;
struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events);
- if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus))
- return NOTIFY_DONE;
+ if (!cpumask_test_cpu(cpu, &armpmu->supported_cpus)) {
+ data->ret = NOTIFY_DONE;
+ return;
+ }
/*
* Always reset the PMU registers on power-up even if
@@ -775,8 +761,12 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
if (cmd == CPU_PM_EXIT && armpmu->reset)
armpmu->reset(armpmu);
- if (!enabled)
- return NOTIFY_OK;
+ if (!enabled) {
+ data->ret = NOTIFY_OK;
+ return;
+ }
+
+ data->ret = NOTIFY_OK;
switch (cmd) {
case CPU_PM_ENTER:
@@ -784,15 +774,29 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
cpu_pm_pmu_setup(armpmu, cmd);
break;
case CPU_PM_EXIT:
- cpu_pm_pmu_setup(armpmu, cmd);
case CPU_PM_ENTER_FAILED:
+ cpu_pm_pmu_setup(armpmu, cmd);
armpmu->start(armpmu);
break;
default:
- return NOTIFY_DONE;
+ data->ret = NOTIFY_DONE;
+ break;
}
- return NOTIFY_OK;
+ return;
+}
+
+static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
+ void *v)
+{
+ struct cpu_pm_pmu_args data = {
+ .armpmu = container_of(b, struct arm_pmu, cpu_pm_nb),
+ .cmd = cmd,
+ .cpu = smp_processor_id(),
+ };
+
+ cpu_pm_pmu_common(&data);
+ return data.ret;
}
static int cpu_pm_pmu_register(struct arm_pmu *cpu_pmu)
@@ -808,8 +812,62 @@ static void cpu_pm_pmu_unregister(struct arm_pmu *cpu_pmu)
#else
static inline int cpu_pm_pmu_register(struct arm_pmu *cpu_pmu) { return 0; }
static inline void cpu_pm_pmu_unregister(struct arm_pmu *cpu_pmu) { }
+static inline void cpu_pm_pmu_common(void *info) { }
#endif
+/*
+ * 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 cpu_pmu_notify(struct notifier_block *b, unsigned long action,
+ void *hcpu)
+{
+ unsigned long masked_action = (action & ~CPU_TASKS_FROZEN);
+ struct cpu_pm_pmu_args data = {
+ .armpmu = container_of(b, struct arm_pmu, hotplug_nb),
+ .cpu = (unsigned long)hcpu,
+ };
+
+ if (!cpumask_test_cpu(data.cpu, &data.armpmu->supported_cpus))
+ return NOTIFY_DONE;
+
+ switch (masked_action) {
+ case CPU_STARTING:
+ data.cmd = CPU_PM_EXIT;
+ break;
+ case CPU_DYING:
+ data.cmd = CPU_PM_ENTER;
+ break;
+ case CPU_DOWN_FAILED:
+ data.cmd = CPU_PM_ENTER_FAILED;
+ break;
+ case CPU_ONLINE:
+ if (data.armpmu->plat_device) {
+ struct platform_device *pmu_device =
+ data.armpmu->plat_device;
+ int irq = platform_get_irq(pmu_device, 0);
+
+ if (irq >= 0 && irq_is_percpu(irq)) {
+ smp_call_function_single(data.cpu,
+ cpu_pmu_enable_percpu_irq, &irq, 1);
+ }
+ }
+ return NOTIFY_DONE;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ if (smp_processor_id() == data.cpu)
+ cpu_pm_pmu_common(&data);
+ else
+ smp_call_function_single(data.cpu,
+ cpu_pm_pmu_common, &data, 1);
+
+ return data.ret;
+}
+
static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
{
int err;