perf/core: adding PMU driver specific configuration
authorMathieu Poirier <mathieu.poirier@linaro.org>
Tue, 31 May 2016 22:32:55 +0000 (16:32 -0600)
committerMathieu Poirier <mathieu.poirier@linaro.org>
Mon, 20 Jun 2016 17:09:45 +0000 (11:09 -0600)
It is entirely possible that some PMUs need specific configuration
that is currently not found in the perf options before a session
can be setup.

It is the case for the CoreSight PMU where a sink needs to be
provided.  That sink doesn't fall in any of the current perf
options.

As such this patch adds the capability to receive driver
specific configuration using the existing ioctl() mechanism.
Once the configuration has been pushed down the kernel PMU
callbacks are used to deal with the information sent from user
space.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
include/linux/perf_event.h
include/uapi/linux/perf_event.h
kernel/events/core.c

index f9828a48f16addab4737683f3c8345ab87e52a0a..6e02e62fa1d9015a4f054f2f8060845a03ce8cb5 100644 (file)
@@ -391,6 +391,14 @@ struct pmu {
         * Filter events for PMU-specific reasons.
         */
        int (*filter_match)             (struct perf_event *event); /* optional */
+
+       /*
+        * Initial, PMU driver specific configuration.
+        */
+       int (*get_drv_configs)          (struct perf_event *event,
+                                        void __user *arg); /* optional */
+       void (*free_drv_configs)        (struct perf_event *event);
+                                       /* optional */
 };
 
 /**
@@ -558,6 +566,7 @@ struct perf_event {
        struct irq_work                 pending;
 
        atomic_t                        event_limit;
+       struct list_head                drv_configs;
 
        void (*destroy)(struct perf_event *);
        struct rcu_head                 rcu_head;
index d801bb0d9f6d2b08b6be600565716d045d8b85ca..cd2e88450faaddd516a2ae6665744e5888d7bdfd 100644 (file)
@@ -395,6 +395,7 @@ struct perf_event_attr {
 #define PERF_EVENT_IOC_SET_FILTER      _IOW('$', 6, char *)
 #define PERF_EVENT_IOC_ID              _IOR('$', 7, __u64 *)
 #define PERF_EVENT_IOC_SET_BPF         _IOW('$', 8, __u32)
+#define PERF_EVENT_IOC_SET_DRV_CONFIGS _IOW('$', 10, char *)
 
 enum perf_event_ioc_flags {
        PERF_IOC_FLAG_GROUP             = 1U << 0,
index cda4b292135a85f3fe75ea0bae173c34be4c9b24..59552da6d6aac03b1f4d24a08d7676bfa5db7f25 100644 (file)
@@ -3729,6 +3729,9 @@ static void __free_event(struct perf_event *event)
        if (event->destroy)
                event->destroy(event);
 
+       if (event->pmu->free_drv_configs)
+               event->pmu->free_drv_configs(event);
+
        if (event->ctx)
                put_ctx(event->ctx);
 
@@ -4281,6 +4284,8 @@ static int perf_event_set_output(struct perf_event *event,
                                 struct perf_event *output_event);
 static int perf_event_set_filter(struct perf_event *event, void __user *arg);
 static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd);
+static int perf_event_drv_configs(struct perf_event *event,
+                                 void __user *arg);
 
 static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned long arg)
 {
@@ -4337,6 +4342,9 @@ static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned lon
        case PERF_EVENT_IOC_SET_BPF:
                return perf_event_set_bpf_prog(event, arg);
 
+       case PERF_EVENT_IOC_SET_DRV_CONFIGS:
+               return perf_event_drv_configs(event, (void __user *)arg);
+
        default:
                return -ENOTTY;
        }
@@ -4369,6 +4377,7 @@ static long perf_compat_ioctl(struct file *file, unsigned int cmd,
        switch (_IOC_NR(cmd)) {
        case _IOC_NR(PERF_EVENT_IOC_SET_FILTER):
        case _IOC_NR(PERF_EVENT_IOC_ID):
+       case _IOC_NR(PERF_EVENT_IOC_SET_DRV_CONFIGS):
                /* Fix up pointer size (usually 4 -> 8 in 32-on-64-bit case */
                if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
                        cmd &= ~IOCSIZE_MASK;
@@ -7268,6 +7277,15 @@ void perf_bp_event(struct perf_event *bp, void *data)
 }
 #endif
 
+static int perf_event_drv_configs(struct perf_event *event,
+                                 void __user *arg)
+{
+       if (!event->pmu->get_drv_configs)
+               return -EINVAL;
+
+       return event->pmu->get_drv_configs(event, arg);
+}
+
 /*
  * hrtimer based swevent callback
  */
@@ -8004,6 +8022,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
        INIT_LIST_HEAD(&event->sibling_list);
        INIT_LIST_HEAD(&event->rb_entry);
        INIT_LIST_HEAD(&event->active_entry);
+       INIT_LIST_HEAD(&event->drv_configs);
        INIT_HLIST_NODE(&event->hlist_entry);