From a6c93afed38c242ccf4ec5bcb5ff26ff2521cf36 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Fri, 15 Apr 2011 11:14:38 +0100 Subject: ARM: perf: de-const struct arm_pmu This patch removes const qualifiers from instances of struct arm_pmu, and functions initialising them, in preparation for generalising arm_pmu usage to system (AKA uncore) PMUs. This will allow for dynamically modifiable structures (locks, struct pmu) to be added as members of struct arm_pmu. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Mark Rutland Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 53c9c26..7304996 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -90,7 +90,7 @@ struct arm_pmu { }; /* Set at runtime when we know what CPU type we are. */ -static const struct arm_pmu *armpmu; +static struct arm_pmu *armpmu; enum arm_perf_pmu_ids armpmu_get_pmu_id(void) diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index dd7f3b9..d1abe97 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -635,7 +635,7 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, raw_spin_unlock_irqrestore(&pmu_lock, flags); } -static const struct arm_pmu armv6pmu = { +static struct arm_pmu armv6pmu = { .id = ARM_PERF_PMU_ID_V6, .name = "v6", .handle_irq = armv6pmu_handle_irq, @@ -653,7 +653,7 @@ static const struct arm_pmu armv6pmu = { .max_period = (1LLU << 32) - 1, }; -static const struct arm_pmu *__init armv6pmu_init(void) +static struct arm_pmu *__init armv6pmu_init(void) { return &armv6pmu; } @@ -665,7 +665,7 @@ static const struct arm_pmu *__init armv6pmu_init(void) * disable the interrupt reporting and update the event. When unthrottling we * reset the period and enable the interrupt reporting. */ -static const struct arm_pmu armv6mpcore_pmu = { +static struct arm_pmu armv6mpcore_pmu = { .id = ARM_PERF_PMU_ID_V6MP, .name = "v6mpcore", .handle_irq = armv6pmu_handle_irq, @@ -683,17 +683,17 @@ static const struct arm_pmu armv6mpcore_pmu = { .max_period = (1LLU << 32) - 1, }; -static const struct arm_pmu *__init armv6mpcore_pmu_init(void) +static struct arm_pmu *__init armv6mpcore_pmu_init(void) { return &armv6mpcore_pmu; } #else -static const struct arm_pmu *__init armv6pmu_init(void) +static struct arm_pmu *__init armv6pmu_init(void) { return NULL; } -static const struct arm_pmu *__init armv6mpcore_pmu_init(void) +static struct arm_pmu *__init armv6mpcore_pmu_init(void) { return NULL; } diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 4c85183..74f9119 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1188,7 +1188,7 @@ static u32 __init armv7_read_num_pmnc_events(void) return nb_cnt + 1; } -static const struct arm_pmu *__init armv7_a8_pmu_init(void) +static struct arm_pmu *__init armv7_a8_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA8; armv7pmu.name = "ARMv7 Cortex-A8"; @@ -1198,7 +1198,7 @@ static const struct arm_pmu *__init armv7_a8_pmu_init(void) return &armv7pmu; } -static const struct arm_pmu *__init armv7_a9_pmu_init(void) +static struct arm_pmu *__init armv7_a9_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA9; armv7pmu.name = "ARMv7 Cortex-A9"; @@ -1208,7 +1208,7 @@ static const struct arm_pmu *__init armv7_a9_pmu_init(void) return &armv7pmu; } -static const struct arm_pmu *__init armv7_a5_pmu_init(void) +static struct arm_pmu *__init armv7_a5_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA5; armv7pmu.name = "ARMv7 Cortex-A5"; @@ -1218,7 +1218,7 @@ static const struct arm_pmu *__init armv7_a5_pmu_init(void) return &armv7pmu; } -static const struct arm_pmu *__init armv7_a15_pmu_init(void) +static struct arm_pmu *__init armv7_a15_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA15; armv7pmu.name = "ARMv7 Cortex-A15"; @@ -1228,22 +1228,22 @@ static const struct arm_pmu *__init armv7_a15_pmu_init(void) return &armv7pmu; } #else -static const struct arm_pmu *__init armv7_a8_pmu_init(void) +static struct arm_pmu *__init armv7_a8_pmu_init(void) { return NULL; } -static const struct arm_pmu *__init armv7_a9_pmu_init(void) +static struct arm_pmu *__init armv7_a9_pmu_init(void) { return NULL; } -static const struct arm_pmu *__init armv7_a5_pmu_init(void) +static struct arm_pmu *__init armv7_a5_pmu_init(void) { return NULL; } -static const struct arm_pmu *__init armv7_a15_pmu_init(void) +static struct arm_pmu *__init armv7_a15_pmu_init(void) { return NULL; } diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 3c43974..730b1c2 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -424,7 +424,7 @@ xscale1pmu_write_counter(int counter, u32 val) } } -static const struct arm_pmu xscale1pmu = { +static struct arm_pmu xscale1pmu = { .id = ARM_PERF_PMU_ID_XSCALE1, .name = "xscale1", .handle_irq = xscale1pmu_handle_irq, @@ -442,7 +442,7 @@ static const struct arm_pmu xscale1pmu = { .max_period = (1LLU << 32) - 1, }; -static const struct arm_pmu *__init xscale1pmu_init(void) +static struct arm_pmu *__init xscale1pmu_init(void) { return &xscale1pmu; } @@ -786,7 +786,7 @@ xscale2pmu_write_counter(int counter, u32 val) } } -static const struct arm_pmu xscale2pmu = { +static struct arm_pmu xscale2pmu = { .id = ARM_PERF_PMU_ID_XSCALE2, .name = "xscale2", .handle_irq = xscale2pmu_handle_irq, @@ -804,17 +804,17 @@ static const struct arm_pmu xscale2pmu = { .max_period = (1LLU << 32) - 1, }; -static const struct arm_pmu *__init xscale2pmu_init(void) +static struct arm_pmu *__init xscale2pmu_init(void) { return &xscale2pmu; } #else -static const struct arm_pmu *__init xscale1pmu_init(void) +static struct arm_pmu *__init xscale1pmu_init(void) { return NULL; } -static const struct arm_pmu *__init xscale2pmu_init(void) +static struct arm_pmu *__init xscale2pmu_init(void) { return NULL; } -- cgit v0.10.2 From b0e89590f4f27ea5ff30bdedb9a58ea904a6b353 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 26 Jul 2011 22:10:28 +0100 Subject: ARM: PMU: move CPU PMU platform device handling and init into perf Once upon a time, OProfile and Perf fought hard over who could play with the PMU. To stop all hell from breaking loose, pmu.c offered an internal reserve/release API and took care of parsing PMU platform data passed in from board support code. Now that Perf has ingested OProfile, let's move the platform device handling into the Perf driver and out of the PMU locking code. Unfortunately, the lock has to remain to prevent Perf being bitten by out-of-tree modules such as LTTng, which still claim a right to the PMU when Perf isn't looking. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Will Deacon diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index b7e82c4..a06ba87 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -14,6 +14,10 @@ #include +/* + * Types of PMUs that can be accessed directly and require mutual + * exclusion between profiling tools. + */ enum arm_pmu_type { ARM_PMU_DEVICE_CPU = 0, ARM_NUM_PMU_DEVICES, @@ -37,21 +41,17 @@ struct arm_pmu_platdata { * reserve_pmu() - reserve the hardware performance counters * * Reserve the hardware performance counters in the system for exclusive use. - * The platform_device for the system is returned on success, ERR_PTR() - * encoded error on failure. + * Returns 0 on success or -EBUSY if the lock is already held. */ -extern struct platform_device * +extern int reserve_pmu(enum arm_pmu_type type); /** * release_pmu() - Relinquish control of the performance counters * * Release the performance counters and allow someone else to use them. - * Callers must have disabled the counters and released IRQs before calling - * this. The platform_device returned from reserve_pmu() must be passed as - * a cookie. */ -extern int +extern void release_pmu(enum arm_pmu_type type); /** @@ -68,23 +68,14 @@ init_pmu(enum arm_pmu_type type); #include -static inline struct platform_device * -reserve_pmu(enum arm_pmu_type type) -{ - return ERR_PTR(-ENODEV); -} - static inline int -release_pmu(enum arm_pmu_type type) +reserve_pmu(enum arm_pmu_type type) { return -ENODEV; } -static inline int -init_pmu(enum arm_pmu_type type) -{ - return -ENODEV; -} +static inline void +release_pmu(enum arm_pmu_type type) { } #endif /* CONFIG_CPU_HAS_PMU */ diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 7304996..8514855 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -393,15 +393,15 @@ armpmu_reserve_hardware(void) { struct arm_pmu_platdata *plat; irq_handler_t handle_irq; - int i, err = -ENODEV, irq; + int i, err, irq, irqs; - pmu_device = reserve_pmu(ARM_PMU_DEVICE_CPU); - if (IS_ERR(pmu_device)) { + err = reserve_pmu(ARM_PMU_DEVICE_CPU); + if (err) { pr_warning("unable to reserve pmu\n"); - return PTR_ERR(pmu_device); + return err; } - init_pmu(ARM_PMU_DEVICE_CPU); + irqs = pmu_device->num_resources; plat = dev_get_platdata(&pmu_device->dev); if (plat && plat->handle_irq) @@ -409,22 +409,34 @@ armpmu_reserve_hardware(void) else handle_irq = armpmu->handle_irq; - if (pmu_device->num_resources < 1) { + if (irqs < 1) { pr_err("no irqs for PMUs defined\n"); return -ENODEV; } - for (i = 0; i < pmu_device->num_resources; ++i) { + for (i = 0; i < irqs; ++i) { irq = platform_get_irq(pmu_device, i); if (irq < 0) continue; + /* + * If we have a single PMU interrupt that we can't shift, + * assume that we're running on a uniprocessor machine and + * continue. + */ + err = irq_set_affinity(irq, cpumask_of(i)); + if (err && irqs > 1) { + pr_err("unable to set irq affinity (irq=%d, cpu=%u)\n", + irq, i); + break; + } + err = request_irq(irq, handle_irq, IRQF_DISABLED | IRQF_NOBALANCING, - "armpmu", NULL); + "arm-pmu", NULL); if (err) { - pr_warning("unable to request IRQ%d for ARM perf " - "counters\n", irq); + pr_err("unable to request IRQ%d for ARM PMU counters\n", + irq); break; } } @@ -436,7 +448,6 @@ armpmu_reserve_hardware(void) free_irq(irq, NULL); } release_pmu(ARM_PMU_DEVICE_CPU); - pmu_device = NULL; } return err; @@ -455,7 +466,6 @@ armpmu_release_hardware(void) armpmu->stop(); release_pmu(ARM_PMU_DEVICE_CPU); - pmu_device = NULL; } static atomic_t active_events = ATOMIC_INIT(0); @@ -638,6 +648,46 @@ armpmu_reset(void) } arch_initcall(armpmu_reset); +/* + * PMU platform driver and devicetree bindings. + */ +static struct of_device_id armpmu_of_device_ids[] = { + {.compatible = "arm,cortex-a9-pmu"}, + {.compatible = "arm,cortex-a8-pmu"}, + {.compatible = "arm,arm1136-pmu"}, + {.compatible = "arm,arm1176-pmu"}, + {}, +}; + +static struct platform_device_id armpmu_plat_device_ids[] = { + {.name = "arm-pmu"}, + {}, +}; + +static int __devinit armpmu_device_probe(struct platform_device *pdev) +{ + pmu_device = pdev; + return 0; +} + +static struct platform_driver armpmu_driver = { + .driver = { + .name = "arm-pmu", + .of_match_table = armpmu_of_device_ids, + }, + .probe = armpmu_device_probe, + .id_table = armpmu_plat_device_ids, +}; + +static int __init register_pmu_driver(void) +{ + return platform_driver_register(&armpmu_driver); +} +device_initcall(register_pmu_driver); + +/* + * CPU PMU identification and registration. + */ static int __init init_hw_perf_events(void) { diff --git a/arch/arm/kernel/pmu.c b/arch/arm/kernel/pmu.c index c53474f..2c3407e 100644 --- a/arch/arm/kernel/pmu.c +++ b/arch/arm/kernel/pmu.c @@ -10,192 +10,26 @@ * */ -#define pr_fmt(fmt) "PMU: " fmt - -#include #include -#include #include #include -#include -#include #include -static volatile long pmu_lock; - -static struct platform_device *pmu_devices[ARM_NUM_PMU_DEVICES]; - -static int __devinit pmu_register(struct platform_device *pdev, - enum arm_pmu_type type) -{ - if (type < 0 || type >= ARM_NUM_PMU_DEVICES) { - pr_warning("received registration request for unknown " - "PMU device type %d\n", type); - return -EINVAL; - } - - if (pmu_devices[type]) { - pr_warning("rejecting duplicate registration of PMU device " - "type %d.", type); - return -ENOSPC; - } - - pr_info("registered new PMU device of type %d\n", type); - pmu_devices[type] = pdev; - return 0; -} - -#define OF_MATCH_PMU(_name, _type) { \ - .compatible = _name, \ - .data = (void *)_type, \ -} - -#define OF_MATCH_CPU(name) OF_MATCH_PMU(name, ARM_PMU_DEVICE_CPU) - -static struct of_device_id armpmu_of_device_ids[] = { - OF_MATCH_CPU("arm,cortex-a9-pmu"), - OF_MATCH_CPU("arm,cortex-a8-pmu"), - OF_MATCH_CPU("arm,arm1136-pmu"), - OF_MATCH_CPU("arm,arm1176-pmu"), - {}, -}; - -#define PLAT_MATCH_PMU(_name, _type) { \ - .name = _name, \ - .driver_data = _type, \ -} - -#define PLAT_MATCH_CPU(_name) PLAT_MATCH_PMU(_name, ARM_PMU_DEVICE_CPU) - -static struct platform_device_id armpmu_plat_device_ids[] = { - PLAT_MATCH_CPU("arm-pmu"), - {}, -}; - -enum arm_pmu_type armpmu_device_type(struct platform_device *pdev) -{ - const struct of_device_id *of_id; - const struct platform_device_id *pdev_id; - - /* provided by of_device_id table */ - if (pdev->dev.of_node) { - of_id = of_match_device(armpmu_of_device_ids, &pdev->dev); - BUG_ON(!of_id); - return (enum arm_pmu_type)of_id->data; - } - - /* Provided by platform_device_id table */ - pdev_id = platform_get_device_id(pdev); - BUG_ON(!pdev_id); - return pdev_id->driver_data; -} - -static int __devinit armpmu_device_probe(struct platform_device *pdev) -{ - return pmu_register(pdev, armpmu_device_type(pdev)); -} - -static struct platform_driver armpmu_driver = { - .driver = { - .name = "arm-pmu", - .of_match_table = armpmu_of_device_ids, - }, - .probe = armpmu_device_probe, - .id_table = armpmu_plat_device_ids, -}; - -static int __init register_pmu_driver(void) -{ - return platform_driver_register(&armpmu_driver); -} -device_initcall(register_pmu_driver); +/* + * PMU locking to ensure mutual exclusion between different subsystems. + */ +static unsigned long pmu_lock[BITS_TO_LONGS(ARM_NUM_PMU_DEVICES)]; -struct platform_device * +int reserve_pmu(enum arm_pmu_type type) { - struct platform_device *pdev; - - if (test_and_set_bit_lock(type, &pmu_lock)) { - pdev = ERR_PTR(-EBUSY); - } else if (pmu_devices[type] == NULL) { - clear_bit_unlock(type, &pmu_lock); - pdev = ERR_PTR(-ENODEV); - } else { - pdev = pmu_devices[type]; - } - - return pdev; + return test_and_set_bit_lock(type, pmu_lock) ? -EBUSY : 0; } EXPORT_SYMBOL_GPL(reserve_pmu); -int +void release_pmu(enum arm_pmu_type type) { - if (WARN_ON(!pmu_devices[type])) - return -EINVAL; - clear_bit_unlock(type, &pmu_lock); - return 0; -} -EXPORT_SYMBOL_GPL(release_pmu); - -static int -set_irq_affinity(int irq, - unsigned int cpu) -{ -#ifdef CONFIG_SMP - int err = irq_set_affinity(irq, cpumask_of(cpu)); - if (err) - pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", - irq, cpu); - return err; -#else - return -EINVAL; -#endif -} - -static int -init_cpu_pmu(void) -{ - int i, irqs, err = 0; - struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU]; - - if (!pdev) - return -ENODEV; - - irqs = pdev->num_resources; - - /* - * If we have a single PMU interrupt that we can't shift, assume that - * we're running on a uniprocessor machine and continue. - */ - if (irqs == 1 && !irq_can_set_affinity(platform_get_irq(pdev, 0))) - return 0; - - for (i = 0; i < irqs; ++i) { - err = set_irq_affinity(platform_get_irq(pdev, i), i); - if (err) - break; - } - - return err; -} - -int -init_pmu(enum arm_pmu_type type) -{ - int err = 0; - - switch (type) { - case ARM_PMU_DEVICE_CPU: - err = init_cpu_pmu(); - break; - default: - pr_warning("attempt to initialise PMU of unknown " - "type %d\n", type); - err = -EINVAL; - } - - return err; + clear_bit_unlock(type, pmu_lock); } -EXPORT_SYMBOL_GPL(init_pmu); -- cgit v0.10.2 From 0b390e2126e03b6ec41f96fb0550b1526d00e203 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 27 Jul 2011 15:18:59 +0100 Subject: ARM: perf: use cpumask_t to record active IRQs Commit 5dfc54e0 ("ARM: GIC: avoid routing interrupts to offline CPUs") prevents the GIC from setting the affinity of an IRQ to a CPU with id >= nr_cpu_ids. This was previously abused by perf on some platforms where more IRQs were registered than possible CPUs. This patch fixes the problem by using a cpumask_t to keep track of the active (requested) interrupts in perf. The same effect could be achieved by limiting the number of IRQs to the number of CPUs, but using a mask instead will be useful for adding extended CPU hotplug support in the future. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 8514855..d507fe1 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -69,6 +69,7 @@ static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); struct arm_pmu { enum arm_perf_pmu_ids id; + cpumask_t active_irqs; const char *name; irqreturn_t (*handle_irq)(int irq_num, void *dev); void (*enable)(struct hw_perf_event *evt, int idx); @@ -388,6 +389,25 @@ static irqreturn_t armpmu_platform_irq(int irq, void *dev) return plat->handle_irq(irq, dev, armpmu->handle_irq); } +static void +armpmu_release_hardware(void) +{ + int i, irq, irqs; + + irqs = min(pmu_device->num_resources, num_possible_cpus()); + + for (i = 0; i < irqs; ++i) { + if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) + continue; + irq = platform_get_irq(pmu_device, i); + if (irq >= 0) + free_irq(irq, NULL); + } + + armpmu->stop(); + release_pmu(ARM_PMU_DEVICE_CPU); +} + static int armpmu_reserve_hardware(void) { @@ -401,20 +421,20 @@ armpmu_reserve_hardware(void) return err; } - irqs = pmu_device->num_resources; - plat = dev_get_platdata(&pmu_device->dev); if (plat && plat->handle_irq) handle_irq = armpmu_platform_irq; else handle_irq = armpmu->handle_irq; + irqs = min(pmu_device->num_resources, num_possible_cpus()); if (irqs < 1) { pr_err("no irqs for PMUs defined\n"); return -ENODEV; } for (i = 0; i < irqs; ++i) { + err = 0; irq = platform_get_irq(pmu_device, i); if (irq < 0) continue; @@ -422,13 +442,12 @@ armpmu_reserve_hardware(void) /* * If we have a single PMU interrupt that we can't shift, * assume that we're running on a uniprocessor machine and - * continue. + * continue. Otherwise, continue without this interrupt. */ - err = irq_set_affinity(irq, cpumask_of(i)); - if (err && irqs > 1) { - pr_err("unable to set irq affinity (irq=%d, cpu=%u)\n", - irq, i); - break; + if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { + pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", + irq, i); + continue; } err = request_irq(irq, handle_irq, @@ -437,35 +456,14 @@ armpmu_reserve_hardware(void) if (err) { pr_err("unable to request IRQ%d for ARM PMU counters\n", irq); - break; + armpmu_release_hardware(); + return err; } - } - if (err) { - for (i = i - 1; i >= 0; --i) { - irq = platform_get_irq(pmu_device, i); - if (irq >= 0) - free_irq(irq, NULL); - } - release_pmu(ARM_PMU_DEVICE_CPU); + cpumask_set_cpu(i, &armpmu->active_irqs); } - return err; -} - -static void -armpmu_release_hardware(void) -{ - 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); - } - armpmu->stop(); - - release_pmu(ARM_PMU_DEVICE_CPU); + return 0; } static atomic_t active_events = ATOMIC_INIT(0); -- cgit v0.10.2 From 6330aae7dcd54df893813392e310141be7aa5323 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 19 Jul 2011 14:55:57 +0100 Subject: ARM: perf: use u32 instead of unsigned long for PMNC register The ARMv7 perf backend mixes up u32 and unsigned long, which is rather ugly. This patch makes the ARMv7 PMU code consistently use the u32 type instead. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 74f9119..f4f260d 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -758,26 +758,26 @@ enum armv7_counters { #define ARMV7_FLAG_MASK 0xffffffff /* Mask for writable bits */ #define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK -static inline unsigned long armv7_pmnc_read(void) +static inline u32 armv7_pmnc_read(void) { u32 val; asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r"(val)); return val; } -static inline void armv7_pmnc_write(unsigned long val) +static inline void armv7_pmnc_write(u32 val) { val &= ARMV7_PMNC_MASK; isb(); asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(val)); } -static inline int armv7_pmnc_has_overflowed(unsigned long pmnc) +static inline int armv7_pmnc_has_overflowed(u32 pmnc) { return pmnc & ARMV7_OVERFLOWED_MASK; } -static inline int armv7_pmnc_counter_has_overflowed(unsigned long pmnc, +static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, enum armv7_counters counter) { int ret = 0; @@ -812,7 +812,7 @@ static inline int armv7_pmnc_select_counter(unsigned int idx) static inline u32 armv7pmu_read_counter(int idx) { - unsigned long value = 0; + u32 value = 0; if (idx == ARMV7_CYCLE_COUNTER) asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); @@ -1044,7 +1044,7 @@ static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx) static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) { - unsigned long pmnc; + u32 pmnc; struct perf_sample_data data; struct cpu_hw_events *cpuc; struct pt_regs *regs; -- cgit v0.10.2 From 25e29c7c0f4fcbe911b77a69f015bd6424cedcd0 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 19 Jul 2011 22:17:48 +0100 Subject: ARM: perf: use integers for ARMv7 event indices This patch ensures that integers are used to represent event indices in the ARMv7 PMU backend. This ensures consistency between functions and also with the arm_pmu structure. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index f4f260d..e39bc89 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -793,7 +793,7 @@ static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, return ret; } -static inline int armv7_pmnc_select_counter(unsigned int idx) +static inline int armv7_pmnc_select_counter(int idx) { u32 val; @@ -840,7 +840,7 @@ static inline void armv7pmu_write_counter(int idx, u32 value) smp_processor_id(), idx); } -static inline void armv7_pmnc_write_evtsel(unsigned int idx, u32 val) +static inline void armv7_pmnc_write_evtsel(int idx, u32 val) { if (armv7_pmnc_select_counter(idx) == idx) { val &= ARMV7_EVTSEL_MASK; @@ -848,7 +848,7 @@ static inline void armv7_pmnc_write_evtsel(unsigned int idx, u32 val) } } -static inline u32 armv7_pmnc_enable_counter(unsigned int idx) +static inline int armv7_pmnc_enable_counter(int idx) { u32 val; @@ -869,7 +869,7 @@ static inline u32 armv7_pmnc_enable_counter(unsigned int idx) return idx; } -static inline u32 armv7_pmnc_disable_counter(unsigned int idx) +static inline int armv7_pmnc_disable_counter(int idx) { u32 val; @@ -891,7 +891,7 @@ static inline u32 armv7_pmnc_disable_counter(unsigned int idx) return idx; } -static inline u32 armv7_pmnc_enable_intens(unsigned int idx) +static inline int armv7_pmnc_enable_intens(int idx) { u32 val; @@ -912,7 +912,7 @@ static inline u32 armv7_pmnc_enable_intens(unsigned int idx) return idx; } -static inline u32 armv7_pmnc_disable_intens(unsigned int idx) +static inline int armv7_pmnc_disable_intens(int idx) { u32 val; -- cgit v0.10.2 From c691bb6249b25104fcb6dad31bd772c139ce4a50 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 19 Jul 2011 22:25:55 +0100 Subject: ARM: perf: index ARMv7 event counters starting from zero The current ARMv7 PMU backend indexes event counters from two, with index zero being reserved and index one being used to represent the cycle counter. This patch tidies up the code by indexing from one instead (with zero for the cycle counter). This allows us to remove many of the accessor macros along with the counter enumeration and makes the code much more readable. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index e39bc89..0934c82 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -676,23 +676,24 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] }; /* - * Perf Events counters + * Perf Events' indices */ -enum armv7_counters { - ARMV7_CYCLE_COUNTER = 1, /* Cycle counter */ - ARMV7_COUNTER0 = 2, /* First event counter */ -}; +#define ARMV7_IDX_CYCLE_COUNTER 0 +#define ARMV7_IDX_COUNTER0 1 +#define ARMV7_IDX_COUNTER_LAST (ARMV7_IDX_CYCLE_COUNTER + armpmu->num_events - 1) + +#define ARMV7_MAX_COUNTERS 32 +#define ARMV7_COUNTER_MASK (ARMV7_MAX_COUNTERS - 1) /* - * The cycle counter is ARMV7_CYCLE_COUNTER. - * The first event counter is ARMV7_COUNTER0. - * The last event counter is (ARMV7_COUNTER0 + armpmu->num_events - 1). + * ARMv7 low level PMNC access */ -#define ARMV7_COUNTER_LAST (ARMV7_COUNTER0 + armpmu->num_events - 1) /* - * ARMv7 low level PMNC access + * Perf Event to low level counters mapping */ +#define ARMV7_IDX_TO_COUNTER(x) \ + (((x) - ARMV7_IDX_COUNTER0) & ARMV7_COUNTER_MASK) /* * Per-CPU PMNC: config reg @@ -708,53 +709,13 @@ enum armv7_counters { #define ARMV7_PMNC_MASK 0x3f /* Mask for writable bits */ /* - * Available counters - */ -#define ARMV7_CNT0 0 /* First event counter */ -#define ARMV7_CCNT 31 /* Cycle counter */ - -/* Perf Event to low level counters mapping */ -#define ARMV7_EVENT_CNT_TO_CNTx (ARMV7_COUNTER0 - ARMV7_CNT0) - -/* - * CNTENS: counters enable reg - */ -#define ARMV7_CNTENS_P(idx) (1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) -#define ARMV7_CNTENS_C (1 << ARMV7_CCNT) - -/* - * CNTENC: counters disable reg - */ -#define ARMV7_CNTENC_P(idx) (1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) -#define ARMV7_CNTENC_C (1 << ARMV7_CCNT) - -/* - * INTENS: counters overflow interrupt enable reg - */ -#define ARMV7_INTENS_P(idx) (1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) -#define ARMV7_INTENS_C (1 << ARMV7_CCNT) - -/* - * INTENC: counters overflow interrupt disable reg - */ -#define ARMV7_INTENC_P(idx) (1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) -#define ARMV7_INTENC_C (1 << ARMV7_CCNT) - -/* * EVTSEL: Event selection reg */ #define ARMV7_EVTSEL_MASK 0xff /* Mask for writable bits */ /* - * SELECT: Counter selection reg - */ -#define ARMV7_SELECT_MASK 0x1f /* Mask for writable bits */ - -/* * FLAG: counters overflow flag status reg */ -#define ARMV7_FLAG_P(idx) (1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) -#define ARMV7_FLAG_C (1 << ARMV7_CCNT) #define ARMV7_FLAG_MASK 0xffffffff /* Mask for writable bits */ #define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK @@ -777,34 +738,39 @@ static inline int armv7_pmnc_has_overflowed(u32 pmnc) return pmnc & ARMV7_OVERFLOWED_MASK; } -static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, - enum armv7_counters counter) +static inline int armv7_pmnc_counter_valid(int idx) +{ + return idx >= ARMV7_IDX_CYCLE_COUNTER && idx <= ARMV7_IDX_COUNTER_LAST; +} + +static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx) { int ret = 0; + u32 counter; - if (counter == ARMV7_CYCLE_COUNTER) - ret = pmnc & ARMV7_FLAG_C; - else if ((counter >= ARMV7_COUNTER0) && (counter <= ARMV7_COUNTER_LAST)) - ret = pmnc & ARMV7_FLAG_P(counter); - else + if (!armv7_pmnc_counter_valid(idx)) { pr_err("CPU%u checking wrong counter %d overflow status\n", - smp_processor_id(), counter); + smp_processor_id(), idx); + } else { + counter = ARMV7_IDX_TO_COUNTER(idx); + ret = pmnc & BIT(counter); + } return ret; } static inline int armv7_pmnc_select_counter(int idx) { - u32 val; + u32 counter; - if ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST)) { - pr_err("CPU%u selecting wrong PMNC counter" - " %d\n", smp_processor_id(), idx); - return -1; + if (!armv7_pmnc_counter_valid(idx)) { + pr_err("CPU%u selecting wrong PMNC counter %d\n", + smp_processor_id(), idx); + return -EINVAL; } - val = (idx - ARMV7_EVENT_CNT_TO_CNTx) & ARMV7_SELECT_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + counter = ARMV7_IDX_TO_COUNTER(idx); + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter)); isb(); return idx; @@ -814,30 +780,26 @@ static inline u32 armv7pmu_read_counter(int idx) { u32 value = 0; - if (idx == ARMV7_CYCLE_COUNTER) - asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); - else if ((idx >= ARMV7_COUNTER0) && (idx <= ARMV7_COUNTER_LAST)) { - if (armv7_pmnc_select_counter(idx) == idx) - asm volatile("mrc p15, 0, %0, c9, c13, 2" - : "=r" (value)); - } else + if (!armv7_pmnc_counter_valid(idx)) pr_err("CPU%u reading wrong counter %d\n", smp_processor_id(), idx); + else if (idx == ARMV7_IDX_CYCLE_COUNTER) + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); + else if (armv7_pmnc_select_counter(idx) == idx) + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value)); return value; } static inline void armv7pmu_write_counter(int idx, u32 value) { - if (idx == ARMV7_CYCLE_COUNTER) - asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value)); - else if ((idx >= ARMV7_COUNTER0) && (idx <= ARMV7_COUNTER_LAST)) { - if (armv7_pmnc_select_counter(idx) == idx) - asm volatile("mcr p15, 0, %0, c9, c13, 2" - : : "r" (value)); - } else + if (!armv7_pmnc_counter_valid(idx)) pr_err("CPU%u writing wrong counter %d\n", smp_processor_id(), idx); + else if (idx == ARMV7_IDX_CYCLE_COUNTER) + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value)); + else if (armv7_pmnc_select_counter(idx) == idx) + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value)); } static inline void armv7_pmnc_write_evtsel(int idx, u32 val) @@ -850,86 +812,61 @@ static inline void armv7_pmnc_write_evtsel(int idx, u32 val) static inline int armv7_pmnc_enable_counter(int idx) { - u32 val; + u32 counter; - if ((idx != ARMV7_CYCLE_COUNTER) && - ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { - pr_err("CPU%u enabling wrong PMNC counter" - " %d\n", smp_processor_id(), idx); - return -1; + if (!armv7_pmnc_counter_valid(idx)) { + pr_err("CPU%u enabling wrong PMNC counter %d\n", + smp_processor_id(), idx); + return -EINVAL; } - if (idx == ARMV7_CYCLE_COUNTER) - val = ARMV7_CNTENS_C; - else - val = ARMV7_CNTENS_P(idx); - - asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); - + counter = ARMV7_IDX_TO_COUNTER(idx); + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter))); return idx; } static inline int armv7_pmnc_disable_counter(int idx) { - u32 val; + u32 counter; - - if ((idx != ARMV7_CYCLE_COUNTER) && - ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { - pr_err("CPU%u disabling wrong PMNC counter" - " %d\n", smp_processor_id(), idx); - return -1; + if (!armv7_pmnc_counter_valid(idx)) { + pr_err("CPU%u disabling wrong PMNC counter %d\n", + smp_processor_id(), idx); + return -EINVAL; } - if (idx == ARMV7_CYCLE_COUNTER) - val = ARMV7_CNTENC_C; - else - val = ARMV7_CNTENC_P(idx); - - asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); - + counter = ARMV7_IDX_TO_COUNTER(idx); + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter))); return idx; } static inline int armv7_pmnc_enable_intens(int idx) { - u32 val; + u32 counter; - if ((idx != ARMV7_CYCLE_COUNTER) && - ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { - pr_err("CPU%u enabling wrong PMNC counter" - " interrupt enable %d\n", smp_processor_id(), idx); - return -1; + if (!armv7_pmnc_counter_valid(idx)) { + pr_err("CPU%u enabling wrong PMNC counter IRQ enable %d\n", + smp_processor_id(), idx); + return -EINVAL; } - if (idx == ARMV7_CYCLE_COUNTER) - val = ARMV7_INTENS_C; - else - val = ARMV7_INTENS_P(idx); - - asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val)); - + counter = ARMV7_IDX_TO_COUNTER(idx); + asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter))); return idx; } static inline int armv7_pmnc_disable_intens(int idx) { - u32 val; + u32 counter; - if ((idx != ARMV7_CYCLE_COUNTER) && - ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { - pr_err("CPU%u disabling wrong PMNC counter" - " interrupt enable %d\n", smp_processor_id(), idx); - return -1; + if (!armv7_pmnc_counter_valid(idx)) { + pr_err("CPU%u disabling wrong PMNC counter IRQ enable %d\n", + smp_processor_id(), idx); + return -EINVAL; } - if (idx == ARMV7_CYCLE_COUNTER) - val = ARMV7_INTENC_C; - else - val = ARMV7_INTENC_P(idx); - - asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val)); - + counter = ARMV7_IDX_TO_COUNTER(idx); + asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter))); return idx; } @@ -973,14 +910,14 @@ static void armv7_pmnc_dump_regs(void) asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); printk(KERN_INFO "CCNT =0x%08x\n", val); - for (cnt = ARMV7_COUNTER0; cnt < ARMV7_COUNTER_LAST; cnt++) { + for (cnt = ARMV7_IDX_COUNTER0; cnt <= ARMV7_IDX_COUNTER_LAST; cnt++) { armv7_pmnc_select_counter(cnt); asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); printk(KERN_INFO "CNT[%d] count =0x%08x\n", - cnt-ARMV7_EVENT_CNT_TO_CNTx, val); + ARMV7_IDX_TO_COUNTER(cnt), val); asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val)); printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", - cnt-ARMV7_EVENT_CNT_TO_CNTx, val); + ARMV7_IDX_TO_COUNTER(cnt), val); } } #endif @@ -1004,7 +941,7 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) * Set event (if destined for PMNx counters) * We don't need to set the event if it's a cycle count */ - if (idx != ARMV7_CYCLE_COUNTER) + if (idx != ARMV7_IDX_CYCLE_COUNTER) armv7_pmnc_write_evtsel(idx, hwc->config_base); /* @@ -1069,7 +1006,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx <= armpmu->num_events; ++idx) { + for (idx = 0; idx < armpmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; @@ -1132,23 +1069,23 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, /* Always place a cycle counter into the cycle counter. */ if (event->config_base == ARMV7_PERFCTR_CPU_CYCLES) { - if (test_and_set_bit(ARMV7_CYCLE_COUNTER, cpuc->used_mask)) + if (test_and_set_bit(ARMV7_IDX_CYCLE_COUNTER, cpuc->used_mask)) return -EAGAIN; - return ARMV7_CYCLE_COUNTER; - } else { - /* - * For anything other than a cycle counter, try and use - * the events counters - */ - for (idx = ARMV7_COUNTER0; idx <= armpmu->num_events; ++idx) { - if (!test_and_set_bit(idx, cpuc->used_mask)) - return idx; - } + return ARMV7_IDX_CYCLE_COUNTER; + } - /* The counters are all in use. */ - return -EAGAIN; + /* + * For anything other than a cycle counter, try and use + * the events counters + */ + for (idx = ARMV7_IDX_COUNTER0; idx < armpmu->num_events; ++idx) { + if (!test_and_set_bit(idx, cpuc->used_mask)) + return idx; } + + /* The counters are all in use. */ + return -EAGAIN; } static void armv7pmu_reset(void *info) @@ -1156,7 +1093,7 @@ static void armv7pmu_reset(void *info) u32 idx, nb_cnt = armpmu->num_events; /* The counter and interrupt enable registers are unknown at reset. */ - for (idx = 1; idx < nb_cnt; ++idx) + for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) armv7pmu_disable_event(NULL, idx); /* Initialize & Reset PMNC: C and P bits */ -- cgit v0.10.2 From d2b41f7456223ba6abd3b38d7b54be97914f3aa5 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 19 Jul 2011 22:41:36 +0100 Subject: ARM: perf: index Xscale and ARMv6 event counters starting from zero Now that the ARMv7 PMU backend indexes event counters from zero, follow suit and do the same for ARMv6 and Xscale. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Mark Rutland Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index d1abe97..87f29b5 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -54,7 +54,7 @@ enum armv6_perf_types { }; enum armv6_counters { - ARMV6_CYCLE_COUNTER = 1, + ARMV6_CYCLE_COUNTER = 0, ARMV6_COUNTER0, ARMV6_COUNTER1, }; @@ -487,7 +487,7 @@ armv6pmu_handle_irq(int irq_num, perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx <= armpmu->num_events; ++idx) { + for (idx = 0; idx < armpmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 730b1c2..54312fc 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -40,7 +40,7 @@ enum xscale_perf_types { }; enum xscale_counters { - XSCALE_CYCLE_COUNTER = 1, + XSCALE_CYCLE_COUNTER = 0, XSCALE_COUNTER0, XSCALE_COUNTER1, XSCALE_COUNTER2, @@ -249,7 +249,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx <= armpmu->num_events; ++idx) { + for (idx = 0; idx < armpmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; @@ -581,7 +581,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx <= armpmu->num_events; ++idx) { + for (idx = 0; idx < armpmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; -- cgit v0.10.2 From ecf5a893211c26e02b9d4cfd6ba2183473ac0203 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 19 Jul 2011 22:43:28 +0100 Subject: ARM: perf: index PMU registers from zero ARM PMU code used to use 1-based indices for PMU registers. This caused several data structures (pmu_hw_events::{active_events, used_mask, events}) to have an unused element at index zero. ARMPMU_MAX_HWEVENTS still takes this indexing into account, and currently equates to 33. This patch updates the core ARM perf code to use the 0th index again. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Mark Rutland Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index d507fe1..c668c91 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -35,7 +35,7 @@ static struct platform_device *pmu_device; static DEFINE_RAW_SPINLOCK(pmu_lock); /* - * ARMv6 supports a maximum of 3 events, starting from index 1. If we add + * ARMv6 supports a maximum of 3 events, starting from index 0. If we add * another platform that supports more, we need to increase this to be the * largest of all platforms. * @@ -43,13 +43,12 @@ static DEFINE_RAW_SPINLOCK(pmu_lock); * cycle counter CCNT + 31 events counters CNT0..30. * Cortex-A8 has 1+4 counters, Cortex-A9 has 1+6 counters. */ -#define ARMPMU_MAX_HWEVENTS 33 +#define ARMPMU_MAX_HWEVENTS 32 /* The events for a given CPU. */ struct cpu_hw_events { /* - * The events that are active on the CPU for the given index. Index 0 - * is reserved. + * The events that are active on the CPU for the given index. */ struct perf_event *events[ARMPMU_MAX_HWEVENTS]; @@ -597,7 +596,7 @@ static void armpmu_enable(struct pmu *pmu) if (!armpmu) return; - for (idx = 0; idx <= armpmu->num_events; ++idx) { + for (idx = 0; idx < armpmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; if (!event) -- cgit v0.10.2 From 05d22fde3c0b86c8395d8f12ac01fbbc524d73ca Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 19 Jul 2011 11:57:30 +0100 Subject: ARM: perf: allow armpmu to implement mode exclusion Modern PMUs allow for mode exclusion, so we no longer wish to return -EPERM if it is requested. This patch provides a hook in the armpmu structure for implementing mode exclusion. The hw_perf_event initialisation is slightly delayed so that the backend code can update the structure if required. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index c668c91..5d60c9c 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -75,6 +75,8 @@ struct arm_pmu { void (*disable)(struct hw_perf_event *evt, int idx); int (*get_event_idx)(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc); + int (*set_event_filter)(struct hw_perf_event *evt, + struct perf_event_attr *attr); u32 (*read_counter)(int idx); void (*write_counter)(int idx, u32 val); void (*start)(void); @@ -478,6 +480,13 @@ hw_perf_event_destroy(struct perf_event *event) } static int +event_requires_mode_exclusion(struct perf_event_attr *attr) +{ + return attr->exclude_idle || attr->exclude_user || + attr->exclude_kernel || attr->exclude_hv; +} + +static int __hw_perf_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -502,34 +511,31 @@ __hw_perf_event_init(struct perf_event *event) } /* + * We don't assign an index until we actually place the event onto + * hardware. Use -1 to signify that we haven't decided where to put it + * yet. For SMP systems, each core has it's own PMU so we can't do any + * clever allocation or constraints checking at this point. + */ + hwc->idx = -1; + hwc->config_base = 0; + hwc->config = 0; + hwc->event_base = 0; + + /* * Check whether we need to exclude the counter from certain modes. - * The ARM performance counters are on all of the time so if someone - * has asked us for some excludes then we have to fail. */ - if (event->attr.exclude_kernel || event->attr.exclude_user || - event->attr.exclude_hv || event->attr.exclude_idle) { + if ((!armpmu->set_event_filter || + armpmu->set_event_filter(hwc, &event->attr)) && + event_requires_mode_exclusion(&event->attr)) { pr_debug("ARM performance counters do not support " "mode exclusion\n"); return -EPERM; } /* - * We don't assign an index until we actually place the event onto - * hardware. Use -1 to signify that we haven't decided where to put it - * yet. For SMP systems, each core has it's own PMU so we can't do any - * clever allocation or constraints checking at this point. - */ - hwc->idx = -1; - - /* - * Store the event encoding into the config_base field. config and - * event_base are unused as the only 2 things we need to know are - * the event mapping and the counter to use. The counter to use is - * also the indx and the config_base is the event type. + * Store the event encoding into the config_base field. */ - hwc->config_base = (unsigned long)mapping; - hwc->config = 0; - hwc->event_base = 0; + hwc->config_base |= (unsigned long)mapping; if (!hwc->sample_period) { hwc->sample_period = armpmu->max_period; -- cgit v0.10.2 From a505addc366525cd8d9358298be0dc8655be5953 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 19 Jul 2011 13:53:36 +0100 Subject: ARM: perf: add mode exclusion for Cortex-A15 PMU The Cortex-A15 PMU implements the PMUv2 specification and therefore has support for some mode exclusion. This patch adds support for excluding user, kernel and hypervisor counts from a given event. Acked-by: Jamie Iles Reviewed-by: Jean Pihet Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 0934c82..fe6c931 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -17,6 +17,9 @@ */ #ifdef CONFIG_CPU_V7 + +static struct arm_pmu armv7pmu; + /* * Common ARMv7 event types * @@ -709,16 +712,24 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] #define ARMV7_PMNC_MASK 0x3f /* Mask for writable bits */ /* - * EVTSEL: Event selection reg - */ -#define ARMV7_EVTSEL_MASK 0xff /* Mask for writable bits */ - -/* * FLAG: counters overflow flag status reg */ #define ARMV7_FLAG_MASK 0xffffffff /* Mask for writable bits */ #define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK +/* + * PMXEVTYPER: Event selection reg + */ +#define ARMV7_EVTYPE_MASK 0xc00000ff /* Mask for writable bits */ +#define ARMV7_EVTYPE_EVENT 0xff /* Mask for EVENT bits */ + +/* + * Event filters for PMUv2 + */ +#define ARMV7_EXCLUDE_PL1 (1 << 31) +#define ARMV7_EXCLUDE_USER (1 << 30) +#define ARMV7_INCLUDE_HYP (1 << 27) + static inline u32 armv7_pmnc_read(void) { u32 val; @@ -805,7 +816,7 @@ static inline void armv7pmu_write_counter(int idx, u32 value) static inline void armv7_pmnc_write_evtsel(int idx, u32 val) { if (armv7_pmnc_select_counter(idx) == idx) { - val &= ARMV7_EVTSEL_MASK; + val &= ARMV7_EVTYPE_MASK; asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); } } @@ -939,9 +950,10 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) /* * Set event (if destined for PMNx counters) - * We don't need to set the event if it's a cycle count + * We only need to set the event for the cycle counter if we + * have the ability to perform event filtering. */ - if (idx != ARMV7_IDX_CYCLE_COUNTER) + if (armv7pmu.set_event_filter || idx != ARMV7_IDX_CYCLE_COUNTER) armv7_pmnc_write_evtsel(idx, hwc->config_base); /* @@ -1066,9 +1078,10 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, struct hw_perf_event *event) { int idx; + unsigned long evtype = event->config_base & ARMV7_EVTYPE_EVENT; /* Always place a cycle counter into the cycle counter. */ - if (event->config_base == ARMV7_PERFCTR_CPU_CYCLES) { + if (evtype == ARMV7_PERFCTR_CPU_CYCLES) { if (test_and_set_bit(ARMV7_IDX_CYCLE_COUNTER, cpuc->used_mask)) return -EAGAIN; @@ -1088,6 +1101,32 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, return -EAGAIN; } +/* + * Add an event filter to a given event. This will only work for PMUv2 PMUs. + */ +static int armv7pmu_set_event_filter(struct hw_perf_event *event, + struct perf_event_attr *attr) +{ + unsigned long config_base = 0; + + if (attr->exclude_idle) + return -EPERM; + if (attr->exclude_user) + config_base |= ARMV7_EXCLUDE_USER; + if (attr->exclude_kernel) + config_base |= ARMV7_EXCLUDE_PL1; + if (!attr->exclude_hv) + config_base |= ARMV7_INCLUDE_HYP; + + /* + * Install the filter into config_base as this is used to + * construct the event type. + */ + event->config_base = config_base; + + return 0; +} + static void armv7pmu_reset(void *info) { u32 idx, nb_cnt = armpmu->num_events; @@ -1162,6 +1201,7 @@ static struct arm_pmu *__init armv7_a15_pmu_init(void) armv7pmu.cache_map = &armv7_a15_perf_cache_map; armv7pmu.event_map = &armv7_a15_perf_map; armv7pmu.num_events = armv7_read_num_pmnc_events(); + armv7pmu.set_event_filter = armv7pmu_set_event_filter; return &armv7pmu; } #else -- cgit v0.10.2 From 5f12a761937373d2f9b557d7519e6f1cf738b8f0 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 11 Aug 2011 12:31:20 +0100 Subject: perf: provide PMU when initing events Currently, an event's 'pmu' field is set after pmu::event_init() is called. This means that pmu::event_init() must figure out which struct pmu the event was initialised from. This makes it difficult to consolidate common event initialisation code for similar PMUs, and very difficult to implement drivers for PMUs which can have multiple instances (e.g. a USB controller PMU, a GPU PMU, etc). This patch sets the 'pmu' field before initialising the event, allowing event init code to identify the struct pmu instance easily. In the event of failure to initialise an event, the event is destroyed via kfree() without calling perf_event::destroy(), so this shouldn't result in bad behaviour even if the destroy field was set before failure to initialise was noted. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1313062280-19123-1-git-send-email-mark.rutland@arm.com Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index b8785e2..68c8017 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5715,6 +5715,7 @@ struct pmu *perf_init_event(struct perf_event *event) pmu = idr_find(&pmu_idr, event->attr.type); rcu_read_unlock(); if (pmu) { + event->pmu = pmu; ret = pmu->event_init(event); if (ret) pmu = ERR_PTR(ret); @@ -5722,6 +5723,7 @@ struct pmu *perf_init_event(struct perf_event *event) } list_for_each_entry_rcu(pmu, &pmus, entry) { + event->pmu = pmu; ret = pmu->event_init(event); if (!ret) goto unlock; @@ -5848,8 +5850,6 @@ done: return ERR_PTR(err); } - event->pmu = pmu; - if (!event->parent) { if (event->attach_state & PERF_ATTACH_TASK) jump_label_inc(&perf_sched_events); -- cgit v0.10.2 From 48957155f8791964d8567479e6986f88343aba38 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 27 Apr 2011 10:31:51 +0100 Subject: ARM: perf: only register a CPU PMU when present Currently, an "empty" struct pmu is registered as the CPU PMU, regardless of whether there is a physical PMU. This burdens the accessor functions with checks to see whether a PMU is actually present. This patch changes initialisation to register a PMU only if there is a supported PMU present, and removes the checks that this change makes redundant. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 5d60c9c..753648c 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -250,9 +250,6 @@ armpmu_stop(struct perf_event *event, int flags) { struct hw_perf_event *hwc = &event->hw; - if (!armpmu) - return; - /* * ARM pmu always has to update the counter, so ignore * PERF_EF_UPDATE, see comments in armpmu_start(). @@ -270,9 +267,6 @@ armpmu_start(struct perf_event *event, int flags) { struct hw_perf_event *hwc = &event->hw; - if (!armpmu) - return; - /* * ARM pmu always has to reprogram the period, so ignore * PERF_EF_RELOAD, see the comment below. @@ -567,9 +561,6 @@ static int armpmu_event_init(struct perf_event *event) return -ENOENT; } - if (!armpmu) - return -ENODEV; - event->destroy = hw_perf_event_destroy; if (!atomic_inc_not_zero(&active_events)) { @@ -599,9 +590,6 @@ static void armpmu_enable(struct pmu *pmu) int idx, enabled = 0; struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - if (!armpmu) - return; - for (idx = 0; idx < armpmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; @@ -618,8 +606,7 @@ static void armpmu_enable(struct pmu *pmu) static void armpmu_disable(struct pmu *pmu) { - if (armpmu) - armpmu->stop(); + armpmu->stop(); } static struct pmu pmu = { @@ -738,12 +725,11 @@ init_hw_perf_events(void) if (armpmu) { pr_info("enabled with %s PMU driver, %d counters available\n", armpmu->name, armpmu->num_events); + perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW); } else { pr_info("no hardware support available\n"); } - perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW); - return 0; } early_initcall(init_hw_perf_events); -- cgit v0.10.2 From 7b9f72c62ed047a200b1ef8c70bee0b58e880af8 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 27 Apr 2011 16:22:21 +0100 Subject: ARM: perf: clean up event group validation Currently, event group validation compares each event's 'pmu' pointer against the static 'pmu' pointer. This limits the code to supporting only 1 PMU. This patch changes the behaviour to consider an event's group leader's 'pmu' pointer as canonical for validation. This should ease later generalisation of the code to support multiple PMUs at once. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 753648c..dfde928 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -348,8 +348,9 @@ validate_event(struct cpu_hw_events *cpuc, struct perf_event *event) { struct hw_perf_event fake_event = event->hw; + struct pmu *leader_pmu = event->group_leader->pmu; - if (event->pmu != &pmu || event->state <= PERF_EVENT_STATE_OFF) + if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF) return 1; return armpmu->get_event_idx(cpuc, &fake_event) >= 0; -- cgit v0.10.2 From c47f8684baefa2bf52c4320f894e73db08dc8a0a Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 19 Jul 2011 09:37:10 +0100 Subject: ARM: perf: remove active_mask Currently, pmu_hw_events::active_mask is used to keep track of which events are active in hardware. As we can stop counters and their interrupts, this is unnecessary. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index dfde928..438482f 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -57,12 +57,6 @@ struct cpu_hw_events { * an event. A 0 means that the counter can be used. */ unsigned long used_mask[BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)]; - - /* - * A 1 bit for an index indicates that the counter is actively being - * used. - */ - unsigned long active_mask[BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)]; }; static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); @@ -295,7 +289,6 @@ armpmu_del(struct perf_event *event, int flags) WARN_ON(idx < 0); - clear_bit(idx, cpuc->active_mask); armpmu_stop(event, PERF_EF_UPDATE); cpuc->events[idx] = NULL; clear_bit(idx, cpuc->used_mask); @@ -327,7 +320,6 @@ armpmu_add(struct perf_event *event, int flags) event->hw.idx = idx; armpmu->disable(hwc, idx); cpuc->events[idx] = event; - set_bit(idx, cpuc->active_mask); hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; if (flags & PERF_EF_START) diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index 87f29b5..8390128 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -462,6 +462,23 @@ armv6pmu_enable_event(struct hw_perf_event *hwc, raw_spin_unlock_irqrestore(&pmu_lock, flags); } +static int counter_is_active(unsigned long pmcr, int idx) +{ + unsigned long mask = 0; + if (idx == ARMV6_CYCLE_COUNTER) + mask = ARMV6_PMCR_CCOUNT_IEN; + else if (idx == ARMV6_COUNTER0) + mask = ARMV6_PMCR_COUNT0_IEN; + else if (idx == ARMV6_COUNTER1) + mask = ARMV6_PMCR_COUNT1_IEN; + + if (mask) + return pmcr & mask; + + WARN_ONCE(1, "invalid counter number (%d)\n", idx); + return 0; +} + static irqreturn_t armv6pmu_handle_irq(int irq_num, void *dev) @@ -491,7 +508,7 @@ armv6pmu_handle_irq(int irq_num, struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; - if (!test_bit(idx, cpuc->active_mask)) + if (!counter_is_active(pmcr, idx)) continue; /* diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index fe6c931..f4170fc 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1022,9 +1022,6 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; - if (!test_bit(idx, cpuc->active_mask)) - continue; - /* * We have a single interrupt for all counters. Check that * each counter has overflowed before we process it. diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 54312fc..ca89a06 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -253,9 +253,6 @@ xscale1pmu_handle_irq(int irq_num, void *dev) struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; - if (!test_bit(idx, cpuc->active_mask)) - continue; - if (!xscale1_pmnc_counter_has_overflowed(pmnc, idx)) continue; @@ -585,9 +582,6 @@ xscale2pmu_handle_irq(int irq_num, void *dev) struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; - if (!test_bit(idx, cpuc->active_mask)) - continue; - if (!xscale2_pmnc_counter_has_overflowed(pmnc, idx)) continue; -- cgit v0.10.2 From 03b7898d300de62078cc130fbc83b84b1d1e0f8d Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 27 Apr 2011 11:20:11 +0100 Subject: ARM: perf: move active_events into struct arm_pmu This patch moves the active_events counter into struct arm_pmu, in preparation for supporting multiple PMUs. This also moves pmu_reserve_mutex, as it is used to guard accesses to active_events. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 438482f..9874395 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -82,6 +82,8 @@ struct arm_pmu { const unsigned (*event_map)[PERF_COUNT_HW_MAX]; u32 raw_event_mask; int num_events; + atomic_t active_events; + struct mutex reserve_mutex; u64 max_period; }; @@ -454,15 +456,15 @@ armpmu_reserve_hardware(void) return 0; } -static atomic_t active_events = ATOMIC_INIT(0); -static DEFINE_MUTEX(pmu_reserve_mutex); - static void hw_perf_event_destroy(struct perf_event *event) { - if (atomic_dec_and_mutex_lock(&active_events, &pmu_reserve_mutex)) { + atomic_t *active_events = &armpmu->active_events; + struct mutex *pmu_reserve_mutex = &armpmu->reserve_mutex; + + if (atomic_dec_and_mutex_lock(active_events, pmu_reserve_mutex)) { armpmu_release_hardware(); - mutex_unlock(&pmu_reserve_mutex); + mutex_unlock(pmu_reserve_mutex); } } @@ -543,6 +545,7 @@ __hw_perf_event_init(struct perf_event *event) static int armpmu_event_init(struct perf_event *event) { int err = 0; + atomic_t *active_events = &armpmu->active_events; switch (event->attr.type) { case PERF_TYPE_RAW: @@ -556,15 +559,14 @@ static int armpmu_event_init(struct perf_event *event) event->destroy = hw_perf_event_destroy; - if (!atomic_inc_not_zero(&active_events)) { - mutex_lock(&pmu_reserve_mutex); - if (atomic_read(&active_events) == 0) { + if (!atomic_inc_not_zero(active_events)) { + mutex_lock(&armpmu->reserve_mutex); + if (atomic_read(active_events) == 0) err = armpmu_reserve_hardware(); - } if (!err) - atomic_inc(&active_events); - mutex_unlock(&pmu_reserve_mutex); + atomic_inc(active_events); + mutex_unlock(&armpmu->reserve_mutex); } if (err) @@ -613,6 +615,12 @@ static struct pmu pmu = { .read = armpmu_read, }; +static void __init armpmu_init(struct arm_pmu *armpmu) +{ + atomic_set(&armpmu->active_events, 0); + mutex_init(&armpmu->reserve_mutex); +} + /* Include the PMU-specific implementations. */ #include "perf_event_xscale.c" #include "perf_event_v6.c" @@ -718,6 +726,7 @@ init_hw_perf_events(void) if (armpmu) { pr_info("enabled with %s PMU driver, %d counters available\n", armpmu->name, armpmu->num_events); + armpmu_init(armpmu); perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW); } else { pr_info("no hardware support available\n"); -- cgit v0.10.2 From a9356a04fab912289b886824cb4b1d461987a910 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 4 May 2011 09:23:15 +0100 Subject: ARM: perf: move platform device to struct arm_pmu Currently the ARM perf code supports having a single struct platform_device to supply IRQ numbers, limiting it to supporting a single PMU. This patch makes a platform_device instance variable on struct arm_pmu. This should allow for multiple PMUs to be supported in future. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 9874395..1d648d1 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -26,8 +26,6 @@ #include #include -static struct platform_device *pmu_device; - /* * Hardware lock to serialize accesses to PMU registers. Needed for the * read/modify/write sequences. @@ -85,6 +83,7 @@ struct arm_pmu { atomic_t active_events; struct mutex reserve_mutex; u64 max_period; + struct platform_device *plat_device; }; /* Set at runtime when we know what CPU type we are. */ @@ -374,7 +373,8 @@ validate_group(struct perf_event *event) static irqreturn_t armpmu_platform_irq(int irq, void *dev) { - struct arm_pmu_platdata *plat = dev_get_platdata(&pmu_device->dev); + struct platform_device *plat_device = armpmu->plat_device; + struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); return plat->handle_irq(irq, dev, armpmu->handle_irq); } @@ -383,6 +383,7 @@ static void armpmu_release_hardware(void) { int i, irq, irqs; + struct platform_device *pmu_device = armpmu->plat_device; irqs = min(pmu_device->num_resources, num_possible_cpus()); @@ -404,6 +405,7 @@ armpmu_reserve_hardware(void) struct arm_pmu_platdata *plat; irq_handler_t handle_irq; int i, err, irq, irqs; + struct platform_device *pmu_device = armpmu->plat_device; err = reserve_pmu(ARM_PMU_DEVICE_CPU); if (err) { @@ -657,7 +659,7 @@ static struct platform_device_id armpmu_plat_device_ids[] = { static int __devinit armpmu_device_probe(struct platform_device *pdev) { - pmu_device = pdev; + armpmu->plat_device = pdev; return 0; } -- cgit v0.10.2 From 92f701e1f429e007f9619469d548022061c41ecc Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 4 May 2011 09:23:51 +0100 Subject: ARM: perf: indirect access to cpu_hw_events Currently, cpu_hw_events is a global per-CPU variable. To enable support for multiple PMUs, there needs to be a mapping from an instance of arm_pmu to its cpu_hw_events. Additionally, as system PMUs are not CPU-affine, they should not have this stored per-CPU. This patch moves access to the hardware events data behind an accessor function (arm_pmu::get_hw_events). This allows each instance to have its own hardware event data, which can be stored per-CPU or globally as required. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 1d648d1..9d6ac99 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -84,6 +84,7 @@ struct arm_pmu { struct mutex reserve_mutex; u64 max_period; struct platform_device *plat_device; + struct cpu_hw_events *(*get_hw_events)(void); }; /* Set at runtime when we know what CPU type we are. */ @@ -284,7 +285,7 @@ armpmu_start(struct perf_event *event, int flags) static void armpmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = armpmu->get_hw_events(); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -300,7 +301,7 @@ armpmu_del(struct perf_event *event, int flags) static int armpmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = armpmu->get_hw_events(); struct hw_perf_event *hwc = &event->hw; int idx; int err = 0; @@ -585,7 +586,7 @@ static void armpmu_enable(struct pmu *pmu) { /* Enable all of the perf events on hardware. */ int idx, enabled = 0; - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = armpmu->get_hw_events(); for (idx = 0; idx < armpmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; @@ -678,6 +679,16 @@ static int __init register_pmu_driver(void) } device_initcall(register_pmu_driver); +static struct cpu_hw_events *armpmu_get_cpu_events(void) +{ + return &__get_cpu_var(cpu_hw_events); +} + +static void __init cpu_pmu_init(struct arm_pmu *armpmu) +{ + armpmu->get_hw_events = armpmu_get_cpu_events; +} + /* * CPU PMU identification and registration. */ @@ -728,6 +739,7 @@ init_hw_perf_events(void) if (armpmu) { pr_info("enabled with %s PMU driver, %d counters available\n", armpmu->name, armpmu->num_events); + cpu_pmu_init(armpmu); armpmu_init(armpmu); perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW); } else { -- cgit v0.10.2 From 1b69beb7684c79673995607939d8acab51056b63 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 8 Aug 2011 14:56:02 +0100 Subject: ARM: perf: remove unnecessary armpmu->stop As armpmu_disable will call armpmu->stop when the last event has been removed, this is pointless and simply adds to the noise when debugging. Additionally, due to this call occurring in a preemptible context, this is problematic for per-cpu locking of PMU registers (where we will attempt to access per-cpu spinlock for use with raw_spin_lock_irqsave). This patch removes the call to armpmu->stop. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 9d6ac99..5ce6c33 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -396,7 +396,6 @@ armpmu_release_hardware(void) free_irq(irq, NULL); } - armpmu->stop(); release_pmu(ARM_PMU_DEVICE_CPU); } -- cgit v0.10.2 From 0f78d2d5ccf72ec834da6901886a40fd8e3b7615 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 28 Apr 2011 10:17:04 +0100 Subject: ARM: perf: lock PMU registers per-CPU Currently, a single lock serialises access to CPU PMU registers. This global locking is unnecessary as PMU registers are local to the CPU they monitor. This patch replaces the global lock with a per-CPU lock. As the lock is in struct cpu_hw_events, PMUs providing a single cpu_hw_events instance can be locked globally. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 5ce6c33..9331d57 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -27,12 +27,6 @@ #include /* - * Hardware lock to serialize accesses to PMU registers. Needed for the - * read/modify/write sequences. - */ -static DEFINE_RAW_SPINLOCK(pmu_lock); - -/* * ARMv6 supports a maximum of 3 events, starting from index 0. If we add * another platform that supports more, we need to increase this to be the * largest of all platforms. @@ -55,6 +49,12 @@ struct cpu_hw_events { * an event. A 0 means that the counter can be used. */ unsigned long used_mask[BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)]; + + /* + * Hardware lock to serialize accesses to PMU registers. Needed for the + * read/modify/write sequences. + */ + raw_spinlock_t pmu_lock; }; static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); @@ -685,6 +685,11 @@ static struct cpu_hw_events *armpmu_get_cpu_events(void) static void __init cpu_pmu_init(struct arm_pmu *armpmu) { + int cpu; + for_each_possible_cpu(cpu) { + struct cpu_hw_events *events = &per_cpu(cpu_hw_events, cpu); + raw_spin_lock_init(&events->pmu_lock); + } armpmu->get_hw_events = armpmu_get_cpu_events; } diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index 8390128..68cf704 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -433,6 +433,7 @@ armv6pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); if (ARMV6_CYCLE_COUNTER == idx) { mask = 0; @@ -454,12 +455,12 @@ armv6pmu_enable_event(struct hw_perf_event *hwc, * Mask out the current event and set the counter to count the event * that we're interested in. */ - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = armv6_pmcr_read(); val &= ~mask; val |= evt; armv6_pmcr_write(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static int counter_is_active(unsigned long pmcr, int idx) @@ -544,24 +545,26 @@ static void armv6pmu_start(void) { unsigned long flags, val; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = armv6_pmcr_read(); val |= ARMV6_PMCR_ENABLE; armv6_pmcr_write(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void armv6pmu_stop(void) { unsigned long flags, val; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = armv6_pmcr_read(); val &= ~ARMV6_PMCR_ENABLE; armv6_pmcr_write(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static int @@ -595,6 +598,7 @@ armv6pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); if (ARMV6_CYCLE_COUNTER == idx) { mask = ARMV6_PMCR_CCOUNT_IEN; @@ -615,12 +619,12 @@ armv6pmu_disable_event(struct hw_perf_event *hwc, * of ETM bus signal assertion cycles. The external reporting should * be disabled and so this should never increment. */ - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = armv6_pmcr_read(); val &= ~mask; val |= evt; armv6_pmcr_write(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void @@ -628,6 +632,7 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, flags, evt = 0; + struct cpu_hw_events *events = armpmu->get_hw_events(); if (ARMV6_CYCLE_COUNTER == idx) { mask = ARMV6_PMCR_CCOUNT_IEN; @@ -644,12 +649,12 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, * Unlike UP ARMv6, we don't have a way of stopping the counters. We * simply disable the interrupt reporting. */ - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = armv6_pmcr_read(); val &= ~mask; val |= evt; armv6_pmcr_write(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static struct arm_pmu armv6pmu = { diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index f4170fc..68ac522 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -936,12 +936,13 @@ static void armv7_pmnc_dump_regs(void) static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); /* * Enable counter and interrupt, and set the counter to count * the event that we're interested in. */ - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); /* * Disable counter @@ -966,17 +967,18 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) */ armv7_pmnc_enable_counter(idx); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); /* * Disable counter and interrupt */ - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); /* * Disable counter @@ -988,7 +990,7 @@ static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx) */ armv7_pmnc_disable_intens(idx); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) @@ -1054,21 +1056,23 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) static void armv7pmu_start(void) { unsigned long flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); /* Enable all counters */ armv7_pmnc_write(armv7_pmnc_read() | ARMV7_PMNC_E); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void armv7pmu_stop(void) { unsigned long flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); /* Disable all counters */ armv7_pmnc_write(armv7_pmnc_read() & ~ARMV7_PMNC_E); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index ca89a06..18e4823 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -281,6 +281,7 @@ static void xscale1pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); switch (idx) { case XSCALE_CYCLE_COUNTER: @@ -302,18 +303,19 @@ xscale1pmu_enable_event(struct hw_perf_event *hwc, int idx) return; } - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale1pmu_read_pmnc(); val &= ~mask; val |= evt; xscale1pmu_write_pmnc(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void xscale1pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; + struct cpu_hw_events *events = armpmu->get_hw_events(); switch (idx) { case XSCALE_CYCLE_COUNTER: @@ -333,12 +335,12 @@ xscale1pmu_disable_event(struct hw_perf_event *hwc, int idx) return; } - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale1pmu_read_pmnc(); val &= ~mask; val |= evt; xscale1pmu_write_pmnc(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static int @@ -365,24 +367,26 @@ static void xscale1pmu_start(void) { unsigned long flags, val; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale1pmu_read_pmnc(); val |= XSCALE_PMU_ENABLE; xscale1pmu_write_pmnc(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void xscale1pmu_stop(void) { unsigned long flags, val; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale1pmu_read_pmnc(); val &= ~XSCALE_PMU_ENABLE; xscale1pmu_write_pmnc(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static inline u32 @@ -610,6 +614,7 @@ static void xscale2pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags, ien, evtsel; + struct cpu_hw_events *events = armpmu->get_hw_events(); ien = xscale2pmu_read_int_enable(); evtsel = xscale2pmu_read_event_select(); @@ -643,16 +648,17 @@ xscale2pmu_enable_event(struct hw_perf_event *hwc, int idx) return; } - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); xscale2pmu_write_event_select(evtsel); xscale2pmu_write_int_enable(ien); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags, ien, evtsel; + struct cpu_hw_events *events = armpmu->get_hw_events(); ien = xscale2pmu_read_int_enable(); evtsel = xscale2pmu_read_event_select(); @@ -686,10 +692,10 @@ xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) return; } - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); xscale2pmu_write_event_select(evtsel); xscale2pmu_write_int_enable(ien); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static int @@ -712,24 +718,26 @@ static void xscale2pmu_start(void) { unsigned long flags, val; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64; val |= XSCALE_PMU_ENABLE; xscale2pmu_write_pmnc(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static void xscale2pmu_stop(void) { unsigned long flags, val; + struct cpu_hw_events *events = armpmu->get_hw_events(); - raw_spin_lock_irqsave(&pmu_lock, flags); + raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale2pmu_read_pmnc(); val &= ~XSCALE_PMU_ENABLE; xscale2pmu_write_pmnc(val); - raw_spin_unlock_irqrestore(&pmu_lock, flags); + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } static inline u32 -- cgit v0.10.2 From 7ae18a5717cbbf1879bdd5b66d7009a9958e5aef Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 6 Jun 2011 10:37:50 +0100 Subject: ARM: perf: add type field to struct arm_pmu Currently, the ARM perf code assumes all PMUs it will handle are CPU PMUs, having ARM_PMU_DEVICE_CPU hardcoded when reserving or releasing hardware. This means that currently, the ARM perf code can't support system PMUs. This patch adds a 'type' field to struct arm_pmu, which allows the code to reserve & release the hardware regardless of the PMU type. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 9331d57..1a2ebbf 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -60,6 +60,7 @@ static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); struct arm_pmu { enum arm_perf_pmu_ids id; + enum arm_pmu_type type; cpumask_t active_irqs; const char *name; irqreturn_t (*handle_irq)(int irq_num, void *dev); @@ -396,7 +397,7 @@ armpmu_release_hardware(void) free_irq(irq, NULL); } - release_pmu(ARM_PMU_DEVICE_CPU); + release_pmu(armpmu->type); } static int @@ -407,7 +408,7 @@ armpmu_reserve_hardware(void) int i, err, irq, irqs; struct platform_device *pmu_device = armpmu->plat_device; - err = reserve_pmu(ARM_PMU_DEVICE_CPU); + err = reserve_pmu(armpmu->type); if (err) { pr_warning("unable to reserve pmu\n"); return err; @@ -691,6 +692,7 @@ static void __init cpu_pmu_init(struct arm_pmu *armpmu) raw_spin_lock_init(&events->pmu_lock); } armpmu->get_hw_events = armpmu_get_cpu_events; + armpmu->type = ARM_PMU_DEVICE_CPU; } /* -- cgit v0.10.2 From e1f431b57ef9e4a68281540933fa74865cbb7a74 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 28 Apr 2011 15:47:10 +0100 Subject: ARM: perf: refactor event mapping Currently mapping an event type to a hardware configuration value depends on the data being pointed to from struct arm_pmu. These fields (cache_map, event_map, raw_event_mask) are currently specific to CPU PMUs, and do not serve the general case well. This patch replaces the event map pointers on struct arm_pmu with a new 'map_event' function pointer. Small shim functions are used to reuse the existing common code. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 1a2ebbf..b13bf23 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -75,11 +75,7 @@ struct arm_pmu { void (*start)(void); void (*stop)(void); void (*reset)(void *); - const unsigned (*cache_map)[PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX]; - const unsigned (*event_map)[PERF_COUNT_HW_MAX]; - u32 raw_event_mask; + int (*map_event)(struct perf_event *event); int num_events; atomic_t active_events; struct mutex reserve_mutex; @@ -129,7 +125,11 @@ EXPORT_SYMBOL_GPL(perf_num_counters); #define CACHE_OP_UNSUPPORTED 0xFFFF static int -armpmu_map_cache_event(u64 config) +armpmu_map_cache_event(const unsigned (*cache_map) + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX], + u64 config) { unsigned int cache_type, cache_op, cache_result, ret; @@ -145,7 +145,7 @@ armpmu_map_cache_event(u64 config) if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) return -EINVAL; - ret = (int)(*armpmu->cache_map)[cache_type][cache_op][cache_result]; + ret = (int)(*cache_map)[cache_type][cache_op][cache_result]; if (ret == CACHE_OP_UNSUPPORTED) return -ENOENT; @@ -154,16 +154,38 @@ armpmu_map_cache_event(u64 config) } static int -armpmu_map_event(u64 config) +armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) { - int mapping = (*armpmu->event_map)[config]; - return mapping == HW_OP_UNSUPPORTED ? -EOPNOTSUPP : mapping; + int mapping = (*event_map)[config]; + return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; } static int -armpmu_map_raw_event(u64 config) +armpmu_map_raw_event(u32 raw_event_mask, u64 config) { - return (int)(config & armpmu->raw_event_mask); + return (int)(config & raw_event_mask); +} + +static int map_cpu_event(struct perf_event *event, + const unsigned (*event_map)[PERF_COUNT_HW_MAX], + const unsigned (*cache_map) + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX], + u32 raw_event_mask) +{ + u64 config = event->attr.config; + + switch (event->attr.type) { + case PERF_TYPE_HARDWARE: + return armpmu_map_event(event_map, config); + case PERF_TYPE_HW_CACHE: + return armpmu_map_cache_event(cache_map, config); + case PERF_TYPE_RAW: + return armpmu_map_raw_event(raw_event_mask, config); + } + + return -ENOENT; } static int @@ -484,17 +506,7 @@ __hw_perf_event_init(struct perf_event *event) struct hw_perf_event *hwc = &event->hw; int mapping, err; - /* Decode the generic type into an ARM event identifier. */ - if (PERF_TYPE_HARDWARE == event->attr.type) { - mapping = armpmu_map_event(event->attr.config); - } else if (PERF_TYPE_HW_CACHE == event->attr.type) { - mapping = armpmu_map_cache_event(event->attr.config); - } else if (PERF_TYPE_RAW == event->attr.type) { - mapping = armpmu_map_raw_event(event->attr.config); - } else { - pr_debug("event type %x not supported\n", event->attr.type); - return -EOPNOTSUPP; - } + mapping = armpmu->map_event(event); if (mapping < 0) { pr_debug("event %x:%llx not supported\n", event->attr.type, @@ -550,15 +562,8 @@ static int armpmu_event_init(struct perf_event *event) int err = 0; atomic_t *active_events = &armpmu->active_events; - switch (event->attr.type) { - case PERF_TYPE_RAW: - case PERF_TYPE_HARDWARE: - case PERF_TYPE_HW_CACHE: - break; - - default: + if (armpmu->map_event(event) == -ENOENT) return -ENOENT; - } event->destroy = hw_perf_event_destroy; diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index 68cf704..a4c5aa9 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -657,6 +657,12 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } +static int armv6_map_event(struct perf_event *event) +{ + return map_cpu_event(event, &armv6_perf_map, + &armv6_perf_cache_map, 0xFF); +} + static struct arm_pmu armv6pmu = { .id = ARM_PERF_PMU_ID_V6, .name = "v6", @@ -668,9 +674,7 @@ static struct arm_pmu armv6pmu = { .get_event_idx = armv6pmu_get_event_idx, .start = armv6pmu_start, .stop = armv6pmu_stop, - .cache_map = &armv6_perf_cache_map, - .event_map = &armv6_perf_map, - .raw_event_mask = 0xFF, + .map_event = armv6_map_event, .num_events = 3, .max_period = (1LLU << 32) - 1, }; @@ -687,6 +691,13 @@ static struct arm_pmu *__init armv6pmu_init(void) * disable the interrupt reporting and update the event. When unthrottling we * reset the period and enable the interrupt reporting. */ + +static int armv6mpcore_map_event(struct perf_event *event) +{ + return map_cpu_event(event, &armv6mpcore_perf_map, + &armv6mpcore_perf_cache_map, 0xFF); +} + static struct arm_pmu armv6mpcore_pmu = { .id = ARM_PERF_PMU_ID_V6MP, .name = "v6mpcore", @@ -698,9 +709,7 @@ static struct arm_pmu armv6mpcore_pmu = { .get_event_idx = armv6pmu_get_event_idx, .start = armv6pmu_start, .stop = armv6pmu_stop, - .cache_map = &armv6mpcore_perf_cache_map, - .event_map = &armv6mpcore_perf_map, - .raw_event_mask = 0xFF, + .map_event = armv6mpcore_map_event, .num_events = 3, .max_period = (1LLU << 32) - 1, }; diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 68ac522..be7b58a 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1140,6 +1140,30 @@ static void armv7pmu_reset(void *info) armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C); } +static int armv7_a8_map_event(struct perf_event *event) +{ + return map_cpu_event(event, &armv7_a8_perf_map, + &armv7_a8_perf_cache_map, 0xFF); +} + +static int armv7_a9_map_event(struct perf_event *event) +{ + return map_cpu_event(event, &armv7_a9_perf_map, + &armv7_a9_perf_cache_map, 0xFF); +} + +static int armv7_a5_map_event(struct perf_event *event) +{ + return map_cpu_event(event, &armv7_a5_perf_map, + &armv7_a5_perf_cache_map, 0xFF); +} + +static int armv7_a15_map_event(struct perf_event *event) +{ + return map_cpu_event(event, &armv7_a15_perf_map, + &armv7_a15_perf_cache_map, 0xFF); +} + static struct arm_pmu armv7pmu = { .handle_irq = armv7pmu_handle_irq, .enable = armv7pmu_enable_event, @@ -1150,7 +1174,6 @@ static struct arm_pmu armv7pmu = { .start = armv7pmu_start, .stop = armv7pmu_stop, .reset = armv7pmu_reset, - .raw_event_mask = 0xFF, .max_period = (1LLU << 32) - 1, }; @@ -1169,8 +1192,7 @@ static struct arm_pmu *__init armv7_a8_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA8; armv7pmu.name = "ARMv7 Cortex-A8"; - armv7pmu.cache_map = &armv7_a8_perf_cache_map; - armv7pmu.event_map = &armv7_a8_perf_map; + armv7pmu.map_event = armv7_a8_map_event; armv7pmu.num_events = armv7_read_num_pmnc_events(); return &armv7pmu; } @@ -1179,8 +1201,7 @@ static struct arm_pmu *__init armv7_a9_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA9; armv7pmu.name = "ARMv7 Cortex-A9"; - armv7pmu.cache_map = &armv7_a9_perf_cache_map; - armv7pmu.event_map = &armv7_a9_perf_map; + armv7pmu.map_event = armv7_a9_map_event; armv7pmu.num_events = armv7_read_num_pmnc_events(); return &armv7pmu; } @@ -1189,8 +1210,7 @@ static struct arm_pmu *__init armv7_a5_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA5; armv7pmu.name = "ARMv7 Cortex-A5"; - armv7pmu.cache_map = &armv7_a5_perf_cache_map; - armv7pmu.event_map = &armv7_a5_perf_map; + armv7pmu.map_event = armv7_a5_map_event; armv7pmu.num_events = armv7_read_num_pmnc_events(); return &armv7pmu; } @@ -1199,8 +1219,7 @@ static struct arm_pmu *__init armv7_a15_pmu_init(void) { armv7pmu.id = ARM_PERF_PMU_ID_CA15; armv7pmu.name = "ARMv7 Cortex-A15"; - armv7pmu.cache_map = &armv7_a15_perf_cache_map; - armv7pmu.event_map = &armv7_a15_perf_map; + armv7pmu.map_event = armv7_a15_map_event; armv7pmu.num_events = armv7_read_num_pmnc_events(); armv7pmu.set_event_filter = armv7pmu_set_event_filter; return &armv7pmu; diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 18e4823..d4c7610 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -425,6 +425,12 @@ xscale1pmu_write_counter(int counter, u32 val) } } +static int xscale_map_event(struct perf_event *event) +{ + return map_cpu_event(event, &xscale_perf_map, + &xscale_perf_cache_map, 0xFF); +} + static struct arm_pmu xscale1pmu = { .id = ARM_PERF_PMU_ID_XSCALE1, .name = "xscale1", @@ -436,9 +442,7 @@ static struct arm_pmu xscale1pmu = { .get_event_idx = xscale1pmu_get_event_idx, .start = xscale1pmu_start, .stop = xscale1pmu_stop, - .cache_map = &xscale_perf_cache_map, - .event_map = &xscale_perf_map, - .raw_event_mask = 0xFF, + .map_event = xscale_map_event, .num_events = 3, .max_period = (1LLU << 32) - 1, }; @@ -799,9 +803,7 @@ static struct arm_pmu xscale2pmu = { .get_event_idx = xscale2pmu_get_event_idx, .start = xscale2pmu_start, .stop = xscale2pmu_stop, - .cache_map = &xscale_perf_cache_map, - .event_map = &xscale_perf_map, - .raw_event_mask = 0xFF, + .map_event = xscale_map_event, .num_events = 5, .max_period = (1LLU << 32) - 1, }; -- cgit v0.10.2 From 8a16b34e21199eb5fcf2c5050d3bc414fc5d6563 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 28 Apr 2011 16:27:54 +0100 Subject: ARM: perf: add support for multiple PMUs Currently, a single static instance of struct pmu is used when registering an ARM PMU with the main perf subsystem. This limits the ARM perf code to supporting a single PMU. This patch replaces the static struct pmu instance with a member variable on struct arm_pmu. This provides bidirectional mapping between the two structs, and therefore allows for support of multiple PMUs. The function 'to_arm_pmu' is provided for convenience. PMU-generic functions are also updated to use the new mapping, and PMU-generic initialisation of the member variables is moved into a new function: armpmu_init. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index b13bf23..7f31eff 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -59,6 +59,7 @@ struct cpu_hw_events { static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); struct arm_pmu { + struct pmu pmu; enum arm_perf_pmu_ids id; enum arm_pmu_type type; cpumask_t active_irqs; @@ -84,6 +85,8 @@ struct arm_pmu { struct cpu_hw_events *(*get_hw_events)(void); }; +#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) + /* Set at runtime when we know what CPU type we are. */ static struct arm_pmu *armpmu; @@ -193,6 +196,7 @@ armpmu_event_set_period(struct perf_event *event, struct hw_perf_event *hwc, int idx) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); s64 left = local64_read(&hwc->period_left); s64 period = hwc->sample_period; int ret = 0; @@ -228,6 +232,7 @@ armpmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, int idx, int overflow) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); u64 delta, prev_raw_count, new_raw_count; again: @@ -267,6 +272,7 @@ armpmu_read(struct perf_event *event) static void armpmu_stop(struct perf_event *event, int flags) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; /* @@ -284,6 +290,7 @@ armpmu_stop(struct perf_event *event, int flags) static void armpmu_start(struct perf_event *event, int flags) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; /* @@ -308,6 +315,7 @@ armpmu_start(struct perf_event *event, int flags) static void armpmu_del(struct perf_event *event, int flags) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct cpu_hw_events *cpuc = armpmu->get_hw_events(); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -324,6 +332,7 @@ armpmu_del(struct perf_event *event, int flags) static int armpmu_add(struct perf_event *event, int flags) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct cpu_hw_events *cpuc = armpmu->get_hw_events(); struct hw_perf_event *hwc = &event->hw; int idx; @@ -358,12 +367,11 @@ out: return err; } -static struct pmu pmu; - static int validate_event(struct cpu_hw_events *cpuc, struct perf_event *event) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct hw_perf_event fake_event = event->hw; struct pmu *leader_pmu = event->group_leader->pmu; @@ -397,6 +405,7 @@ validate_group(struct perf_event *event) static irqreturn_t armpmu_platform_irq(int irq, void *dev) { + struct arm_pmu *armpmu = (struct arm_pmu *) dev; struct platform_device *plat_device = armpmu->plat_device; struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); @@ -404,7 +413,7 @@ static irqreturn_t armpmu_platform_irq(int irq, void *dev) } static void -armpmu_release_hardware(void) +armpmu_release_hardware(struct arm_pmu *armpmu) { int i, irq, irqs; struct platform_device *pmu_device = armpmu->plat_device; @@ -416,14 +425,14 @@ armpmu_release_hardware(void) continue; irq = platform_get_irq(pmu_device, i); if (irq >= 0) - free_irq(irq, NULL); + free_irq(irq, armpmu); } release_pmu(armpmu->type); } static int -armpmu_reserve_hardware(void) +armpmu_reserve_hardware(struct arm_pmu *armpmu) { struct arm_pmu_platdata *plat; irq_handler_t handle_irq; @@ -467,11 +476,11 @@ armpmu_reserve_hardware(void) err = request_irq(irq, handle_irq, IRQF_DISABLED | IRQF_NOBALANCING, - "arm-pmu", NULL); + "arm-pmu", armpmu); if (err) { pr_err("unable to request IRQ%d for ARM PMU counters\n", irq); - armpmu_release_hardware(); + armpmu_release_hardware(armpmu); return err; } @@ -484,11 +493,12 @@ armpmu_reserve_hardware(void) static void hw_perf_event_destroy(struct perf_event *event) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); atomic_t *active_events = &armpmu->active_events; struct mutex *pmu_reserve_mutex = &armpmu->reserve_mutex; if (atomic_dec_and_mutex_lock(active_events, pmu_reserve_mutex)) { - armpmu_release_hardware(); + armpmu_release_hardware(armpmu); mutex_unlock(pmu_reserve_mutex); } } @@ -503,6 +513,7 @@ event_requires_mode_exclusion(struct perf_event_attr *attr) static int __hw_perf_event_init(struct perf_event *event) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; int mapping, err; @@ -559,6 +570,7 @@ __hw_perf_event_init(struct perf_event *event) static int armpmu_event_init(struct perf_event *event) { + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); int err = 0; atomic_t *active_events = &armpmu->active_events; @@ -570,7 +582,7 @@ static int armpmu_event_init(struct perf_event *event) if (!atomic_inc_not_zero(active_events)) { mutex_lock(&armpmu->reserve_mutex); if (atomic_read(active_events) == 0) - err = armpmu_reserve_hardware(); + err = armpmu_reserve_hardware(armpmu); if (!err) atomic_inc(active_events); @@ -589,6 +601,7 @@ static int armpmu_event_init(struct perf_event *event) static void armpmu_enable(struct pmu *pmu) { + struct arm_pmu *armpmu = to_arm_pmu(pmu); /* Enable all of the perf events on hardware. */ int idx, enabled = 0; struct cpu_hw_events *cpuc = armpmu->get_hw_events(); @@ -609,24 +622,31 @@ static void armpmu_enable(struct pmu *pmu) static void armpmu_disable(struct pmu *pmu) { + struct arm_pmu *armpmu = to_arm_pmu(pmu); armpmu->stop(); } -static struct pmu pmu = { - .pmu_enable = armpmu_enable, - .pmu_disable = armpmu_disable, - .event_init = armpmu_event_init, - .add = armpmu_add, - .del = armpmu_del, - .start = armpmu_start, - .stop = armpmu_stop, - .read = armpmu_read, -}; - static void __init armpmu_init(struct arm_pmu *armpmu) { atomic_set(&armpmu->active_events, 0); mutex_init(&armpmu->reserve_mutex); + + armpmu->pmu = (struct pmu) { + .pmu_enable = armpmu_enable, + .pmu_disable = armpmu_disable, + .event_init = armpmu_event_init, + .add = armpmu_add, + .del = armpmu_del, + .start = armpmu_start, + .stop = armpmu_stop, + .read = armpmu_read, + }; +} + +static int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) +{ + armpmu_init(armpmu); + return perf_pmu_register(&armpmu->pmu, name, type); } /* Include the PMU-specific implementations. */ @@ -751,8 +771,7 @@ init_hw_perf_events(void) pr_info("enabled with %s PMU driver, %d counters available\n", armpmu->name, armpmu->num_events); cpu_pmu_init(armpmu); - armpmu_init(armpmu); - perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW); + armpmu_register(armpmu, "cpu", PERF_TYPE_RAW); } else { pr_info("no hardware support available\n"); } -- cgit v0.10.2 From 3fc2c83087717dc88003428245d97b9d432fff2d Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Fri, 24 Jun 2011 11:30:59 +0100 Subject: ARM: perf: remove event limit from pmu_hw_events Currently the event accounting data in pmu_hw_events is stored in fixed-sized arrays within the structure. This patch refactors the accounting data to allow any number of events to be managed. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 7f31eff..e1db555 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -42,13 +42,13 @@ struct cpu_hw_events { /* * The events that are active on the CPU for the given index. */ - struct perf_event *events[ARMPMU_MAX_HWEVENTS]; + struct perf_event **events; /* * A 1 bit for an index indicates that the counter is being used for * an event. A 0 means that the counter can be used. */ - unsigned long used_mask[BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)]; + unsigned long *used_mask; /* * Hardware lock to serialize accesses to PMU registers. Needed for the @@ -56,6 +56,9 @@ struct cpu_hw_events { */ raw_spinlock_t pmu_lock; }; + +static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); +static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); struct arm_pmu { @@ -714,6 +717,8 @@ static void __init cpu_pmu_init(struct arm_pmu *armpmu) int cpu; for_each_possible_cpu(cpu) { struct cpu_hw_events *events = &per_cpu(cpu_hw_events, cpu); + events->events = per_cpu(hw_events, cpu); + events->used_mask = per_cpu(used_mask, cpu); raw_spin_lock_init(&events->pmu_lock); } armpmu->get_hw_events = armpmu_get_cpu_events; -- cgit v0.10.2 From 8be3f9a2385f91f7bf5c58f351e24b9247898e8f Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 17 May 2011 11:20:11 +0100 Subject: ARM: perf: remove cpu-related misnomers Currently struct cpu_hw_events stores data on events running on a PMU associated with a CPU. As this data is general enough to be used for system PMUs, this name is a misnomer, and may cause confusion when it is used for system PMUs. Additionally, 'armpmu' is commonly used as a parameter name for an instance of struct arm_pmu. The name is also used for a global instance which represents the CPU's PMU. As cpu_hw_events is now not tied to CPU PMUs, it is renamed to pmu_hw_events, with instances of it renamed similarly. As the global 'armpmu' is CPU-specfic, it is renamed to cpu_pmu. This should make it clearer which code is generic, and which is coupled with the CPU. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index e1db555..8315133 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -37,10 +37,10 @@ */ #define ARMPMU_MAX_HWEVENTS 32 -/* The events for a given CPU. */ -struct cpu_hw_events { +/* The events for a given PMU register set. */ +struct pmu_hw_events { /* - * The events that are active on the CPU for the given index. + * The events that are active on the PMU for the given index. */ struct perf_event **events; @@ -59,7 +59,7 @@ struct cpu_hw_events { static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); -static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); +static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); struct arm_pmu { struct pmu pmu; @@ -70,7 +70,7 @@ struct arm_pmu { irqreturn_t (*handle_irq)(int irq_num, void *dev); void (*enable)(struct hw_perf_event *evt, int idx); void (*disable)(struct hw_perf_event *evt, int idx); - int (*get_event_idx)(struct cpu_hw_events *cpuc, + int (*get_event_idx)(struct pmu_hw_events *hw_events, struct hw_perf_event *hwc); int (*set_event_filter)(struct hw_perf_event *evt, struct perf_event_attr *attr); @@ -85,21 +85,21 @@ struct arm_pmu { struct mutex reserve_mutex; u64 max_period; struct platform_device *plat_device; - struct cpu_hw_events *(*get_hw_events)(void); + struct pmu_hw_events *(*get_hw_events)(void); }; #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) /* Set at runtime when we know what CPU type we are. */ -static struct arm_pmu *armpmu; +static struct arm_pmu *cpu_pmu; enum arm_perf_pmu_ids armpmu_get_pmu_id(void) { int id = -ENODEV; - if (armpmu != NULL) - id = armpmu->id; + if (cpu_pmu != NULL) + id = cpu_pmu->id; return id; } @@ -110,8 +110,8 @@ armpmu_get_max_events(void) { int max_events = 0; - if (armpmu != NULL) - max_events = armpmu->num_events; + if (cpu_pmu != NULL) + max_events = cpu_pmu->num_events; return max_events; } @@ -319,15 +319,15 @@ static void armpmu_del(struct perf_event *event, int flags) { struct arm_pmu *armpmu = to_arm_pmu(event->pmu); - struct cpu_hw_events *cpuc = armpmu->get_hw_events(); + struct pmu_hw_events *hw_events = armpmu->get_hw_events(); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; WARN_ON(idx < 0); armpmu_stop(event, PERF_EF_UPDATE); - cpuc->events[idx] = NULL; - clear_bit(idx, cpuc->used_mask); + hw_events->events[idx] = NULL; + clear_bit(idx, hw_events->used_mask); perf_event_update_userpage(event); } @@ -336,7 +336,7 @@ static int armpmu_add(struct perf_event *event, int flags) { struct arm_pmu *armpmu = to_arm_pmu(event->pmu); - struct cpu_hw_events *cpuc = armpmu->get_hw_events(); + struct pmu_hw_events *hw_events = armpmu->get_hw_events(); struct hw_perf_event *hwc = &event->hw; int idx; int err = 0; @@ -344,7 +344,7 @@ armpmu_add(struct perf_event *event, int flags) perf_pmu_disable(event->pmu); /* If we don't have a space for the counter then finish early. */ - idx = armpmu->get_event_idx(cpuc, hwc); + idx = armpmu->get_event_idx(hw_events, hwc); if (idx < 0) { err = idx; goto out; @@ -356,7 +356,7 @@ armpmu_add(struct perf_event *event, int flags) */ event->hw.idx = idx; armpmu->disable(hwc, idx); - cpuc->events[idx] = event; + hw_events->events[idx] = event; hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; if (flags & PERF_EF_START) @@ -371,7 +371,7 @@ out: } static int -validate_event(struct cpu_hw_events *cpuc, +validate_event(struct pmu_hw_events *hw_events, struct perf_event *event) { struct arm_pmu *armpmu = to_arm_pmu(event->pmu); @@ -381,14 +381,14 @@ validate_event(struct cpu_hw_events *cpuc, if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF) return 1; - return armpmu->get_event_idx(cpuc, &fake_event) >= 0; + return armpmu->get_event_idx(hw_events, &fake_event) >= 0; } static int validate_group(struct perf_event *event) { struct perf_event *sibling, *leader = event->group_leader; - struct cpu_hw_events fake_pmu; + struct pmu_hw_events fake_pmu; memset(&fake_pmu, 0, sizeof(fake_pmu)); @@ -604,13 +604,13 @@ static int armpmu_event_init(struct perf_event *event) static void armpmu_enable(struct pmu *pmu) { - struct arm_pmu *armpmu = to_arm_pmu(pmu); /* Enable all of the perf events on hardware. */ + struct arm_pmu *armpmu = to_arm_pmu(pmu); int idx, enabled = 0; - struct cpu_hw_events *cpuc = armpmu->get_hw_events(); + struct pmu_hw_events *hw_events = armpmu->get_hw_events(); for (idx = 0; idx < armpmu->num_events; ++idx) { - struct perf_event *event = cpuc->events[idx]; + struct perf_event *event = hw_events->events[idx]; if (!event) continue; @@ -662,13 +662,13 @@ static int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) * This requires SMP to be available, so exists as a separate initcall. */ static int __init -armpmu_reset(void) +cpu_pmu_reset(void) { - if (armpmu && armpmu->reset) - return on_each_cpu(armpmu->reset, NULL, 1); + if (cpu_pmu && cpu_pmu->reset) + return on_each_cpu(cpu_pmu->reset, NULL, 1); return 0; } -arch_initcall(armpmu_reset); +arch_initcall(cpu_pmu_reset); /* * PMU platform driver and devicetree bindings. @@ -688,7 +688,7 @@ static struct platform_device_id armpmu_plat_device_ids[] = { static int __devinit armpmu_device_probe(struct platform_device *pdev) { - armpmu->plat_device = pdev; + cpu_pmu->plat_device = pdev; return 0; } @@ -707,7 +707,7 @@ static int __init register_pmu_driver(void) } device_initcall(register_pmu_driver); -static struct cpu_hw_events *armpmu_get_cpu_events(void) +static struct pmu_hw_events *armpmu_get_cpu_events(void) { return &__get_cpu_var(cpu_hw_events); } @@ -716,7 +716,7 @@ static void __init cpu_pmu_init(struct arm_pmu *armpmu) { int cpu; for_each_possible_cpu(cpu) { - struct cpu_hw_events *events = &per_cpu(cpu_hw_events, cpu); + struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); events->events = per_cpu(hw_events, cpu); events->used_mask = per_cpu(used_mask, cpu); raw_spin_lock_init(&events->pmu_lock); @@ -741,22 +741,22 @@ init_hw_perf_events(void) case 0xB360: /* ARM1136 */ case 0xB560: /* ARM1156 */ case 0xB760: /* ARM1176 */ - armpmu = armv6pmu_init(); + cpu_pmu = armv6pmu_init(); break; case 0xB020: /* ARM11mpcore */ - armpmu = armv6mpcore_pmu_init(); + cpu_pmu = armv6mpcore_pmu_init(); break; case 0xC080: /* Cortex-A8 */ - armpmu = armv7_a8_pmu_init(); + cpu_pmu = armv7_a8_pmu_init(); break; case 0xC090: /* Cortex-A9 */ - armpmu = armv7_a9_pmu_init(); + cpu_pmu = armv7_a9_pmu_init(); break; case 0xC050: /* Cortex-A5 */ - armpmu = armv7_a5_pmu_init(); + cpu_pmu = armv7_a5_pmu_init(); break; case 0xC0F0: /* Cortex-A15 */ - armpmu = armv7_a15_pmu_init(); + cpu_pmu = armv7_a15_pmu_init(); break; } /* Intel CPUs [xscale]. */ @@ -764,19 +764,19 @@ init_hw_perf_events(void) part_number = (cpuid >> 13) & 0x7; switch (part_number) { case 1: - armpmu = xscale1pmu_init(); + cpu_pmu = xscale1pmu_init(); break; case 2: - armpmu = xscale2pmu_init(); + cpu_pmu = xscale2pmu_init(); break; } } - if (armpmu) { + if (cpu_pmu) { pr_info("enabled with %s PMU driver, %d counters available\n", - armpmu->name, armpmu->num_events); - cpu_pmu_init(armpmu); - armpmu_register(armpmu, "cpu", PERF_TYPE_RAW); + cpu_pmu->name, cpu_pmu->num_events); + cpu_pmu_init(cpu_pmu); + armpmu_register(cpu_pmu, "cpu", PERF_TYPE_RAW); } else { pr_info("no hardware support available\n"); } diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index a4c5aa9..e63d811 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -433,7 +433,7 @@ armv6pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); if (ARMV6_CYCLE_COUNTER == idx) { mask = 0; @@ -486,7 +486,7 @@ armv6pmu_handle_irq(int irq_num, { unsigned long pmcr = armv6_pmcr_read(); struct perf_sample_data data; - struct cpu_hw_events *cpuc; + struct pmu_hw_events *cpuc; struct pt_regs *regs; int idx; @@ -505,7 +505,7 @@ armv6pmu_handle_irq(int irq_num, perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx < armpmu->num_events; ++idx) { + for (idx = 0; idx < cpu_pmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; @@ -526,7 +526,7 @@ armv6pmu_handle_irq(int irq_num, continue; if (perf_event_overflow(event, &data, regs)) - armpmu->disable(hwc, idx); + cpu_pmu->disable(hwc, idx); } /* @@ -545,7 +545,7 @@ static void armv6pmu_start(void) { unsigned long flags, val; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); val = armv6_pmcr_read(); @@ -558,7 +558,7 @@ static void armv6pmu_stop(void) { unsigned long flags, val; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); val = armv6_pmcr_read(); @@ -568,7 +568,7 @@ armv6pmu_stop(void) } static int -armv6pmu_get_event_idx(struct cpu_hw_events *cpuc, +armv6pmu_get_event_idx(struct pmu_hw_events *cpuc, struct hw_perf_event *event) { /* Always place a cycle counter into the cycle counter. */ @@ -598,7 +598,7 @@ armv6pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); if (ARMV6_CYCLE_COUNTER == idx) { mask = ARMV6_PMCR_CCOUNT_IEN; @@ -632,7 +632,7 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, flags, evt = 0; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); if (ARMV6_CYCLE_COUNTER == idx) { mask = ARMV6_PMCR_CCOUNT_IEN; diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index be7b58a..98b7573 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -683,7 +683,7 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] */ #define ARMV7_IDX_CYCLE_COUNTER 0 #define ARMV7_IDX_COUNTER0 1 -#define ARMV7_IDX_COUNTER_LAST (ARMV7_IDX_CYCLE_COUNTER + armpmu->num_events - 1) +#define ARMV7_IDX_COUNTER_LAST (ARMV7_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1) #define ARMV7_MAX_COUNTERS 32 #define ARMV7_COUNTER_MASK (ARMV7_MAX_COUNTERS - 1) @@ -936,7 +936,7 @@ static void armv7_pmnc_dump_regs(void) static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); /* * Enable counter and interrupt, and set the counter to count @@ -973,7 +973,7 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); /* * Disable counter and interrupt @@ -997,7 +997,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) { u32 pmnc; struct perf_sample_data data; - struct cpu_hw_events *cpuc; + struct pmu_hw_events *cpuc; struct pt_regs *regs; int idx; @@ -1020,7 +1020,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx < armpmu->num_events; ++idx) { + for (idx = 0; idx < cpu_pmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; @@ -1038,7 +1038,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) continue; if (perf_event_overflow(event, &data, regs)) - armpmu->disable(hwc, idx); + cpu_pmu->disable(hwc, idx); } /* @@ -1056,7 +1056,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) static void armv7pmu_start(void) { unsigned long flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); /* Enable all counters */ @@ -1067,7 +1067,7 @@ static void armv7pmu_start(void) static void armv7pmu_stop(void) { unsigned long flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); /* Disable all counters */ @@ -1075,7 +1075,7 @@ static void armv7pmu_stop(void) raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } -static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, +static int armv7pmu_get_event_idx(struct pmu_hw_events *cpuc, struct hw_perf_event *event) { int idx; @@ -1093,7 +1093,7 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, * For anything other than a cycle counter, try and use * the events counters */ - for (idx = ARMV7_IDX_COUNTER0; idx < armpmu->num_events; ++idx) { + for (idx = ARMV7_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) { if (!test_and_set_bit(idx, cpuc->used_mask)) return idx; } @@ -1130,7 +1130,7 @@ static int armv7pmu_set_event_filter(struct hw_perf_event *event, static void armv7pmu_reset(void *info) { - u32 idx, nb_cnt = armpmu->num_events; + u32 idx, nb_cnt = cpu_pmu->num_events; /* The counter and interrupt enable registers are unknown at reset. */ for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index d4c7610..e0cca10 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -222,7 +222,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) { unsigned long pmnc; struct perf_sample_data data; - struct cpu_hw_events *cpuc; + struct pmu_hw_events *cpuc; struct pt_regs *regs; int idx; @@ -249,7 +249,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx < armpmu->num_events; ++idx) { + for (idx = 0; idx < cpu_pmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; @@ -263,7 +263,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) continue; if (perf_event_overflow(event, &data, regs)) - armpmu->disable(hwc, idx); + cpu_pmu->disable(hwc, idx); } irq_work_run(); @@ -281,7 +281,7 @@ static void xscale1pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); switch (idx) { case XSCALE_CYCLE_COUNTER: @@ -315,7 +315,7 @@ static void xscale1pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long val, mask, evt, flags; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); switch (idx) { case XSCALE_CYCLE_COUNTER: @@ -344,7 +344,7 @@ xscale1pmu_disable_event(struct hw_perf_event *hwc, int idx) } static int -xscale1pmu_get_event_idx(struct cpu_hw_events *cpuc, +xscale1pmu_get_event_idx(struct pmu_hw_events *cpuc, struct hw_perf_event *event) { if (XSCALE_PERFCTR_CCNT == event->config_base) { @@ -367,7 +367,7 @@ static void xscale1pmu_start(void) { unsigned long flags, val; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale1pmu_read_pmnc(); @@ -380,7 +380,7 @@ static void xscale1pmu_stop(void) { unsigned long flags, val; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale1pmu_read_pmnc(); @@ -565,7 +565,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) { unsigned long pmnc, of_flags; struct perf_sample_data data; - struct cpu_hw_events *cpuc; + struct pmu_hw_events *cpuc; struct pt_regs *regs; int idx; @@ -586,7 +586,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) perf_sample_data_init(&data, 0); cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx < armpmu->num_events; ++idx) { + for (idx = 0; idx < cpu_pmu->num_events; ++idx) { struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; @@ -600,7 +600,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) continue; if (perf_event_overflow(event, &data, regs)) - armpmu->disable(hwc, idx); + cpu_pmu->disable(hwc, idx); } irq_work_run(); @@ -618,7 +618,7 @@ static void xscale2pmu_enable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags, ien, evtsel; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); ien = xscale2pmu_read_int_enable(); evtsel = xscale2pmu_read_event_select(); @@ -662,7 +662,7 @@ static void xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) { unsigned long flags, ien, evtsel; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); ien = xscale2pmu_read_int_enable(); evtsel = xscale2pmu_read_event_select(); @@ -703,7 +703,7 @@ xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) } static int -xscale2pmu_get_event_idx(struct cpu_hw_events *cpuc, +xscale2pmu_get_event_idx(struct pmu_hw_events *cpuc, struct hw_perf_event *event) { int idx = xscale1pmu_get_event_idx(cpuc, event); @@ -722,7 +722,7 @@ static void xscale2pmu_start(void) { unsigned long flags, val; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64; @@ -735,7 +735,7 @@ static void xscale2pmu_stop(void) { unsigned long flags, val; - struct cpu_hw_events *events = armpmu->get_hw_events(); + struct pmu_hw_events *events = cpu_pmu->get_hw_events(); raw_spin_lock_irqsave(&events->pmu_lock, flags); val = xscale2pmu_read_pmnc(); -- cgit v0.10.2 From 0ce47080dfffe71edd433b35dcdada24c61079eb Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 19 May 2011 10:07:57 +0100 Subject: ARM: perf: move arm_pmu into Currently, struct arm_pmu and related functions are only visible to {,arch/arm/}/kernel/perf_event.c. This prevents new drivers from using the framework. This patch moves declarations to asm/pmu.h, allowing new PMU drivers to use the framework. Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Reviewed-by: Jamie Iles Reviewed-by: Ashwin Chaugule Signed-off-by: Will Deacon diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index a06ba87..71d99b8 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -13,6 +13,7 @@ #define __ARM_PMU_H__ #include +#include /* * Types of PMUs that can be accessed directly and require mutual @@ -79,4 +80,67 @@ release_pmu(enum arm_pmu_type type) { } #endif /* CONFIG_CPU_HAS_PMU */ +#ifdef CONFIG_HW_PERF_EVENTS + +/* The events for a given PMU register set. */ +struct pmu_hw_events { + /* + * The events that are active on the PMU for the given index. + */ + struct perf_event **events; + + /* + * A 1 bit for an index indicates that the counter is being used for + * an event. A 0 means that the counter can be used. + */ + unsigned long *used_mask; + + /* + * Hardware lock to serialize accesses to PMU registers. Needed for the + * read/modify/write sequences. + */ + raw_spinlock_t pmu_lock; +}; + +struct arm_pmu { + struct pmu pmu; + enum arm_perf_pmu_ids id; + enum arm_pmu_type type; + cpumask_t active_irqs; + const char *name; + irqreturn_t (*handle_irq)(int irq_num, void *dev); + void (*enable)(struct hw_perf_event *evt, int idx); + void (*disable)(struct hw_perf_event *evt, int idx); + int (*get_event_idx)(struct pmu_hw_events *hw_events, + struct hw_perf_event *hwc); + int (*set_event_filter)(struct hw_perf_event *evt, + struct perf_event_attr *attr); + u32 (*read_counter)(int idx); + void (*write_counter)(int idx, u32 val); + void (*start)(void); + void (*stop)(void); + void (*reset)(void *); + int (*map_event)(struct perf_event *event); + int num_events; + atomic_t active_events; + struct mutex reserve_mutex; + u64 max_period; + struct platform_device *plat_device; + struct pmu_hw_events *(*get_hw_events)(void); +}; + +#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) + +int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type); + +u64 armpmu_event_update(struct perf_event *event, + struct hw_perf_event *hwc, + int idx, int overflow); + +int armpmu_event_set_period(struct perf_event *event, + struct hw_perf_event *hwc, + int idx); + +#endif /* CONFIG_HW_PERF_EVENTS */ + #endif /* __ARM_PMU_H__ */ diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 8315133..aaa631b 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -37,57 +37,10 @@ */ #define ARMPMU_MAX_HWEVENTS 32 -/* The events for a given PMU register set. */ -struct pmu_hw_events { - /* - * The events that are active on the PMU for the given index. - */ - struct perf_event **events; - - /* - * A 1 bit for an index indicates that the counter is being used for - * an event. A 0 means that the counter can be used. - */ - unsigned long *used_mask; - - /* - * Hardware lock to serialize accesses to PMU registers. Needed for the - * read/modify/write sequences. - */ - raw_spinlock_t pmu_lock; -}; - static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); -struct arm_pmu { - struct pmu pmu; - enum arm_perf_pmu_ids id; - enum arm_pmu_type type; - cpumask_t active_irqs; - const char *name; - irqreturn_t (*handle_irq)(int irq_num, void *dev); - void (*enable)(struct hw_perf_event *evt, int idx); - void (*disable)(struct hw_perf_event *evt, int idx); - int (*get_event_idx)(struct pmu_hw_events *hw_events, - struct hw_perf_event *hwc); - int (*set_event_filter)(struct hw_perf_event *evt, - struct perf_event_attr *attr); - u32 (*read_counter)(int idx); - void (*write_counter)(int idx, u32 val); - void (*start)(void); - void (*stop)(void); - void (*reset)(void *); - int (*map_event)(struct perf_event *event); - int num_events; - atomic_t active_events; - struct mutex reserve_mutex; - u64 max_period; - struct platform_device *plat_device; - struct pmu_hw_events *(*get_hw_events)(void); -}; - #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) /* Set at runtime when we know what CPU type we are. */ @@ -194,7 +147,7 @@ static int map_cpu_event(struct perf_event *event, return -ENOENT; } -static int +int armpmu_event_set_period(struct perf_event *event, struct hw_perf_event *hwc, int idx) @@ -230,7 +183,7 @@ armpmu_event_set_period(struct perf_event *event, return ret; } -static u64 +u64 armpmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, int idx, int overflow) @@ -646,7 +599,7 @@ static void __init armpmu_init(struct arm_pmu *armpmu) }; } -static int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) +int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) { armpmu_init(armpmu); return perf_pmu_register(&armpmu->pmu, name, type); -- cgit v0.10.2 From 7325eaec439cd0cc8c9b61b59d41d99abace1b23 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 23 Aug 2011 11:59:49 +0100 Subject: ARM: perf: Remove unnecessary armpmu->enable()s Currently, armpmu_enable iterates through the events for a given counter set, calling armpmu->enable on each before calling armpmu->start to start the PMU's counters. As armpmu->enable is called when each event is added, each event is already configured in hardware. Due to this, calling armpmu->enable in armpmu_enable is unnecessary and confusing. This patch removes the unnecessary calls to armpmu->enable. Signed-off-by: Mark Rutland Signed-off-by: Will Deacon diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index aaa631b..e6e5d7c 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -12,6 +12,7 @@ */ #define pr_fmt(fmt) "hw perfevents: " fmt +#include #include #include #include @@ -557,20 +558,9 @@ static int armpmu_event_init(struct perf_event *event) static void armpmu_enable(struct pmu *pmu) { - /* Enable all of the perf events on hardware. */ struct arm_pmu *armpmu = to_arm_pmu(pmu); - int idx, enabled = 0; struct pmu_hw_events *hw_events = armpmu->get_hw_events(); - - for (idx = 0; idx < armpmu->num_events; ++idx) { - struct perf_event *event = hw_events->events[idx]; - - if (!event) - continue; - - armpmu->enable(&event->hw, idx); - enabled = 1; - } + int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events); if (enabled) armpmu->start(); -- cgit v0.10.2