s390/perf,oprofile: Share sampling facility
authorHendrik Brueckner <brueckner@linux.vnet.ibm.com>
Thu, 12 Dec 2013 15:52:48 +0000 (16:52 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 16 Dec 2013 13:37:52 +0000 (14:37 +0100)
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: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/perf_event.h
arch/s390/kernel/perf_cpum_sf.c
arch/s390/kernel/perf_event.c
arch/s390/oprofile/hwsampler.c
arch/s390/oprofile/init.c

index b4eea25f379efa53040542819bd66e2b2d466047..23d2dfa8201d636faff7b0d800a740c13d87d527 100644 (file)
@@ -52,5 +52,9 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs);
 #define TEAR_REG(hwc)          ((hwc)->last_tag)
 #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 /* _ASM_S390_PERF_EVENT_H */
index 52bf36ee91aa34fbdb71a9d0202cfb9b1eaf19fa..ae5e0192160d6e5269395f8e43320e1172034cce 100644 (file)
@@ -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;
index b9843ba9829f96c0013329ed84a92749d969a6ea..4edcdfa4894e333c4a674b9fcf29cea3f5419682 100644 (file)
@@ -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);
index bbca76ad6e1bf9b1997b6e3f61877ba58a35a4b8..eb095874540db977a34f38c0acd298e87e8df1c0 100644 (file)
@@ -41,6 +41,7 @@ static DEFINE_MUTEX(hws_sem_oom);
 
 static unsigned char hws_flush_all;
 static unsigned int hws_oom;
+static unsigned int hws_alert;
 static struct workqueue_struct *hws_wq;
 
 static unsigned int hws_state;
@@ -182,6 +183,9 @@ static void hws_ext_handler(struct ext_code ext_code,
        if (!(param32 & CPU_MF_INT_SF_MASK))
                return;
 
+       if (!hws_alert)
+               return;
+
        inc_irq_stat(IRQEXT_CMS);
        atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32);
 
@@ -941,6 +945,7 @@ int hwsampler_deallocate(void)
                goto deallocate_exit;
 
        irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
+       hws_alert = 0;
        deallocate_sdbt();
 
        hws_state = HWS_DEALLOCATED;
@@ -1055,6 +1060,7 @@ int hwsampler_shutdown(void)
 
                if (hws_state == HWS_STOPPED) {
                        irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
+                       hws_alert = 0;
                        deallocate_sdbt();
                }
                if (hws_wq) {
@@ -1129,6 +1135,7 @@ start_all_exit:
        hws_oom = 1;
        hws_flush_all = 0;
        /* now let them in, 1407 CPUMF external interrupts */
+       hws_alert = 1;
        irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
 
        return 0;
index 04e1b6a8536297cc61f2bacc0187c0b6a7089573..9ffe645d59898cbe5e9e88f95c9a08342b91bb00 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/oprofile.h>
+#include <linux/perf_event.h>
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -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"
                           "(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)
 {
        int retval;
@@ -76,13 +92,13 @@ static int oprofile_hwsampler_start(void)
        if (!hwsampler_running)
                return timer_ops.start();
 
-       retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks);
+       retval = perf_reserve_sampling();
        if (retval)
                return retval;
 
-       retval = hwsampler_start_all(oprofile_hw_interval);
+       retval = __oprofile_hwsampler_start();
        if (retval)
-               hwsampler_deallocate();
+               perf_release_sampling();
 
        return retval;
 }
@@ -96,6 +112,7 @@ static void oprofile_hwsampler_stop(void)
 
        hwsampler_stop_all();
        hwsampler_deallocate();
+       perf_release_sampling();
        return;
 }