Commit e28bb79d authored by Hendrik Brueckner's avatar Hendrik Brueckner Committed by Martin Schwidefsky

s390/perf,oprofile: Share sampling facility

Introduce reserve/release functions to share the sampling facility
between perf and oprofile.
Also improve error handling for the sampling facility support in perf.
Signed-off-by: default avatarHendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 55baa2f8
...@@ -52,5 +52,9 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); ...@@ -52,5 +52,9 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs);
#define TEAR_REG(hwc) ((hwc)->last_tag) #define TEAR_REG(hwc) ((hwc)->last_tag)
#define SAMPL_RATE(hwc) ((hwc)->event_base) #define SAMPL_RATE(hwc) ((hwc)->event_base)
/* Perf hardware reserve and release functions */
int perf_reserve_sampling(void);
void perf_release_sampling(void);
#endif /* CONFIG_64BIT */ #endif /* CONFIG_64BIT */
#endif /* _ASM_S390_PERF_EVENT_H */ #endif /* _ASM_S390_PERF_EVENT_H */
...@@ -260,16 +260,12 @@ static int sf_disable(void) ...@@ -260,16 +260,12 @@ static int sf_disable(void)
#define PMC_INIT 0 #define PMC_INIT 0
#define PMC_RELEASE 1 #define PMC_RELEASE 1
#define PMC_FAILURE 2
static void setup_pmc_cpu(void *flags) static void setup_pmc_cpu(void *flags)
{ {
int err; int err;
struct cpu_hw_sf *cpusf = &__get_cpu_var(cpu_hw_sf); 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; err = 0;
switch (*((int *) flags)) { switch (*((int *) flags)) {
case PMC_INIT: case PMC_INIT:
...@@ -299,6 +295,8 @@ static void setup_pmc_cpu(void *flags) ...@@ -299,6 +295,8 @@ static void setup_pmc_cpu(void *flags)
"setup_pmc_cpu: released: cpuhw=%p\n", cpusf); "setup_pmc_cpu: released: cpuhw=%p\n", cpusf);
break; break;
} }
if (err)
*((int *) flags) |= PMC_FAILURE;
} }
static void release_pmc_hardware(void) static void release_pmc_hardware(void)
...@@ -307,13 +305,22 @@ static void release_pmc_hardware(void) ...@@ -307,13 +305,22 @@ static void release_pmc_hardware(void)
irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
on_each_cpu(setup_pmc_cpu, &flags, 1); on_each_cpu(setup_pmc_cpu, &flags, 1);
perf_release_sampling();
} }
static int reserve_pmc_hardware(void) static int reserve_pmc_hardware(void)
{ {
int flags = PMC_INIT; int flags = PMC_INIT;
int err;
err = perf_reserve_sampling();
if (err)
return err;
on_each_cpu(setup_pmc_cpu, &flags, 1); on_each_cpu(setup_pmc_cpu, &flags, 1);
if (flags & PMC_FAILURE) {
release_pmc_hardware();
return -ENODEV;
}
irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
return 0; return 0;
......
...@@ -208,3 +208,33 @@ ssize_t cpumf_events_sysfs_show(struct device *dev, ...@@ -208,3 +208,33 @@ ssize_t cpumf_events_sysfs_show(struct device *dev,
return sprintf(page, "event=0x%04llx,name=%s\n", return sprintf(page, "event=0x%04llx,name=%s\n",
pmu_attr->id, attr->attr.name); 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);
...@@ -41,6 +41,7 @@ static DEFINE_MUTEX(hws_sem_oom); ...@@ -41,6 +41,7 @@ static DEFINE_MUTEX(hws_sem_oom);
static unsigned char hws_flush_all; static unsigned char hws_flush_all;
static unsigned int hws_oom; static unsigned int hws_oom;
static unsigned int hws_alert;
static struct workqueue_struct *hws_wq; static struct workqueue_struct *hws_wq;
static unsigned int hws_state; static unsigned int hws_state;
...@@ -182,6 +183,9 @@ static void hws_ext_handler(struct ext_code ext_code, ...@@ -182,6 +183,9 @@ static void hws_ext_handler(struct ext_code ext_code,
if (!(param32 & CPU_MF_INT_SF_MASK)) if (!(param32 & CPU_MF_INT_SF_MASK))
return; return;
if (!hws_alert)
return;
inc_irq_stat(IRQEXT_CMS); inc_irq_stat(IRQEXT_CMS);
atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32); atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32);
...@@ -941,6 +945,7 @@ int hwsampler_deallocate(void) ...@@ -941,6 +945,7 @@ int hwsampler_deallocate(void)
goto deallocate_exit; goto deallocate_exit;
irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
hws_alert = 0;
deallocate_sdbt(); deallocate_sdbt();
hws_state = HWS_DEALLOCATED; hws_state = HWS_DEALLOCATED;
...@@ -1055,6 +1060,7 @@ int hwsampler_shutdown(void) ...@@ -1055,6 +1060,7 @@ int hwsampler_shutdown(void)
if (hws_state == HWS_STOPPED) { if (hws_state == HWS_STOPPED) {
irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
hws_alert = 0;
deallocate_sdbt(); deallocate_sdbt();
} }
if (hws_wq) { if (hws_wq) {
...@@ -1129,6 +1135,7 @@ int hwsampler_start_all(unsigned long rate) ...@@ -1129,6 +1135,7 @@ int hwsampler_start_all(unsigned long rate)
hws_oom = 1; hws_oom = 1;
hws_flush_all = 0; hws_flush_all = 0;
/* now let them in, 1407 CPUMF external interrupts */ /* now let them in, 1407 CPUMF external interrupts */
hws_alert = 1;
irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
return 0; return 0;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/oprofile.h> #include <linux/oprofile.h>
#include <linux/perf_event.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -67,6 +68,21 @@ module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0); ...@@ -67,6 +68,21 @@ module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0);
MODULE_PARM_DESC(cpu_type, "Force legacy basic mode sampling" MODULE_PARM_DESC(cpu_type, "Force legacy basic mode sampling"
"(report cpu_type \"timer\""); "(report cpu_type \"timer\"");
static int __oprofile_hwsampler_start(void)
{
int retval;
retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks);
if (retval)
return retval;
retval = hwsampler_start_all(oprofile_hw_interval);
if (retval)
hwsampler_deallocate();
return retval;
}
static int oprofile_hwsampler_start(void) static int oprofile_hwsampler_start(void)
{ {
int retval; int retval;
...@@ -76,13 +92,13 @@ static int oprofile_hwsampler_start(void) ...@@ -76,13 +92,13 @@ static int oprofile_hwsampler_start(void)
if (!hwsampler_running) if (!hwsampler_running)
return timer_ops.start(); return timer_ops.start();
retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks); retval = perf_reserve_sampling();
if (retval) if (retval)
return retval; return retval;
retval = hwsampler_start_all(oprofile_hw_interval); retval = __oprofile_hwsampler_start();
if (retval) if (retval)
hwsampler_deallocate(); perf_release_sampling();
return retval; return retval;
} }
...@@ -96,6 +112,7 @@ static void oprofile_hwsampler_stop(void) ...@@ -96,6 +112,7 @@ static void oprofile_hwsampler_stop(void)
hwsampler_stop_all(); hwsampler_stop_all();
hwsampler_deallocate(); hwsampler_deallocate();
perf_release_sampling();
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment