* [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp
@ 2017-11-29 7:20 Yonghong Song
2017-11-29 7:20 ` [PATCH net-next 1/2] " Yonghong Song
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Yonghong Song @ 2017-11-29 7:20 UTC (permalink / raw)
To: peterz, rostedt, ast, daniel, kafai, netdev; +Cc: kernel-team
Commit e87c6bc3852b ("bpf: permit multiple bpf attachments
for a single perf event") added support to attach multiple
bpf programs to a single perf event. Given a perf event
(kprobe, uprobe, or kernel tracepoint), the perf ioctl interface
is used to query bpf programs attached to the same trace event.
The same ioctl interface is also used to attach bpf program.
Patch #1 had the core implementation and patch #2 added
a test case in tools bpf selftests suite.
Yonghong Song (2):
bpf/tracing: allow user space to query prog array on the same tp
bpf/tracing: add a bpf test for new ioctl query interface
include/linux/bpf.h | 4 ++
include/uapi/linux/perf_event.h | 6 ++
kernel/bpf/core.c | 24 +++++++
kernel/events/core.c | 3 +
kernel/trace/bpf_trace.c | 23 +++++++
tools/include/uapi/linux/perf_event.h | 6 ++
tools/testing/selftests/bpf/Makefile | 2 +-
tools/testing/selftests/bpf/test_progs.c | 107 +++++++++++++++++++++++++++++++
8 files changed, 174 insertions(+), 1 deletion(-)
--
2.9.5
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH net-next 1/2] bpf/tracing: allow user space to query prog array on the same tp 2017-11-29 7:20 [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp Yonghong Song @ 2017-11-29 7:20 ` Yonghong Song 2017-11-30 17:27 ` Daniel Borkmann 2017-11-29 7:20 ` [PATCH net-next 2/2] bpf/tracing: add a bpf test for new ioctl query interface Yonghong Song 2017-11-30 5:32 ` [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp Alexei Starovoitov 2 siblings, 1 reply; 6+ messages in thread From: Yonghong Song @ 2017-11-29 7:20 UTC (permalink / raw) To: peterz, rostedt, ast, daniel, kafai, netdev; +Cc: kernel-team 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> --- include/linux/bpf.h | 4 ++++ include/uapi/linux/perf_event.h | 6 ++++++ kernel/bpf/core.c | 24 ++++++++++++++++++++++++ kernel/events/core.c | 3 +++ kernel/trace/bpf_trace.c | 23 +++++++++++++++++++++++ 5 files changed, 60 insertions(+) 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 b9f8686..40e3b8d 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1461,6 +1461,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(); @@ -1544,6 +1546,28 @@ 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) +{ + struct bpf_prog **prog; + u32 cnt = 0; + + if (array) { + for (prog = array->progs; *prog; prog++) + if (*prog != &dummy_bpf_prog.prog) + cnt++; + } + + if (copy_to_user(prog_cnt, &cnt, sizeof(cnt))) + return -EFAULT; + + if (cnt == 0) + 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 9404c63..93aec2c 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 27d1f4f..7fb7f74 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -812,3 +812,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; +} -- 2.9.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next 1/2] bpf/tracing: allow user space to query prog array on the same tp 2017-11-29 7:20 ` [PATCH net-next 1/2] " Yonghong Song @ 2017-11-30 17:27 ` Daniel Borkmann 2017-11-30 19:12 ` Yonghong Song 0 siblings, 1 reply; 6+ messages in thread From: Daniel Borkmann @ 2017-11-30 17:27 UTC (permalink / raw) To: Yonghong Song, peterz, rostedt, ast, kafai, netdev; +Cc: kernel-team On 11/29/2017 08:20 AM, 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> > --- > include/linux/bpf.h | 4 ++++ > include/uapi/linux/perf_event.h | 6 ++++++ > kernel/bpf/core.c | 24 ++++++++++++++++++++++++ > kernel/events/core.c | 3 +++ > kernel/trace/bpf_trace.c | 23 +++++++++++++++++++++++ > 5 files changed, 60 insertions(+) > > 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 b9f8686..40e3b8d 100644 > --- a/kernel/bpf/core.c > +++ b/kernel/bpf/core.c > @@ -1461,6 +1461,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(); > @@ -1544,6 +1546,28 @@ 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) > +{ > + struct bpf_prog **prog; > + u32 cnt = 0; > + > + if (array) { > + for (prog = array->progs; *prog; prog++) > + if (*prog != &dummy_bpf_prog.prog) > + cnt++; > + } > + > + if (copy_to_user(prog_cnt, &cnt, sizeof(cnt))) > + return -EFAULT; > + > + if (cnt == 0) > + return 0; One minor thing I still noticed in bpf_prog_array_copy_info() was that potentially we could return 0 as well if request_cnt was 0 if users only want to query if progs are present (resp. how many attached) but don't care which ones. Otherwise, in bpf_prog_array_copy_to_user() it tries to copy as much prog ids as present if request_cnt == 0. Can we handle this in user space e.g. by having an exposed max upper limit where user space can define the prog id array with? > + 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 9404c63..93aec2c 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 27d1f4f..7fb7f74 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -812,3 +812,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; > +} > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next 1/2] bpf/tracing: allow user space to query prog array on the same tp 2017-11-30 17:27 ` Daniel Borkmann @ 2017-11-30 19:12 ` Yonghong Song 0 siblings, 0 replies; 6+ messages in thread From: Yonghong Song @ 2017-11-30 19:12 UTC (permalink / raw) To: Daniel Borkmann, peterz, rostedt, ast, kafai, netdev; +Cc: kernel-team On 11/30/17 9:27 AM, Daniel Borkmann wrote: > On 11/29/2017 08:20 AM, 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> >> --- >> include/linux/bpf.h | 4 ++++ >> include/uapi/linux/perf_event.h | 6 ++++++ >> kernel/bpf/core.c | 24 ++++++++++++++++++++++++ >> kernel/events/core.c | 3 +++ >> kernel/trace/bpf_trace.c | 23 +++++++++++++++++++++++ >> 5 files changed, 60 insertions(+) >> >> 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 b9f8686..40e3b8d 100644 >> --- a/kernel/bpf/core.c >> +++ b/kernel/bpf/core.c >> @@ -1461,6 +1461,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(); >> @@ -1544,6 +1546,28 @@ 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) >> +{ >> + struct bpf_prog **prog; >> + u32 cnt = 0; >> + >> + if (array) { >> + for (prog = array->progs; *prog; prog++) >> + if (*prog != &dummy_bpf_prog.prog) >> + cnt++; >> + } >> + >> + if (copy_to_user(prog_cnt, &cnt, sizeof(cnt))) >> + return -EFAULT; >> + >> + if (cnt == 0) >> + return 0; > > One minor thing I still noticed in bpf_prog_array_copy_info() was > that potentially we could return 0 as well if request_cnt was 0 if > users only want to query if progs are present (resp. how many attached) > but don't care which ones. > > Otherwise, in bpf_prog_array_copy_to_user() it tries to copy as much > prog ids as present if request_cnt == 0. Can we handle this in user > space e.g. by having an exposed max upper limit where user space can > define the prog id array with? For cgroup program array, in kernel/bpf/cgroup.c, we have if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) return -EFAULT; if (attr->query.prog_cnt == 0 || !prog_ids || !cnt) /* return early if user requested only program count + flags */ return 0; If user requested prog_cnt is 0, or user provided prog_ids pointer is NULL, or the number of actual programs is 0, we will just return fine. I guess I can use the same logic here. Thanks for spotting this issue! Will add additional tests as well for this and send v2. > >> + 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 9404c63..93aec2c 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 27d1f4f..7fb7f74 100644 >> --- a/kernel/trace/bpf_trace.c >> +++ b/kernel/trace/bpf_trace.c >> @@ -812,3 +812,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; >> +} >> > ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next 2/2] bpf/tracing: add a bpf test for new ioctl query interface 2017-11-29 7:20 [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp Yonghong Song 2017-11-29 7:20 ` [PATCH net-next 1/2] " Yonghong Song @ 2017-11-29 7:20 ` Yonghong Song 2017-11-30 5:32 ` [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp Alexei Starovoitov 2 siblings, 0 replies; 6+ messages in thread From: Yonghong Song @ 2017-11-29 7:20 UTC (permalink / raw) To: peterz, rostedt, ast, daniel, kafai, netdev; +Cc: kernel-team Added a subtest in test_progs. The tracepoint is sched/sched_switch. Multiple bpf programs are attached to this tracepoint and the query interface is exercised. Signed-off-by: Yonghong Song <yhs@fb.com> --- tools/include/uapi/linux/perf_event.h | 6 ++ tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/test_progs.c | 107 +++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h index 362493a..8523db0 100644 --- a/tools/include/uapi/linux/perf_event.h +++ b/tools/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/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 333a486..c49c08a 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -17,7 +17,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ - sockmap_verdict_prog.o dev_cgroup.o + sockmap_verdict_prog.o dev_cgroup.o test_tracepoint.o TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 6942753..39e7f7e 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -21,8 +21,10 @@ typedef __u16 __sum16; #include <linux/ipv6.h> #include <linux/tcp.h> #include <linux/filter.h> +#include <linux/perf_event.h> #include <linux/unistd.h> +#include <sys/ioctl.h> #include <sys/wait.h> #include <sys/resource.h> #include <sys/types.h> @@ -617,6 +619,110 @@ static void test_obj_name(void) } } +static void test_tp_attach_query(void) +{ + const char *file = "./test_tracepoint.o"; + const int num_progs = 3; + __u32 duration = 0, info_len, prog_ids[num_progs], saved_prog_ids[num_progs]; + int i, j, bytes, efd, err, prog_fd[num_progs], pmu_fd[num_progs]; + struct perf_event_query_bpf query = {}; + struct perf_event_attr attr = {}; + struct bpf_object *obj[num_progs]; + struct bpf_prog_info prog_info; + char buf[256]; + + snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/sched/sched_switch/id"); + efd = open(buf, O_RDONLY, 0); + if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno)) + return; + bytes = read(efd, buf, sizeof(buf)); + close(efd); + if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "read", "bytes %d errno %d\n", bytes, errno)) + return; + + attr.config = strtol(buf, NULL, 0); + attr.type = PERF_TYPE_TRACEPOINT; + attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN; + attr.sample_period = 1; + attr.wakeup_events = 1; + + for (i = 0; i < num_progs; i++) { + err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj[i], &prog_fd[i]); + if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) + goto cleanup1; + + bzero(&prog_info, sizeof(prog_info)); + prog_info.jited_prog_len = 0; + prog_info.xlated_prog_len = 0; + prog_info.nr_map_ids = 0; + info_len = sizeof(prog_info); + err = bpf_obj_get_info_by_fd(prog_fd[i], &prog_info, &info_len); + if (CHECK(err, "bpf_obj_get_info_by_fd", "err %d errno %d\n", err, errno)) + goto cleanup1; + saved_prog_ids[i] = prog_info.id; + + pmu_fd[i] = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu 0 */, + -1 /* group id */, 0 /* flags */); + if (CHECK(pmu_fd[i] < 0, "perf_event_open", "err %d errno %d\n", pmu_fd[i], errno)) + goto cleanup2; + err = ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE, 0); + if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", err, errno)) + goto cleanup3; + + if (i == 0) { + /* check NULL prog array query */ + query.prog_ids = (__u64)prog_ids; + query.prog_cnt = num_progs; + err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, &query); + if (CHECK(err || query.prog_cnt != 0, "perf_event_ioc_query_bpf", + "err %d errno %d query.prog_cnt %u\n", err, errno, query.prog_cnt)) + goto cleanup3; + } + + err = ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, prog_fd[i]); + if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", err, errno)) + goto cleanup3; + + if (i == 1) { + /* do a few negative tests */ + query.prog_ids = 0; + query.prog_cnt = num_progs; + err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, &query); + if (CHECK(!err || errno != EFAULT, "perf_event_ioc_query_bpf", + "err %d errno %d query.prog_cnt %u\n", err, errno, query.prog_cnt)) + goto cleanup3; + + query.prog_ids = (__u64)prog_ids; + query.prog_cnt = 1; + err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, &query); + if (CHECK(!err || errno != ENOSPC, "perf_event_ioc_query_bpf", + "err %d errno %d query.prog_cnt %u\n", err, errno, query.prog_cnt)) + goto cleanup3; + } + + query.prog_ids = (__u64)prog_ids; + query.prog_cnt = num_progs; + err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, &query); + if (CHECK(err || query.prog_cnt != (i + 1), "perf_event_ioc_query_bpf", + "err %d errno %d query.prog_cnt %u\n", err, errno, query.prog_cnt)) + goto cleanup3; + for (j = 0; j < i + 1; j++) + if (CHECK(saved_prog_ids[j] != prog_ids[j], "perf_event_ioc_query_bpf", + "#%d saved_prog_id %x query prog_id %x\n", j, saved_prog_ids[j], prog_ids[j])) + goto cleanup3; + } + + i = num_progs - 1; + for (; i >= 0; i--) { + cleanup3: + ioctl(pmu_fd[i], PERF_EVENT_IOC_DISABLE); + cleanup2: + close(pmu_fd[i]); + cleanup1: + bpf_object__close(obj[i]); + } +} + int main(void) { struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; @@ -630,6 +736,7 @@ int main(void) test_bpf_obj_id(); test_pkt_md_access(); test_obj_name(); + test_tp_attach_query(); printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; -- 2.9.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp 2017-11-29 7:20 [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp Yonghong Song 2017-11-29 7:20 ` [PATCH net-next 1/2] " Yonghong Song 2017-11-29 7:20 ` [PATCH net-next 2/2] bpf/tracing: add a bpf test for new ioctl query interface Yonghong Song @ 2017-11-30 5:32 ` Alexei Starovoitov 2 siblings, 0 replies; 6+ messages in thread From: Alexei Starovoitov @ 2017-11-30 5:32 UTC (permalink / raw) To: Yonghong Song, peterz, rostedt, daniel, kafai, netdev; +Cc: kernel-team On 11/28/17 11:20 PM, 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. Given a perf event > (kprobe, uprobe, or kernel tracepoint), the perf ioctl interface > is used to query bpf programs attached to the same trace event. > The same ioctl interface is also used to attach bpf program. > Patch #1 had the core implementation and patch #2 added > a test case in tools bpf selftests suite. We actually had an implementation of the same tracepoint+bpf introspection via BPF_PROG_QUERY command that we use for cgroup+bpf, but it looks cleaner to use ioctl() style of api here, since attach to tracepoint/kuprobe is also done via ioctl. For the set: Acked-by: Alexei Starovoitov <ast@kernel.org> The patch touches 3 lines in events/core.c but most likely it won't conflict with anything in tip, so we plan to have this set in bpf-next.git -> net-next.git only. Thanks ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2017-11-30 19:20 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-11-29 7:20 [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp Yonghong Song 2017-11-29 7:20 ` [PATCH net-next 1/2] " Yonghong Song 2017-11-30 17:27 ` Daniel Borkmann 2017-11-30 19:12 ` Yonghong Song 2017-11-29 7:20 ` [PATCH net-next 2/2] bpf/tracing: add a bpf test for new ioctl query interface Yonghong Song 2017-11-30 5:32 ` [PATCH net-next 0/2] bpf/tracing: allow user space to query prog array on the same tp Alexei Starovoitov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox