ARM: perf: allow armpmu to implement mode exclusion
authorWill Deacon <will.deacon@arm.com>
Tue, 19 Jul 2011 10:57:30 +0000 (11:57 +0100)
committerWill Deacon <will.deacon@arm.com>
Wed, 31 Aug 2011 09:18:01 +0000 (10:18 +0100)
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 <jamie@jamieiles.com>
Reviewed-by: Jean Pihet <j-pihet@ti.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm/kernel/perf_event.c

index c668c91d0c0a57649942463e2778742e6379ad44..5d60c9c259648b96d71dd5944576290b8e20622b 100644 (file)
@@ -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);
@@ -477,6 +479,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)
 {
@@ -501,35 +510,32 @@ __hw_perf_event_init(struct perf_event *event)
                return mapping;
        }
 
+       /*
+        * 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;