diff options
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r-- | arch/s390/kernel/perf_cpum_sf.c | 17 | ||||
-rw-r--r-- | arch/s390/kernel/perf_event.c | 30 |
2 files changed, 42 insertions, 5 deletions
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 52bf36ee91aa..ae5e0192160d 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -260,16 +260,12 @@ static int sf_disable(void) #define PMC_INIT 0 #define PMC_RELEASE 1 +#define PMC_FAILURE 2 static void setup_pmc_cpu(void *flags) { int err; struct cpu_hw_sf *cpusf = &__get_cpu_var(cpu_hw_sf); - /* XXX Improve error handling and pass a flag in the *flags - * variable to indicate failures. Alternatively, ignore - * (print) errors here and let the PMU functions fail if - * the per-cpu PMU_F_RESERVED flag is not. - */ err = 0; switch (*((int *) flags)) { case PMC_INIT: @@ -299,6 +295,8 @@ static void setup_pmc_cpu(void *flags) "setup_pmc_cpu: released: cpuhw=%p\n", cpusf); break; } + if (err) + *((int *) flags) |= PMC_FAILURE; } static void release_pmc_hardware(void) @@ -307,13 +305,22 @@ static void release_pmc_hardware(void) irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); on_each_cpu(setup_pmc_cpu, &flags, 1); + perf_release_sampling(); } static int reserve_pmc_hardware(void) { int flags = PMC_INIT; + int err; + err = perf_reserve_sampling(); + if (err) + return err; on_each_cpu(setup_pmc_cpu, &flags, 1); + if (flags & PMC_FAILURE) { + release_pmc_hardware(); + return -ENODEV; + } irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); return 0; diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c index b9843ba9829f..4edcdfa4894e 100644 --- a/arch/s390/kernel/perf_event.c +++ b/arch/s390/kernel/perf_event.c @@ -208,3 +208,33 @@ ssize_t cpumf_events_sysfs_show(struct device *dev, return sprintf(page, "event=0x%04llx,name=%s\n", pmu_attr->id, attr->attr.name); } + +/* Reserve/release functions for sharing perf hardware */ +static DEFINE_SPINLOCK(perf_hw_owner_lock); +static void *perf_sampling_owner; + +int perf_reserve_sampling(void) +{ + int err; + + err = 0; + spin_lock(&perf_hw_owner_lock); + if (perf_sampling_owner) { + pr_warn("The sampling facility is already reserved by %p\n", + perf_sampling_owner); + err = -EBUSY; + } else + perf_sampling_owner = __builtin_return_address(0); + spin_unlock(&perf_hw_owner_lock); + return err; +} +EXPORT_SYMBOL(perf_reserve_sampling); + +void perf_release_sampling(void) +{ + spin_lock(&perf_hw_owner_lock); + WARN_ON(!perf_sampling_owner); + perf_sampling_owner = NULL; + spin_unlock(&perf_hw_owner_lock); +} +EXPORT_SYMBOL(perf_release_sampling); |