History

bpf/tracing: allow user space to query prog array on the same tp

Commit e87c6bc3852b ("bpf: permit multiple bpf attachments for a single perf event") added support to attach multiple bpf programs to a single perf event. Although this provides flexibility, users may want to know what other bpf programs attached to the same tp interface. Besides getting visibility for the underlying bpf system, such information may also help consolidate multiple bpf programs, understand potential performance issues due to a large array, and debug (e.g., one bpf program which overwrites return code may impact subsequent program results). 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 { __u32 ids_len; __u32 prog_cnt; __u32 ids[0]; }; User space provides buffer "ids" for kernel to copy to. When returning from the kernel, the number of available programs in the array is set in "prog_cnt". The usage: struct perf_event_query_bpf *query = malloc(sizeof(*query) + sizeof(u32) * ids_len); query.ids_len = ids_len; err = ioctl(pmu_efd, PERF_EVENT_IOC_QUERY_BPF, query); if (err == 0) { /* query.prog_cnt is the number of available progs, * number of progs in ids: (ids_len == 0) ? 0 : query.prog_cnt */ } else if (errno == ENOSPC) { /* query.ids_len number of progs copied, * query.prog_cnt is the number of available progs */ } else { /* other errors */ }

Yonghong Song committed 8 years ago

Commit f371b301 parent 63060c35 files · +73 −0Browse files

Unified · Split

5 files
  1. include/linux/bpf.h +4 −0
  2. include/uapi/linux/perf_event.h +22 −0
  3. kernel/bpf/core.c +21 −0
  4. kernel/events/core.c +3 −0
  5. kernel/trace/bpf_trace.c +23 −0
include/linux/bpf.h+4−0
@@ -254,6 +254,7 @@ typedef unsigned long (*bpf_ctx_copy_t)(void *dst, const void *src, · 253 unmodified lines
254 254
255u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size,255u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size,
256 void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy);256 void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy);
257int bpf_event_query_prog_array(struct perf_event *event, void __user *info);
257 258
258int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,259int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
259 union bpf_attr __user *uattr);260 union bpf_attr __user *uattr);
@@ -285,6 +286,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, · 25 unmodified lines
285 286
286void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs,287void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs,
287 struct bpf_prog *old_prog);288 struct bpf_prog *old_prog);
289int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
290 __u32 __user *prog_ids, u32 request_cnt,
291 __u32 __user *prog_cnt);
288int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,292int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
289 struct bpf_prog *exclude_prog,293 struct bpf_prog *exclude_prog,
290 struct bpf_prog *include_prog,294 struct bpf_prog *include_prog,
include/uapi/linux/perf_event.h+22−0
@@ -418,6 +418,27 @@ struct perf_event_attr { · 417 unmodified lines
418 __u16 __reserved_2; /* align to __u64 */418 __u16 __reserved_2; /* align to __u64 */
419};419};
420 420
421/*
422 * Structure used by below PERF_EVENT_IOC_QUERY_BPF command
423 * to query bpf programs attached to the same perf tracepoint
424 * as the given perf event.
425 */
426struct perf_event_query_bpf {
427 /*
428 * The below ids array length
429 */
430 __u32 ids_len;
431 /*
432 * Set by the kernel to indicate the number of
433 * available programs
434 */
435 __u32 prog_cnt;
436 /*
437 * User provided buffer to store program ids
438 */
439 __u32 ids[0];
440};
441
421#define perf_flags(attr) (*(&(attr)->read_format + 1))442#define perf_flags(attr) (*(&(attr)->read_format + 1))
422 443
423/*444/*
@@ -433,6 +454,7 @@ struct perf_event_attr { · 9 unmodified lines
433#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)454#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
434#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)455#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
435#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)456#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)
457#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
436 458
437enum perf_event_ioc_flags {459enum perf_event_ioc_flags {
438 PERF_IOC_FLAG_GROUP = 1U << 0,460 PERF_IOC_FLAG_GROUP = 1U << 0,
kernel/bpf/core.c+21−0
@@ -1462,6 +1462,8 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, · 1461 unmodified lines
1462 rcu_read_lock();1462 rcu_read_lock();
1463 prog = rcu_dereference(progs)->progs;1463 prog = rcu_dereference(progs)->progs;
1464 for (; *prog; prog++) {1464 for (; *prog; prog++) {
1465 if (*prog == &dummy_bpf_prog.prog)
1466 continue;
1465 id = (*prog)->aux->id;1467 id = (*prog)->aux->id;
1466 if (copy_to_user(prog_ids + i, &id, sizeof(id))) {1468 if (copy_to_user(prog_ids + i, &id, sizeof(id))) {
1467 rcu_read_unlock();1469 rcu_read_unlock();
@@ -1545,6 +1547,25 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array, · 77 unmodified lines
1545 return 0;1547 return 0;
1546}1548}
1547 1549
1550int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
1551 __u32 __user *prog_ids, u32 request_cnt,
1552 __u32 __user *prog_cnt)
1553{
1554 u32 cnt = 0;
1555
1556 if (array)
1557 cnt = bpf_prog_array_length(array);
1558
1559 if (copy_to_user(prog_cnt, &cnt, sizeof(cnt)))
1560 return -EFAULT;
1561
1562 /* return early if user requested only program count or nothing to copy */
1563 if (!request_cnt || !cnt)
1564 return 0;
1565
1566 return bpf_prog_array_copy_to_user(array, prog_ids, request_cnt);
1567}
1568
1548static void bpf_prog_free_deferred(struct work_struct *work)1569static void bpf_prog_free_deferred(struct work_struct *work)
1549{1570{
1550 struct bpf_prog_aux *aux;1571 struct bpf_prog_aux *aux;
kernel/events/core.c+3−0
@@ -4723,6 +4723,9 @@ static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned lon · 4722 unmodified lines
4723 rcu_read_unlock();4723 rcu_read_unlock();
4724 return 0;4724 return 0;
4725 }4725 }
4726
4727 case PERF_EVENT_IOC_QUERY_BPF:
4728 return bpf_event_query_prog_array(event, (void __user *)arg);
4726 default:4729 default:
4727 return -ENOTTY;4730 return -ENOTTY;
4728 }4731 }
kernel/trace/bpf_trace.c+23−0
@@ -820,3 +820,26 @@ void perf_event_detach_bpf_prog(struct perf_event *event) · 819 unmodified lines
820unlock:820unlock:
821 mutex_unlock(&bpf_event_mutex);821 mutex_unlock(&bpf_event_mutex);
822}822}
823
824int bpf_event_query_prog_array(struct perf_event *event, void __user *info)
825{
826 struct perf_event_query_bpf __user *uquery = info;
827 struct perf_event_query_bpf query = {};
828 int ret;
829
830 if (!capable(CAP_SYS_ADMIN))
831 return -EPERM;
832 if (event->attr.type != PERF_TYPE_TRACEPOINT)
833 return -EINVAL;
834 if (copy_from_user(&query, uquery, sizeof(query)))
835 return -EFAULT;
836
837 mutex_lock(&bpf_event_mutex);
838 ret = bpf_prog_array_copy_info(event->tp_event->prog_array,
839 uquery->ids,
840 query.ids_len,
841 &uquery->prog_cnt);
842 mutex_unlock(&bpf_event_mutex);
843
844 return ret;
845}