diff options
-rw-r--r-- | include/linux/bpf.h | 4 | ||||
-rw-r--r-- | include/uapi/linux/perf_event.h | 22 | ||||
-rw-r--r-- | kernel/bpf/core.c | 21 | ||||
-rw-r--r-- | kernel/events/core.c | 3 | ||||
-rw-r--r-- | kernel/trace/bpf_trace.c | 23 |
5 files changed, 73 insertions, 0 deletions
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e55e4255a210..f812ac508e9f 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, | |||
254 | 254 | ||
255 | u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, | 255 | u64 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); |
257 | int bpf_event_query_prog_array(struct perf_event *event, void __user *info); | ||
257 | 258 | ||
258 | int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, | 259 | int 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, | |||
285 | 286 | ||
286 | void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs, | 287 | void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs, |
287 | struct bpf_prog *old_prog); | 288 | struct bpf_prog *old_prog); |
289 | int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array, | ||
290 | __u32 __user *prog_ids, u32 request_cnt, | ||
291 | __u32 __user *prog_cnt); | ||
288 | int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array, | 292 | int 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, |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index b9a4953018ed..769533696483 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
@@ -418,6 +418,27 @@ struct perf_event_attr { | |||
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 | */ | ||
426 | struct 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 { | |||
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 | ||
437 | enum perf_event_ioc_flags { | 459 | enum perf_event_ioc_flags { |
438 | PERF_IOC_FLAG_GROUP = 1U << 0, | 460 | PERF_IOC_FLAG_GROUP = 1U << 0, |
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 86b50aa26ee8..b16c6f8f42b6 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, | |||
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, | |||
1545 | return 0; | 1547 | return 0; |
1546 | } | 1548 | } |
1547 | 1549 | ||
1550 | int 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 | |||
1548 | static void bpf_prog_free_deferred(struct work_struct *work) | 1569 | static void bpf_prog_free_deferred(struct work_struct *work) |
1549 | { | 1570 | { |
1550 | struct bpf_prog_aux *aux; | 1571 | struct bpf_prog_aux *aux; |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 16beab4767e1..f10609e539d4 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 | |||
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 | } |
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 0ce99c379c30..b143f2a05aff 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) | |||
820 | unlock: | 820 | unlock: |
821 | mutex_unlock(&bpf_event_mutex); | 821 | mutex_unlock(&bpf_event_mutex); |
822 | } | 822 | } |
823 | |||
824 | int 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 | } | ||