perf/core: adding PMU driver specific configuration

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>
This commit is contained in:
Mathieu Poirier 2016-05-31 16:32:55 -06:00
parent 65ad7b198d
commit 1efb79086e
3 changed files with 29 additions and 0 deletions

View 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;

View 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,

View 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);