Message ID | 20171206063129.3730876-2-yhs@fb.com |
---|---|
State | Changes Requested, archived |
Delegated to: | BPF Maintainers |
Headers | show |
Series | [net-next,v2,1/2] bpf/tracing: allow user space to query prog array on the same tp | expand |
On Tue, Dec 05, 2017 at 10:31:28PM -0800, Yonghong Song wrote: > Commit e87c6bc3852b ("bpf: permit multiple bpf attachments > for a single perf event") added support to attach multiple > bpf programs to a single perf event. > Commit 2541517c32be ("tracing, perf: Implement BPF programs > attached to kprobes") utilized the existing perf ioctl > interface and added the command PERF_EVENT_IOC_SET_BPF > to attach a bpf program to a tracepoint. > > This patch adds a new ioctl > command, given a perf event fd, to query the bpf program array > attached to the same perf tracepoint event. > > The new uapi ioctl command: > PERF_EVENT_IOC_QUERY_BPF > > The new uapi/linux/perf_event.h structure: > struct perf_event_query_bpf { > __u64 prog_ids; > __u32 prog_cnt; > }; > > The usage: > struct perf_event_query_bpf query; > query.prog_ids = (__u64)usr_prog_ids_buf; > query.prog_cnt = usr_prog_ids_buf_len; > err = ioctl(pmu_efd, PERF_EVENT_IOC_QUERY_BPF, &query); > > Signed-off-by: Yonghong Song <yhs@fb.com> > Acked-by: Alexei Starovoitov <ast@kernel.org> Can you please fix that example to make it clear that prog_ids is in fact a pointer to an array of size prog_cnt. Ideally also describing what the type of array is. In fact, would not something like: struct perf_event_query_bpf { __u32 len; __u32 __reserved; __u64 ids[0]; }; be a much clearer interface? Also, you forgot to tell us why we need this interface at all.
On Wed, Dec 06, 2017 at 12:56:36PM +0100, Peter Zijlstra wrote: > On Tue, Dec 05, 2017 at 10:31:28PM -0800, Yonghong Song wrote: > > Commit e87c6bc3852b ("bpf: permit multiple bpf attachments > > for a single perf event") added support to attach multiple > > bpf programs to a single perf event. > > Commit 2541517c32be ("tracing, perf: Implement BPF programs > > attached to kprobes") utilized the existing perf ioctl > > interface and added the command PERF_EVENT_IOC_SET_BPF > > to attach a bpf program to a tracepoint. > > > > This patch adds a new ioctl > > command, given a perf event fd, to query the bpf program array > > attached to the same perf tracepoint event. > > > > The new uapi ioctl command: > > PERF_EVENT_IOC_QUERY_BPF > > > > The new uapi/linux/perf_event.h structure: > > struct perf_event_query_bpf { > > __u64 prog_ids; > > __u32 prog_cnt; > > }; > > > > The usage: > > struct perf_event_query_bpf query; > > query.prog_ids = (__u64)usr_prog_ids_buf; > > query.prog_cnt = usr_prog_ids_buf_len; > > err = ioctl(pmu_efd, PERF_EVENT_IOC_QUERY_BPF, &query); > > > > Signed-off-by: Yonghong Song <yhs@fb.com> > > Acked-by: Alexei Starovoitov <ast@kernel.org> > > Can you please fix that example to make it clear that prog_ids is in > fact a pointer to an array of size prog_cnt. Ideally also describing > what the type of array is. > > In fact, would not something like: > > struct perf_event_query_bpf { > __u32 len; > __u32 __reserved; I suppose we could use this field to store the number of entries returned, retaining the len to indicate how large the structure is. > __u64 ids[0]; > }; > > be a much clearer interface? > > Also, you forgot to tell us why we need this interface at all.
On 12/6/17 5:16 AM, Peter Zijlstra wrote: > On Wed, Dec 06, 2017 at 12:56:36PM +0100, Peter Zijlstra wrote: >> On Tue, Dec 05, 2017 at 10:31:28PM -0800, Yonghong Song wrote: >>> Commit e87c6bc3852b ("bpf: permit multiple bpf attachments >>> for a single perf event") added support to attach multiple >>> bpf programs to a single perf event. >>> Commit 2541517c32be ("tracing, perf: Implement BPF programs >>> attached to kprobes") utilized the existing perf ioctl >>> interface and added the command PERF_EVENT_IOC_SET_BPF >>> to attach a bpf program to a tracepoint. >>> >>> This patch adds a new ioctl >>> command, given a perf event fd, to query the bpf program array >>> attached to the same perf tracepoint event. >>> >>> The new uapi ioctl command: >>> PERF_EVENT_IOC_QUERY_BPF >>> >>> The new uapi/linux/perf_event.h structure: >>> struct perf_event_query_bpf { >>> __u64 prog_ids; >>> __u32 prog_cnt; >>> }; >>> >>> The usage: >>> struct perf_event_query_bpf query; >>> query.prog_ids = (__u64)usr_prog_ids_buf; >>> query.prog_cnt = usr_prog_ids_buf_len; >>> err = ioctl(pmu_efd, PERF_EVENT_IOC_QUERY_BPF, &query); >>> >>> Signed-off-by: Yonghong Song <yhs@fb.com> >>> Acked-by: Alexei Starovoitov <ast@kernel.org> >> >> Can you please fix that example to make it clear that prog_ids is in >> fact a pointer to an array of size prog_cnt. Ideally also describing >> what the type of array is. Right. Will address this with more descriptions in the commit message and also add some comments in the perf_event.h. >> >> In fact, would not something like: >> >> struct perf_event_query_bpf { >> __u32 len; >> __u32 __reserved; > > I suppose we could use this field to store the number of entries > returned, retaining the len to indicate how large the structure is. > >> __u64 ids[0]; >> }; >> >> be a much clearer interface? Yes, this is clearer and may be consistent with perf interface. FYI, my old interface is similar to the BPF query interface below: struct { /* anonymous struct used by BPF_PROG_QUERY command */ __u32 target_fd; /* container object to query */ __u32 attach_type; __u32 query_flags; __u32 attach_flags; __aligned_u64 prog_ids; __u32 prog_cnt; } query; >> >> Also, you forgot to tell us why we need this interface at all. Right. Will add some descriptions for this.
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e55e425..f812ac5 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -254,6 +254,7 @@ typedef unsigned long (*bpf_ctx_copy_t)(void *dst, const void *src, u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy); +int bpf_event_query_prog_array(struct perf_event *event, void __user *info); int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr); @@ -285,6 +286,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs, struct bpf_prog *old_prog); +int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array, + __u32 __user *prog_ids, u32 request_cnt, + __u32 __user *prog_cnt); int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array, struct bpf_prog *exclude_prog, struct bpf_prog *include_prog, diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index b9a4953..fee0b43 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -418,6 +418,11 @@ struct perf_event_attr { __u16 __reserved_2; /* align to __u64 */ }; +struct perf_event_query_bpf { + __u64 prog_ids; + __u32 prog_cnt; +}; + #define perf_flags(attr) (*(&(attr)->read_format + 1)) /* @@ -433,6 +438,7 @@ struct perf_event_attr { #define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) #define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) #define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32) +#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 86b50aa..35b427aa 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1462,6 +1462,8 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, rcu_read_lock(); prog = rcu_dereference(progs)->progs; for (; *prog; prog++) { + if (*prog == &dummy_bpf_prog.prog) + continue; id = (*prog)->aux->id; if (copy_to_user(prog_ids + i, &id, sizeof(id))) { rcu_read_unlock(); @@ -1545,6 +1547,25 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array, return 0; } +int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array, + __u32 __user *prog_ids, u32 request_cnt, + __u32 __user *prog_cnt) +{ + u32 cnt = 0; + + if (array) + cnt = bpf_prog_array_length(array); + + if (copy_to_user(prog_cnt, &cnt, sizeof(cnt))) + return -EFAULT; + + /* return early if user requested only program count or nothing to copy */ + if (!request_cnt || !prog_ids || !cnt) + return 0; + + return bpf_prog_array_copy_to_user(array, prog_ids, request_cnt); +} + static void bpf_prog_free_deferred(struct work_struct *work) { struct bpf_prog_aux *aux; diff --git a/kernel/events/core.c b/kernel/events/core.c index 16beab4..f10609e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4723,6 +4723,9 @@ static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned lon rcu_read_unlock(); return 0; } + + case PERF_EVENT_IOC_QUERY_BPF: + return bpf_event_query_prog_array(event, (void __user *)arg); default: return -ENOTTY; } diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 0ce99c3..81eedb2 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -820,3 +820,26 @@ void perf_event_detach_bpf_prog(struct perf_event *event) unlock: mutex_unlock(&bpf_event_mutex); } + +int bpf_event_query_prog_array(struct perf_event *event, void __user *info) +{ + struct perf_event_query_bpf __user *uquery = info; + struct perf_event_query_bpf query = {}; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (event->attr.type != PERF_TYPE_TRACEPOINT) + return -EINVAL; + if (copy_from_user(&query, uquery, sizeof(query))) + return -EFAULT; + + mutex_lock(&bpf_event_mutex); + ret = bpf_prog_array_copy_info(event->tp_event->prog_array, + u64_to_user_ptr(query.prog_ids), + query.prog_cnt, + &uquery->prog_cnt); + mutex_unlock(&bpf_event_mutex); + + return ret; +}