Commit a4be7c27 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar

perf_counter: Allow sharing of output channels

Provide the ability to configure a counter to send its output
to another (already existing) counter's output stream.
Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <20090819092023.980284148@chello.nl>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent fa289bec
...@@ -216,6 +216,7 @@ struct perf_counter_attr { ...@@ -216,6 +216,7 @@ struct perf_counter_attr {
#define PERF_COUNTER_IOC_REFRESH _IO ('$', 2) #define PERF_COUNTER_IOC_REFRESH _IO ('$', 2)
#define PERF_COUNTER_IOC_RESET _IO ('$', 3) #define PERF_COUNTER_IOC_RESET _IO ('$', 3)
#define PERF_COUNTER_IOC_PERIOD _IOW('$', 4, u64) #define PERF_COUNTER_IOC_PERIOD _IOW('$', 4, u64)
#define PERF_COUNTER_IOC_SET_OUTPUT _IO ('$', 5)
enum perf_counter_ioc_flags { enum perf_counter_ioc_flags {
PERF_IOC_FLAG_GROUP = 1U << 0, PERF_IOC_FLAG_GROUP = 1U << 0,
...@@ -415,6 +416,9 @@ enum perf_callchain_context { ...@@ -415,6 +416,9 @@ enum perf_callchain_context {
PERF_CONTEXT_MAX = (__u64)-4095, PERF_CONTEXT_MAX = (__u64)-4095,
}; };
#define PERF_FLAG_FD_NO_GROUP (1U << 0)
#define PERF_FLAG_FD_OUTPUT (1U << 1)
#ifdef __KERNEL__ #ifdef __KERNEL__
/* /*
* Kernel-internal data types and definitions: * Kernel-internal data types and definitions:
...@@ -536,6 +540,7 @@ struct perf_counter { ...@@ -536,6 +540,7 @@ struct perf_counter {
struct list_head sibling_list; struct list_head sibling_list;
int nr_siblings; int nr_siblings;
struct perf_counter *group_leader; struct perf_counter *group_leader;
struct perf_counter *output;
const struct pmu *pmu; const struct pmu *pmu;
enum perf_counter_active_state state; enum perf_counter_active_state state;
......
...@@ -1692,6 +1692,11 @@ static void free_counter(struct perf_counter *counter) ...@@ -1692,6 +1692,11 @@ static void free_counter(struct perf_counter *counter)
atomic_dec(&nr_task_counters); atomic_dec(&nr_task_counters);
} }
if (counter->output) {
fput(counter->output->filp);
counter->output = NULL;
}
if (counter->destroy) if (counter->destroy)
counter->destroy(counter); counter->destroy(counter);
...@@ -1977,6 +1982,8 @@ unlock: ...@@ -1977,6 +1982,8 @@ unlock:
return ret; return ret;
} }
int perf_counter_set_output(struct perf_counter *counter, int output_fd);
static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct perf_counter *counter = file->private_data; struct perf_counter *counter = file->private_data;
...@@ -2000,6 +2007,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -2000,6 +2007,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case PERF_COUNTER_IOC_PERIOD: case PERF_COUNTER_IOC_PERIOD:
return perf_counter_period(counter, (u64 __user *)arg); return perf_counter_period(counter, (u64 __user *)arg);
case PERF_COUNTER_IOC_SET_OUTPUT:
return perf_counter_set_output(counter, arg);
default: default:
return -ENOTTY; return -ENOTTY;
} }
...@@ -2270,6 +2280,11 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -2270,6 +2280,11 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
WARN_ON_ONCE(counter->ctx->parent_ctx); WARN_ON_ONCE(counter->ctx->parent_ctx);
mutex_lock(&counter->mmap_mutex); mutex_lock(&counter->mmap_mutex);
if (counter->output) {
ret = -EINVAL;
goto unlock;
}
if (atomic_inc_not_zero(&counter->mmap_count)) { if (atomic_inc_not_zero(&counter->mmap_count)) {
if (nr_pages != counter->data->nr_pages) if (nr_pages != counter->data->nr_pages)
ret = -EINVAL; ret = -EINVAL;
...@@ -2655,6 +2670,7 @@ static int perf_output_begin(struct perf_output_handle *handle, ...@@ -2655,6 +2670,7 @@ static int perf_output_begin(struct perf_output_handle *handle,
struct perf_counter *counter, unsigned int size, struct perf_counter *counter, unsigned int size,
int nmi, int sample) int nmi, int sample)
{ {
struct perf_counter *output_counter;
struct perf_mmap_data *data; struct perf_mmap_data *data;
unsigned int offset, head; unsigned int offset, head;
int have_lost; int have_lost;
...@@ -2664,13 +2680,17 @@ static int perf_output_begin(struct perf_output_handle *handle, ...@@ -2664,13 +2680,17 @@ static int perf_output_begin(struct perf_output_handle *handle,
u64 lost; u64 lost;
} lost_event; } lost_event;
rcu_read_lock();
/* /*
* For inherited counters we send all the output towards the parent. * For inherited counters we send all the output towards the parent.
*/ */
if (counter->parent) if (counter->parent)
counter = counter->parent; counter = counter->parent;
rcu_read_lock(); output_counter = rcu_dereference(counter->output);
if (output_counter)
counter = output_counter;
data = rcu_dereference(counter->data); data = rcu_dereference(counter->data);
if (!data) if (!data)
goto out; goto out;
...@@ -4218,6 +4238,57 @@ err_size: ...@@ -4218,6 +4238,57 @@ err_size:
goto out; goto out;
} }
int perf_counter_set_output(struct perf_counter *counter, int output_fd)
{
struct perf_counter *output_counter = NULL;
struct file *output_file = NULL;
struct perf_counter *old_output;
int fput_needed = 0;
int ret = -EINVAL;
if (!output_fd)
goto set;
output_file = fget_light(output_fd, &fput_needed);
if (!output_file)
return -EBADF;
if (output_file->f_op != &perf_fops)
goto out;
output_counter = output_file->private_data;
/* Don't chain output fds */
if (output_counter->output)
goto out;
/* Don't set an output fd when we already have an output channel */
if (counter->data)
goto out;
atomic_long_inc(&output_file->f_count);
set:
mutex_lock(&counter->mmap_mutex);
old_output = counter->output;
rcu_assign_pointer(counter->output, output_counter);
mutex_unlock(&counter->mmap_mutex);
if (old_output) {
/*
* we need to make sure no existing perf_output_*()
* is still referencing this counter.
*/
synchronize_rcu();
fput(old_output->filp);
}
ret = 0;
out:
fput_light(output_file, fput_needed);
return ret;
}
/** /**
* sys_perf_counter_open - open a performance counter, associate it to a task/cpu * sys_perf_counter_open - open a performance counter, associate it to a task/cpu
* *
...@@ -4240,7 +4311,7 @@ SYSCALL_DEFINE5(perf_counter_open, ...@@ -4240,7 +4311,7 @@ SYSCALL_DEFINE5(perf_counter_open,
int ret; int ret;
/* for future expandability... */ /* for future expandability... */
if (flags) if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT))
return -EINVAL; return -EINVAL;
ret = perf_copy_attr(attr_uptr, &attr); ret = perf_copy_attr(attr_uptr, &attr);
...@@ -4268,7 +4339,7 @@ SYSCALL_DEFINE5(perf_counter_open, ...@@ -4268,7 +4339,7 @@ SYSCALL_DEFINE5(perf_counter_open,
* Look up the group leader (we will attach this counter to it): * Look up the group leader (we will attach this counter to it):
*/ */
group_leader = NULL; group_leader = NULL;
if (group_fd != -1) { if (group_fd != -1 && !(flags & PERF_FLAG_FD_NO_GROUP)) {
ret = -EINVAL; ret = -EINVAL;
group_file = fget_light(group_fd, &fput_needed); group_file = fget_light(group_fd, &fput_needed);
if (!group_file) if (!group_file)
...@@ -4310,6 +4381,12 @@ SYSCALL_DEFINE5(perf_counter_open, ...@@ -4310,6 +4381,12 @@ SYSCALL_DEFINE5(perf_counter_open,
if (!counter_file) if (!counter_file)
goto err_free_put_context; goto err_free_put_context;
if (flags & PERF_FLAG_FD_OUTPUT) {
ret = perf_counter_set_output(counter, group_fd);
if (ret)
goto err_free_put_context;
}
counter->filp = counter_file; counter->filp = counter_file;
WARN_ON_ONCE(ctx->parent_ctx); WARN_ON_ONCE(ctx->parent_ctx);
mutex_lock(&ctx->mutex); mutex_lock(&ctx->mutex);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment