perf, x86: Provide means for disabling userspace RDPMC
authorPeter Zijlstra <a.p.zijlstra@chello.nl>
Sun, 20 Nov 2011 22:30:47 +0000 (23:30 +0100)
committerIngo Molnar <mingo@elte.hu>
Wed, 21 Dec 2011 10:01:11 +0000 (11:01 +0100)
Allow the disabling of RDPMC via a pmu specific attribute:

  echo 0 > /sys/bus/event_source/devices/cpu/rdpmc

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Cc: Arun Sharma <asharma@fb.com>
Link: http://lkml.kernel.org/n/tip-pqeog465zo5hsimtkfz73f27@git.kernel.org
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event.h
include/linux/perf_event.h
kernel/events/core.c

index 53b5699101756da9c35ff7608d8b635b965a586f..116b040a73a8e2aea3f0eedfe6ace24c46d2ba0b 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/cpu.h>
 #include <linux/bitops.h>
 #include <linux/slab.h>
 #include <linux/cpu.h>
 #include <linux/bitops.h>
+#include <linux/device.h>
 
 #include <asm/apic.h>
 #include <asm/stacktrace.h>
 
 #include <asm/apic.h>
 #include <asm/stacktrace.h>
@@ -1210,7 +1211,8 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
                break;
 
        case CPU_STARTING:
                break;
 
        case CPU_STARTING:
-               set_in_cr4(X86_CR4_PCE);
+               if (x86_pmu.attr_rdpmc)
+                       set_in_cr4(X86_CR4_PCE);
                if (x86_pmu.cpu_starting)
                        x86_pmu.cpu_starting(cpu);
                break;
                if (x86_pmu.cpu_starting)
                        x86_pmu.cpu_starting(cpu);
                break;
@@ -1320,6 +1322,8 @@ static int __init init_hw_perf_events(void)
                }
        }
 
                }
        }
 
+       x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */
+
        pr_info("... version:                %d\n",     x86_pmu.version);
        pr_info("... bit width:              %d\n",     x86_pmu.cntval_bits);
        pr_info("... generic registers:      %d\n",     x86_pmu.num_counters);
        pr_info("... version:                %d\n",     x86_pmu.version);
        pr_info("... bit width:              %d\n",     x86_pmu.cntval_bits);
        pr_info("... generic registers:      %d\n",     x86_pmu.num_counters);
@@ -1555,10 +1559,59 @@ static int x86_pmu_event_idx(struct perf_event *event)
        return idx + 1;
 }
 
        return idx + 1;
 }
 
+static ssize_t get_attr_rdpmc(struct device *cdev,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       return snprintf(buf, 40, "%d\n", x86_pmu.attr_rdpmc);
+}
+
+static void change_rdpmc(void *info)
+{
+       bool enable = !!(unsigned long)info;
+
+       if (enable)
+               set_in_cr4(X86_CR4_PCE);
+       else
+               clear_in_cr4(X86_CR4_PCE);
+}
+
+static ssize_t set_attr_rdpmc(struct device *cdev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       unsigned long val = simple_strtoul(buf, NULL, 0);
+
+       if (!!val != !!x86_pmu.attr_rdpmc) {
+               x86_pmu.attr_rdpmc = !!val;
+               smp_call_function(change_rdpmc, (void *)val, 1);
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(rdpmc, S_IRUSR | S_IWUSR, get_attr_rdpmc, set_attr_rdpmc);
+
+static struct attribute *x86_pmu_attrs[] = {
+       &dev_attr_rdpmc.attr,
+       NULL,
+};
+
+static struct attribute_group x86_pmu_attr_group = {
+       .attrs = x86_pmu_attrs,
+};
+
+static const struct attribute_group *x86_pmu_attr_groups[] = {
+       &x86_pmu_attr_group,
+       NULL,
+};
+
 static struct pmu pmu = {
        .pmu_enable     = x86_pmu_enable,
        .pmu_disable    = x86_pmu_disable,
 
 static struct pmu pmu = {
        .pmu_enable     = x86_pmu_enable,
        .pmu_disable    = x86_pmu_disable,
 
+       .attr_groups    = x86_pmu_attr_groups,
+
        .event_init     = x86_pmu_event_init,
 
        .add            = x86_pmu_add,
        .event_init     = x86_pmu_event_init,
 
        .add            = x86_pmu_add,
index 8944062f46e284faaa65ec043403352ea7c5f14c..513d617b93c43f7145db0e9be5e62ba0d467461f 100644 (file)
@@ -307,6 +307,14 @@ struct x86_pmu {
        struct x86_pmu_quirk *quirks;
        int             perfctr_second_write;
 
        struct x86_pmu_quirk *quirks;
        int             perfctr_second_write;
 
+       /*
+        * sysfs attrs
+        */
+       int             attr_rdpmc;
+
+       /*
+        * CPU Hotplug hooks
+        */
        int             (*cpu_prepare)(int cpu);
        void            (*cpu_starting)(int cpu);
        void            (*cpu_dying)(int cpu);
        int             (*cpu_prepare)(int cpu);
        void            (*cpu_starting)(int cpu);
        void            (*cpu_dying)(int cpu);
index 02545e6df95b5380ec64a2c6a96e7b4597803625..5311b79fe62cc2d684425e27447f7722592bdb38 100644 (file)
@@ -615,6 +615,7 @@ struct pmu {
        struct list_head                entry;
 
        struct device                   *dev;
        struct list_head                entry;
 
        struct device                   *dev;
+       const struct attribute_group    **attr_groups;
        char                            *name;
        int                             type;
 
        char                            *name;
        int                             type;
 
index 05affc3878ff931fbc475e86e8d0e8210c539d7e..dcd4049e92fcf1832aecb50ff5dfa94245108293 100644 (file)
@@ -5505,6 +5505,7 @@ static int pmu_dev_alloc(struct pmu *pmu)
        if (!pmu->dev)
                goto out;
 
        if (!pmu->dev)
                goto out;
 
+       pmu->dev->groups = pmu->attr_groups;
        device_initialize(pmu->dev);
        ret = dev_set_name(pmu->dev, "%s", pmu->name);
        if (ret)
        device_initialize(pmu->dev);
        ret = dev_set_name(pmu->dev, "%s", pmu->name);
        if (ret)