Patch to add cpumask attribute for the Nest pmu to control per-chip counter values to be read by cpus. Also adds support of cpu hotplug.
Signed-off-by: Madhavan Srinivasan <ma...@linux.vnet.ibm.com> --- arch/powerpc/perf/uncore_pmu.c | 152 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/arch/powerpc/perf/uncore_pmu.c b/arch/powerpc/perf/uncore_pmu.c index cc544d3..67ab6c0 100644 --- a/arch/powerpc/perf/uncore_pmu.c +++ b/arch/powerpc/perf/uncore_pmu.c @@ -19,6 +19,32 @@ struct ppc64_uncore_type *empty_uncore[] = { NULL, }; struct ppc64_uncore_type **ppc64_uncore = empty_uncore; +/* mask of cpus that collect uncore events */ +static cpumask_t uncore_cpu_mask; + +static ssize_t uncore_get_attr_cpumask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cpumap_print_to_pagebuf(true, buf, &uncore_cpu_mask); +} + +/* + * cpumask attr used by perf userspace to pick the cpus to execute + * in case of -a option. User can still specify -C option to override. + * Since these Nest Counters are per-chip, make only one cpu from chip + * to read. + */ +static DEVICE_ATTR(cpumask, S_IRUGO, uncore_get_attr_cpumask, NULL); + +static struct attribute *uncore_pmu_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static struct attribute_group uncore_pmu_attr_group = { + .attrs = uncore_pmu_attrs, +}; + struct ppc64_uncore_pmu *uncore_event_to_pmu(struct perf_event *event) { return container_of(event->pmu, struct ppc64_uncore_pmu, pmu); @@ -43,6 +69,7 @@ int __init uncore_type_init(struct ppc64_uncore_type *type) type->name, (int)i); } + type->pmu_group = &uncore_pmu_attr_group; return 0; } @@ -82,6 +109,130 @@ static int __init uncore_pmus_register(void) return 0; } +static void +uncore_change_context(struct ppc64_uncore_type **uncores, + int old_cpu, int new_cpu) +{ + struct ppc64_uncore_type *type; + struct ppc64_uncore_pmu *pmu; + int i, j; + + for (i = 0; uncores[i]; i++) { + type = uncores[i]; + for (j = 0; j < type->num_boxes; j++) { + pmu = &type->pmus[j]; + if (old_cpu < 0) + continue; + if (new_cpu >= 0) { + perf_pmu_migrate_context(&pmu->pmu, + old_cpu, new_cpu); + } + } + } +} + +static void uncore_event_init_cpu(int cpu) +{ + int i, phys_id; + + phys_id = topology_physical_package_id(cpu); + for_each_cpu(i, &uncore_cpu_mask) { + if (phys_id == topology_physical_package_id(i)) + return; + } + + cpumask_set_cpu(cpu, &uncore_cpu_mask); + + uncore_change_context(ppc64_uncore, -1, cpu); +} + +static void uncore_event_exit_cpu(int cpu) +{ + int i, phys_id, target; + + /* if exiting cpu is used for collecting uncore events */ + if (!cpumask_test_and_clear_cpu(cpu, &uncore_cpu_mask)) + return; + + /* find a new cpu to collect uncore events */ + phys_id = topology_physical_package_id(cpu); + target = -1; + for_each_online_cpu(i) { + if (i == cpu) + continue; + if (phys_id == topology_physical_package_id(i)) { + target = i; + break; + } + } + + /* migrate uncore events to the new cpu */ + if (target >= 0) + cpumask_set_cpu(target, &uncore_cpu_mask); + + uncore_change_context(ppc64_uncore, cpu, target); +} + +static int uncore_cpu_notifier(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (long)hcpu; + + /* select the cpu that collects uncore events */ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_DOWN_FAILED: + case CPU_STARTING: + uncore_event_init_cpu(cpu); + break; + case CPU_DOWN_PREPARE: + uncore_event_exit_cpu(cpu); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block uncore_cpu_nb = { + .notifier_call = uncore_cpu_notifier, + /* + * to migrate uncore events, our notifier should be executed + * before perf core's notifier. + */ + .priority = CPU_PRI_PERF + 1, +}; + +static void __init cpumask_per_chip_init(void) +{ + int cpu; + + if (!cpumask_empty(&uncore_cpu_mask)) + return; + + cpu_notifier_register_begin(); + + for_each_online_cpu(cpu) { + int i, phys_id = topology_physical_package_id(cpu); + + for_each_cpu(i, &uncore_cpu_mask) { + if (phys_id == topology_physical_package_id(i)) { + phys_id = -1; + break; + } + } + if (phys_id < 0) + continue; + + uncore_event_init_cpu(cpu); + } + + __register_cpu_notifier(&uncore_cpu_nb); + + cpu_notifier_register_done(); +} + + static int __init uncore_init(void) { int ret = 0; @@ -95,6 +246,7 @@ static int __init uncore_init(void) if (ret) return ret; + cpumask_per_chip_init(); uncore_pmus_register(); return ret; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/