Skip to content

Commit b89fbfb

Browse files
anakryikoborkmann
authored andcommitted
bpf: Implement minimal BPF perf link
Introduce a new type of BPF link - BPF perf link. This brings perf_event-based BPF program attachments (perf_event, tracepoints, kprobes, and uprobes) into the common BPF link infrastructure, allowing to list all active perf_event based attachments, auto-detaching BPF program from perf_event when link's FD is closed, get generic BPF link fdinfo/get_info functionality. BPF_LINK_CREATE command expects perf_event's FD as target_fd. No extra flags are currently supported. Force-detaching and atomic BPF program updates are not yet implemented, but with perf_event-based BPF links we now have common framework for this without the need to extend ioctl()-based perf_event interface. One interesting consideration is a new value for bpf_attach_type, which BPF_LINK_CREATE command expects. Generally, it's either 1-to-1 mapping from bpf_attach_type to bpf_prog_type, or many-to-1 mapping from a subset of bpf_attach_types to one bpf_prog_type (e.g., see BPF_PROG_TYPE_SK_SKB or BPF_PROG_TYPE_CGROUP_SOCK). In this case, though, we have three different program types (KPROBE, TRACEPOINT, PERF_EVENT) using the same perf_event-based mechanism, so it's many bpf_prog_types to one bpf_attach_type. I chose to define a single BPF_PERF_EVENT attach type for all of them and adjust link_create()'s logic for checking correspondence between attach type and program type. The alternative would be to define three new attach types (e.g., BPF_KPROBE, BPF_TRACEPOINT, and BPF_PERF_EVENT), but that seemed like unnecessary overkill and BPF_KPROBE will cause naming conflicts with BPF_KPROBE() macro, defined by libbpf. I chose to not do this to avoid unnecessary proliferation of bpf_attach_type enum values and not have to deal with naming conflicts. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Yonghong Song <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 652c1b1 commit b89fbfb

File tree

6 files changed

+112
-13
lines changed

6 files changed

+112
-13
lines changed

include/linux/bpf_types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,6 @@ BPF_LINK_TYPE(BPF_LINK_TYPE_ITER, iter)
136136
BPF_LINK_TYPE(BPF_LINK_TYPE_NETNS, netns)
137137
BPF_LINK_TYPE(BPF_LINK_TYPE_XDP, xdp)
138138
#endif
139+
#ifdef CONFIG_PERF_EVENTS
140+
BPF_LINK_TYPE(BPF_LINK_TYPE_PERF_EVENT, perf)
141+
#endif

include/linux/trace_events.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,9 @@ extern void ftrace_profile_free_filter(struct perf_event *event);
803803
void perf_trace_buf_update(void *record, u16 type);
804804
void *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp);
805805

806+
int perf_event_set_bpf_prog(struct perf_event *event, struct bpf_prog *prog);
807+
void perf_event_free_bpf_prog(struct perf_event *event);
808+
806809
void bpf_trace_run1(struct bpf_prog *prog, u64 arg1);
807810
void bpf_trace_run2(struct bpf_prog *prog, u64 arg1, u64 arg2);
808811
void bpf_trace_run3(struct bpf_prog *prog, u64 arg1, u64 arg2,

include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@ enum bpf_attach_type {
993993
BPF_SK_SKB_VERDICT,
994994
BPF_SK_REUSEPORT_SELECT,
995995
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
996+
BPF_PERF_EVENT,
996997
__MAX_BPF_ATTACH_TYPE
997998
};
998999

@@ -1006,6 +1007,7 @@ enum bpf_link_type {
10061007
BPF_LINK_TYPE_ITER = 4,
10071008
BPF_LINK_TYPE_NETNS = 5,
10081009
BPF_LINK_TYPE_XDP = 6,
1010+
BPF_LINK_TYPE_PERF_EVENT = 7,
10091011

10101012
MAX_BPF_LINK_TYPE,
10111013
};

kernel/bpf/syscall.c

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,79 @@ static const struct bpf_link_ops bpf_raw_tp_link_lops = {
29062906
.fill_link_info = bpf_raw_tp_link_fill_link_info,
29072907
};
29082908

2909+
#ifdef CONFIG_PERF_EVENTS
2910+
struct bpf_perf_link {
2911+
struct bpf_link link;
2912+
struct file *perf_file;
2913+
};
2914+
2915+
static void bpf_perf_link_release(struct bpf_link *link)
2916+
{
2917+
struct bpf_perf_link *perf_link = container_of(link, struct bpf_perf_link, link);
2918+
struct perf_event *event = perf_link->perf_file->private_data;
2919+
2920+
perf_event_free_bpf_prog(event);
2921+
fput(perf_link->perf_file);
2922+
}
2923+
2924+
static void bpf_perf_link_dealloc(struct bpf_link *link)
2925+
{
2926+
struct bpf_perf_link *perf_link = container_of(link, struct bpf_perf_link, link);
2927+
2928+
kfree(perf_link);
2929+
}
2930+
2931+
static const struct bpf_link_ops bpf_perf_link_lops = {
2932+
.release = bpf_perf_link_release,
2933+
.dealloc = bpf_perf_link_dealloc,
2934+
};
2935+
2936+
static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
2937+
{
2938+
struct bpf_link_primer link_primer;
2939+
struct bpf_perf_link *link;
2940+
struct perf_event *event;
2941+
struct file *perf_file;
2942+
int err;
2943+
2944+
if (attr->link_create.flags)
2945+
return -EINVAL;
2946+
2947+
perf_file = perf_event_get(attr->link_create.target_fd);
2948+
if (IS_ERR(perf_file))
2949+
return PTR_ERR(perf_file);
2950+
2951+
link = kzalloc(sizeof(*link), GFP_USER);
2952+
if (!link) {
2953+
err = -ENOMEM;
2954+
goto out_put_file;
2955+
}
2956+
bpf_link_init(&link->link, BPF_LINK_TYPE_PERF_EVENT, &bpf_perf_link_lops, prog);
2957+
link->perf_file = perf_file;
2958+
2959+
err = bpf_link_prime(&link->link, &link_primer);
2960+
if (err) {
2961+
kfree(link);
2962+
goto out_put_file;
2963+
}
2964+
2965+
event = perf_file->private_data;
2966+
err = perf_event_set_bpf_prog(event, prog);
2967+
if (err) {
2968+
bpf_link_cleanup(&link_primer);
2969+
goto out_put_file;
2970+
}
2971+
/* perf_event_set_bpf_prog() doesn't take its own refcnt on prog */
2972+
bpf_prog_inc(prog);
2973+
2974+
return bpf_link_settle(&link_primer);
2975+
2976+
out_put_file:
2977+
fput(perf_file);
2978+
return err;
2979+
}
2980+
#endif /* CONFIG_PERF_EVENTS */
2981+
29092982
#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
29102983

29112984
static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
@@ -4147,15 +4220,26 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
41474220
if (ret)
41484221
goto out;
41494222

4150-
if (prog->type == BPF_PROG_TYPE_EXT) {
4223+
switch (prog->type) {
4224+
case BPF_PROG_TYPE_EXT:
41514225
ret = tracing_bpf_link_attach(attr, uattr, prog);
41524226
goto out;
4153-
}
4154-
4155-
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
4156-
if (ptype == BPF_PROG_TYPE_UNSPEC || ptype != prog->type) {
4157-
ret = -EINVAL;
4158-
goto out;
4227+
case BPF_PROG_TYPE_PERF_EVENT:
4228+
case BPF_PROG_TYPE_KPROBE:
4229+
case BPF_PROG_TYPE_TRACEPOINT:
4230+
if (attr->link_create.attach_type != BPF_PERF_EVENT) {
4231+
ret = -EINVAL;
4232+
goto out;
4233+
}
4234+
ptype = prog->type;
4235+
break;
4236+
default:
4237+
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
4238+
if (ptype == BPF_PROG_TYPE_UNSPEC || ptype != prog->type) {
4239+
ret = -EINVAL;
4240+
goto out;
4241+
}
4242+
break;
41594243
}
41604244

41614245
switch (ptype) {
@@ -4179,6 +4263,13 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
41794263
case BPF_PROG_TYPE_XDP:
41804264
ret = bpf_xdp_link_attach(attr, prog);
41814265
break;
4266+
#endif
4267+
#ifdef CONFIG_PERF_EVENTS
4268+
case BPF_PROG_TYPE_PERF_EVENT:
4269+
case BPF_PROG_TYPE_TRACEPOINT:
4270+
case BPF_PROG_TYPE_KPROBE:
4271+
ret = bpf_perf_link_attach(attr, prog);
4272+
break;
41824273
#endif
41834274
default:
41844275
ret = -EINVAL;

kernel/events/core.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4697,7 +4697,6 @@ find_get_context(struct pmu *pmu, struct task_struct *task,
46974697
}
46984698

46994699
static void perf_event_free_filter(struct perf_event *event);
4700-
static void perf_event_free_bpf_prog(struct perf_event *event);
47014700

47024701
static void free_event_rcu(struct rcu_head *head)
47034702
{
@@ -5574,7 +5573,6 @@ static inline int perf_fget_light(int fd, struct fd *p)
55745573
static int perf_event_set_output(struct perf_event *event,
55755574
struct perf_event *output_event);
55765575
static int perf_event_set_filter(struct perf_event *event, void __user *arg);
5577-
static int perf_event_set_bpf_prog(struct perf_event *event, struct bpf_prog *prog);
55785576
static int perf_copy_attr(struct perf_event_attr __user *uattr,
55795577
struct perf_event_attr *attr);
55805578

@@ -10013,7 +10011,7 @@ static inline bool perf_event_is_tracing(struct perf_event *event)
1001310011
return false;
1001410012
}
1001510013

10016-
static int perf_event_set_bpf_prog(struct perf_event *event, struct bpf_prog *prog)
10014+
int perf_event_set_bpf_prog(struct perf_event *event, struct bpf_prog *prog)
1001710015
{
1001810016
bool is_kprobe, is_tracepoint, is_syscall_tp;
1001910017

@@ -10047,7 +10045,7 @@ static int perf_event_set_bpf_prog(struct perf_event *event, struct bpf_prog *pr
1004710045
return perf_event_attach_bpf_prog(event, prog);
1004810046
}
1004910047

10050-
static void perf_event_free_bpf_prog(struct perf_event *event)
10048+
void perf_event_free_bpf_prog(struct perf_event *event)
1005110049
{
1005210050
if (!perf_event_is_tracing(event)) {
1005310051
perf_event_free_bpf_handler(event);
@@ -10066,12 +10064,12 @@ static void perf_event_free_filter(struct perf_event *event)
1006610064
{
1006710065
}
1006810066

10069-
static int perf_event_set_bpf_prog(struct perf_event *event, struct bpf_prog *prog)
10067+
int perf_event_set_bpf_prog(struct perf_event *event, struct bpf_prog *prog)
1007010068
{
1007110069
return -ENOENT;
1007210070
}
1007310071

10074-
static void perf_event_free_bpf_prog(struct perf_event *event)
10072+
void perf_event_free_bpf_prog(struct perf_event *event)
1007510073
{
1007610074
}
1007710075
#endif /* CONFIG_EVENT_TRACING */

tools/include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@ enum bpf_attach_type {
993993
BPF_SK_SKB_VERDICT,
994994
BPF_SK_REUSEPORT_SELECT,
995995
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
996+
BPF_PERF_EVENT,
996997
__MAX_BPF_ATTACH_TYPE
997998
};
998999

@@ -1006,6 +1007,7 @@ enum bpf_link_type {
10061007
BPF_LINK_TYPE_ITER = 4,
10071008
BPF_LINK_TYPE_NETNS = 5,
10081009
BPF_LINK_TYPE_XDP = 6,
1010+
BPF_LINK_TYPE_PERF_EVENT = 7,
10091011

10101012
MAX_BPF_LINK_TYPE,
10111013
};

0 commit comments

Comments
 (0)