summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/filter.h7
-rw-r--r--include/linux/perf_event.h6
-rw-r--r--include/uapi/linux/perf_event.h29
-rw-r--r--kernel/bpf/core.c2
-rw-r--r--kernel/bpf/syscall.c2
-rw-r--r--kernel/events/core.c115
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
952void bpf_prog_kallsyms_add(struct bpf_prog *fp); 952void bpf_prog_kallsyms_add(struct bpf_prog *fp);
953void bpf_prog_kallsyms_del(struct bpf_prog *fp); 953void bpf_prog_kallsyms_del(struct bpf_prog *fp);
954void 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)
1006static inline void bpf_prog_kallsyms_del(struct bpf_prog *fp) 1007static inline void bpf_prog_kallsyms_del(struct bpf_prog *fp)
1007{ 1008{
1008} 1009}
1010
1011static 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
1011void bpf_prog_kallsyms_del_subprogs(struct bpf_prog *fp); 1018void 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
1126extern void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, 1126extern void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len,
1127 bool unregister, const char *sym); 1127 bool unregister, const char *sym);
1128extern void perf_event_bpf_event(struct bpf_prog *prog,
1129 enum perf_bpf_event_type type,
1130 u16 flags);
1128 1131
1129extern struct perf_guest_info_callbacks *perf_guest_cbs; 1132extern struct perf_guest_info_callbacks *perf_guest_cbs;
1130extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); 1133extern 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) { }
1350typedef int (perf_ksymbol_get_name_f)(char *name, int name_len, void *data); 1353typedef int (perf_ksymbol_get_name_f)(char *name, int name_len, void *data);
1351static inline void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, 1354static inline void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len,
1352 bool unregister, const char *sym) { } 1355 bool unregister, const char *sym) { }
1356static inline void perf_event_bpf_event(struct bpf_prog *prog,
1357 enum perf_bpf_event_type type,
1358 u16 flags) { }
1353static inline void perf_event_exec(void) { } 1359static inline void perf_event_exec(void) { }
1354static inline void perf_event_comm(struct task_struct *tsk, bool exec) { } 1360static inline void perf_event_comm(struct task_struct *tsk, bool exec) { }
1355static inline void perf_event_namespaces(struct task_struct *tsk) { } 1361static 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
1013enum 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
498static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) 498void 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)
1211static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) 1211static 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
1559free_used_maps: 1561free_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;
386static atomic_t nr_freq_events __read_mostly; 386static atomic_t nr_freq_events __read_mostly;
387static atomic_t nr_switch_events __read_mostly; 387static atomic_t nr_switch_events __read_mostly;
388static atomic_t nr_ksymbol_events __read_mostly; 388static atomic_t nr_ksymbol_events __read_mostly;
389static atomic_t nr_bpf_events __read_mostly;
389 390
390static LIST_HEAD(pmus); 391static LIST_HEAD(pmus);
391static DEFINE_MUTEX(pmus_lock); 392static 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
7757struct 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
7768static int perf_event_bpf_match(struct perf_event *event)
7769{
7770 return event->attr.bpf_event;
7771}
7772
7773static 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
7796static 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
7821void 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
7750void perf_event_itrace_started(struct perf_event *event) 7863void 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 /*