diff options
-rw-r--r-- | include/linux/filter.h | 7 | ||||
-rw-r--r-- | include/linux/perf_event.h | 6 | ||||
-rw-r--r-- | include/uapi/linux/perf_event.h | 29 | ||||
-rw-r--r-- | kernel/bpf/core.c | 2 | ||||
-rw-r--r-- | kernel/bpf/syscall.c | 2 | ||||
-rw-r--r-- | kernel/events/core.c | 115 |
6 files changed, 159 insertions, 2 deletions
diff --git a/include/linux/filter.h b/include/linux/filter.h index ad106d845b22..d531d4250bff 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h | |||
@@ -951,6 +951,7 @@ bpf_address_lookup(unsigned long addr, unsigned long *size, | |||
951 | 951 | ||
952 | void bpf_prog_kallsyms_add(struct bpf_prog *fp); | 952 | void bpf_prog_kallsyms_add(struct bpf_prog *fp); |
953 | void bpf_prog_kallsyms_del(struct bpf_prog *fp); | 953 | void bpf_prog_kallsyms_del(struct bpf_prog *fp); |
954 | void bpf_get_prog_name(const struct bpf_prog *prog, char *sym); | ||
954 | 955 | ||
955 | #else /* CONFIG_BPF_JIT */ | 956 | #else /* CONFIG_BPF_JIT */ |
956 | 957 | ||
@@ -1006,6 +1007,12 @@ static inline void bpf_prog_kallsyms_add(struct bpf_prog *fp) | |||
1006 | static inline void bpf_prog_kallsyms_del(struct bpf_prog *fp) | 1007 | static inline void bpf_prog_kallsyms_del(struct bpf_prog *fp) |
1007 | { | 1008 | { |
1008 | } | 1009 | } |
1010 | |||
1011 | static inline void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) | ||
1012 | { | ||
1013 | sym[0] = '\0'; | ||
1014 | } | ||
1015 | |||
1009 | #endif /* CONFIG_BPF_JIT */ | 1016 | #endif /* CONFIG_BPF_JIT */ |
1010 | 1017 | ||
1011 | void bpf_prog_kallsyms_del_subprogs(struct bpf_prog *fp); | 1018 | void bpf_prog_kallsyms_del_subprogs(struct bpf_prog *fp); |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 136fe0495374..a79e59fc3b7d 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -1125,6 +1125,9 @@ extern void perf_event_mmap(struct vm_area_struct *vma); | |||
1125 | 1125 | ||
1126 | extern void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, | 1126 | extern void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, |
1127 | bool unregister, const char *sym); | 1127 | bool unregister, const char *sym); |
1128 | extern void perf_event_bpf_event(struct bpf_prog *prog, | ||
1129 | enum perf_bpf_event_type type, | ||
1130 | u16 flags); | ||
1128 | 1131 | ||
1129 | extern struct perf_guest_info_callbacks *perf_guest_cbs; | 1132 | extern struct perf_guest_info_callbacks *perf_guest_cbs; |
1130 | extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); | 1133 | extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); |
@@ -1350,6 +1353,9 @@ static inline void perf_event_mmap(struct vm_area_struct *vma) { } | |||
1350 | typedef int (perf_ksymbol_get_name_f)(char *name, int name_len, void *data); | 1353 | typedef int (perf_ksymbol_get_name_f)(char *name, int name_len, void *data); |
1351 | static inline void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, | 1354 | static inline void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, |
1352 | bool unregister, const char *sym) { } | 1355 | bool unregister, const char *sym) { } |
1356 | static inline void perf_event_bpf_event(struct bpf_prog *prog, | ||
1357 | enum perf_bpf_event_type type, | ||
1358 | u16 flags) { } | ||
1353 | static inline void perf_event_exec(void) { } | 1359 | static inline void perf_event_exec(void) { } |
1354 | static inline void perf_event_comm(struct task_struct *tsk, bool exec) { } | 1360 | static inline void perf_event_comm(struct task_struct *tsk, bool exec) { } |
1355 | static inline void perf_event_namespaces(struct task_struct *tsk) { } | 1361 | static inline void perf_event_namespaces(struct task_struct *tsk) { } |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 1dee5c8f166b..7198ddd0c6b1 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
@@ -373,7 +373,8 @@ struct perf_event_attr { | |||
373 | write_backward : 1, /* Write ring buffer from end to beginning */ | 373 | write_backward : 1, /* Write ring buffer from end to beginning */ |
374 | namespaces : 1, /* include namespaces data */ | 374 | namespaces : 1, /* include namespaces data */ |
375 | ksymbol : 1, /* include ksymbol events */ | 375 | ksymbol : 1, /* include ksymbol events */ |
376 | __reserved_1 : 34; | 376 | bpf_event : 1, /* include bpf events */ |
377 | __reserved_1 : 33; | ||
377 | 378 | ||
378 | union { | 379 | union { |
379 | __u32 wakeup_events; /* wakeup every n events */ | 380 | __u32 wakeup_events; /* wakeup every n events */ |
@@ -979,6 +980,25 @@ enum perf_event_type { | |||
979 | */ | 980 | */ |
980 | PERF_RECORD_KSYMBOL = 17, | 981 | PERF_RECORD_KSYMBOL = 17, |
981 | 982 | ||
983 | /* | ||
984 | * Record bpf events: | ||
985 | * enum perf_bpf_event_type { | ||
986 | * PERF_BPF_EVENT_UNKNOWN = 0, | ||
987 | * PERF_BPF_EVENT_PROG_LOAD = 1, | ||
988 | * PERF_BPF_EVENT_PROG_UNLOAD = 2, | ||
989 | * }; | ||
990 | * | ||
991 | * struct { | ||
992 | * struct perf_event_header header; | ||
993 | * u16 type; | ||
994 | * u16 flags; | ||
995 | * u32 id; | ||
996 | * u8 tag[BPF_TAG_SIZE]; | ||
997 | * struct sample_id sample_id; | ||
998 | * }; | ||
999 | */ | ||
1000 | PERF_RECORD_BPF_EVENT = 18, | ||
1001 | |||
982 | PERF_RECORD_MAX, /* non-ABI */ | 1002 | PERF_RECORD_MAX, /* non-ABI */ |
983 | }; | 1003 | }; |
984 | 1004 | ||
@@ -990,6 +1010,13 @@ enum perf_record_ksymbol_type { | |||
990 | 1010 | ||
991 | #define PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER (1 << 0) | 1011 | #define PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER (1 << 0) |
992 | 1012 | ||
1013 | enum perf_bpf_event_type { | ||
1014 | PERF_BPF_EVENT_UNKNOWN = 0, | ||
1015 | PERF_BPF_EVENT_PROG_LOAD = 1, | ||
1016 | PERF_BPF_EVENT_PROG_UNLOAD = 2, | ||
1017 | PERF_BPF_EVENT_MAX, /* non-ABI */ | ||
1018 | }; | ||
1019 | |||
993 | #define PERF_MAX_STACK_DEPTH 127 | 1020 | #define PERF_MAX_STACK_DEPTH 127 |
994 | #define PERF_MAX_CONTEXTS_PER_STACK 8 | 1021 | #define PERF_MAX_CONTEXTS_PER_STACK 8 |
995 | 1022 | ||
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index f908b9356025..19c49313c709 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c | |||
@@ -495,7 +495,7 @@ bpf_get_prog_addr_region(const struct bpf_prog *prog, | |||
495 | *symbol_end = addr + hdr->pages * PAGE_SIZE; | 495 | *symbol_end = addr + hdr->pages * PAGE_SIZE; |
496 | } | 496 | } |
497 | 497 | ||
498 | static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) | 498 | void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) |
499 | { | 499 | { |
500 | const char *end = sym + KSYM_NAME_LEN; | 500 | const char *end = sym + KSYM_NAME_LEN; |
501 | const struct btf_type *type; | 501 | const struct btf_type *type; |
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b155cd17c1bd..30ebd085790b 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
@@ -1211,6 +1211,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu) | |||
1211 | static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) | 1211 | static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) |
1212 | { | 1212 | { |
1213 | if (atomic_dec_and_test(&prog->aux->refcnt)) { | 1213 | if (atomic_dec_and_test(&prog->aux->refcnt)) { |
1214 | perf_event_bpf_event(prog, PERF_BPF_EVENT_PROG_UNLOAD, 0); | ||
1214 | /* bpf_prog_free_id() must be called first */ | 1215 | /* bpf_prog_free_id() must be called first */ |
1215 | bpf_prog_free_id(prog, do_idr_lock); | 1216 | bpf_prog_free_id(prog, do_idr_lock); |
1216 | bpf_prog_kallsyms_del_all(prog); | 1217 | bpf_prog_kallsyms_del_all(prog); |
@@ -1554,6 +1555,7 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) | |||
1554 | } | 1555 | } |
1555 | 1556 | ||
1556 | bpf_prog_kallsyms_add(prog); | 1557 | bpf_prog_kallsyms_add(prog); |
1558 | perf_event_bpf_event(prog, PERF_BPF_EVENT_PROG_LOAD, 0); | ||
1557 | return err; | 1559 | return err; |
1558 | 1560 | ||
1559 | free_used_maps: | 1561 | free_used_maps: |
diff --git a/kernel/events/core.c b/kernel/events/core.c index e04ab5f325cf..236bb8ddb7bc 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -386,6 +386,7 @@ static atomic_t nr_task_events __read_mostly; | |||
386 | static atomic_t nr_freq_events __read_mostly; | 386 | static atomic_t nr_freq_events __read_mostly; |
387 | static atomic_t nr_switch_events __read_mostly; | 387 | static atomic_t nr_switch_events __read_mostly; |
388 | static atomic_t nr_ksymbol_events __read_mostly; | 388 | static atomic_t nr_ksymbol_events __read_mostly; |
389 | static atomic_t nr_bpf_events __read_mostly; | ||
389 | 390 | ||
390 | static LIST_HEAD(pmus); | 391 | static LIST_HEAD(pmus); |
391 | static DEFINE_MUTEX(pmus_lock); | 392 | static DEFINE_MUTEX(pmus_lock); |
@@ -4308,6 +4309,8 @@ static void unaccount_event(struct perf_event *event) | |||
4308 | dec = true; | 4309 | dec = true; |
4309 | if (event->attr.ksymbol) | 4310 | if (event->attr.ksymbol) |
4310 | atomic_dec(&nr_ksymbol_events); | 4311 | atomic_dec(&nr_ksymbol_events); |
4312 | if (event->attr.bpf_event) | ||
4313 | atomic_dec(&nr_bpf_events); | ||
4311 | 4314 | ||
4312 | if (dec) { | 4315 | if (dec) { |
4313 | if (!atomic_add_unless(&perf_sched_count, -1, 1)) | 4316 | if (!atomic_add_unless(&perf_sched_count, -1, 1)) |
@@ -7747,6 +7750,116 @@ err: | |||
7747 | WARN_ONCE(1, "%s: Invalid KSYMBOL type 0x%x\n", __func__, ksym_type); | 7750 | WARN_ONCE(1, "%s: Invalid KSYMBOL type 0x%x\n", __func__, ksym_type); |
7748 | } | 7751 | } |
7749 | 7752 | ||
7753 | /* | ||
7754 | * bpf program load/unload tracking | ||
7755 | */ | ||
7756 | |||
7757 | struct perf_bpf_event { | ||
7758 | struct bpf_prog *prog; | ||
7759 | struct { | ||
7760 | struct perf_event_header header; | ||
7761 | u16 type; | ||
7762 | u16 flags; | ||
7763 | u32 id; | ||
7764 | u8 tag[BPF_TAG_SIZE]; | ||
7765 | } event_id; | ||
7766 | }; | ||
7767 | |||
7768 | static int perf_event_bpf_match(struct perf_event *event) | ||
7769 | { | ||
7770 | return event->attr.bpf_event; | ||
7771 | } | ||
7772 | |||
7773 | static void perf_event_bpf_output(struct perf_event *event, void *data) | ||
7774 | { | ||
7775 | struct perf_bpf_event *bpf_event = data; | ||
7776 | struct perf_output_handle handle; | ||
7777 | struct perf_sample_data sample; | ||
7778 | int ret; | ||
7779 | |||
7780 | if (!perf_event_bpf_match(event)) | ||
7781 | return; | ||
7782 | |||
7783 | perf_event_header__init_id(&bpf_event->event_id.header, | ||
7784 | &sample, event); | ||
7785 | ret = perf_output_begin(&handle, event, | ||
7786 | bpf_event->event_id.header.size); | ||
7787 | if (ret) | ||
7788 | return; | ||
7789 | |||
7790 | perf_output_put(&handle, bpf_event->event_id); | ||
7791 | perf_event__output_id_sample(event, &handle, &sample); | ||
7792 | |||
7793 | perf_output_end(&handle); | ||
7794 | } | ||
7795 | |||
7796 | static void perf_event_bpf_emit_ksymbols(struct bpf_prog *prog, | ||
7797 | enum perf_bpf_event_type type) | ||
7798 | { | ||
7799 | bool unregister = type == PERF_BPF_EVENT_PROG_UNLOAD; | ||
7800 | char sym[KSYM_NAME_LEN]; | ||
7801 | int i; | ||
7802 | |||
7803 | if (prog->aux->func_cnt == 0) { | ||
7804 | bpf_get_prog_name(prog, sym); | ||
7805 | perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, | ||
7806 | (u64)(unsigned long)prog->bpf_func, | ||
7807 | prog->jited_len, unregister, sym); | ||
7808 | } else { | ||
7809 | for (i = 0; i < prog->aux->func_cnt; i++) { | ||
7810 | struct bpf_prog *subprog = prog->aux->func[i]; | ||
7811 | |||
7812 | bpf_get_prog_name(subprog, sym); | ||
7813 | perf_event_ksymbol( | ||
7814 | PERF_RECORD_KSYMBOL_TYPE_BPF, | ||
7815 | (u64)(unsigned long)subprog->bpf_func, | ||
7816 | subprog->jited_len, unregister, sym); | ||
7817 | } | ||
7818 | } | ||
7819 | } | ||
7820 | |||
7821 | void perf_event_bpf_event(struct bpf_prog *prog, | ||
7822 | enum perf_bpf_event_type type, | ||
7823 | u16 flags) | ||
7824 | { | ||
7825 | struct perf_bpf_event bpf_event; | ||
7826 | |||
7827 | if (type <= PERF_BPF_EVENT_UNKNOWN || | ||
7828 | type >= PERF_BPF_EVENT_MAX) | ||
7829 | return; | ||
7830 | |||
7831 | switch (type) { | ||
7832 | case PERF_BPF_EVENT_PROG_LOAD: | ||
7833 | case PERF_BPF_EVENT_PROG_UNLOAD: | ||
7834 | if (atomic_read(&nr_ksymbol_events)) | ||
7835 | perf_event_bpf_emit_ksymbols(prog, type); | ||
7836 | break; | ||
7837 | default: | ||
7838 | break; | ||
7839 | } | ||
7840 | |||
7841 | if (!atomic_read(&nr_bpf_events)) | ||
7842 | return; | ||
7843 | |||
7844 | bpf_event = (struct perf_bpf_event){ | ||
7845 | .prog = prog, | ||
7846 | .event_id = { | ||
7847 | .header = { | ||
7848 | .type = PERF_RECORD_BPF_EVENT, | ||
7849 | .size = sizeof(bpf_event.event_id), | ||
7850 | }, | ||
7851 | .type = type, | ||
7852 | .flags = flags, | ||
7853 | .id = prog->aux->id, | ||
7854 | }, | ||
7855 | }; | ||
7856 | |||
7857 | BUILD_BUG_ON(BPF_TAG_SIZE % sizeof(u64)); | ||
7858 | |||
7859 | memcpy(bpf_event.event_id.tag, prog->tag, BPF_TAG_SIZE); | ||
7860 | perf_iterate_sb(perf_event_bpf_output, &bpf_event, NULL); | ||
7861 | } | ||
7862 | |||
7750 | void perf_event_itrace_started(struct perf_event *event) | 7863 | void perf_event_itrace_started(struct perf_event *event) |
7751 | { | 7864 | { |
7752 | event->attach_state |= PERF_ATTACH_ITRACE; | 7865 | event->attach_state |= PERF_ATTACH_ITRACE; |
@@ -10008,6 +10121,8 @@ static void account_event(struct perf_event *event) | |||
10008 | inc = true; | 10121 | inc = true; |
10009 | if (event->attr.ksymbol) | 10122 | if (event->attr.ksymbol) |
10010 | atomic_inc(&nr_ksymbol_events); | 10123 | atomic_inc(&nr_ksymbol_events); |
10124 | if (event->attr.bpf_event) | ||
10125 | atomic_inc(&nr_bpf_events); | ||
10011 | 10126 | ||
10012 | if (inc) { | 10127 | if (inc) { |
10013 | /* | 10128 | /* |