Skip to content

Commit 1b715e1

Browse files
laoarAlexei Starovoitov
authored andcommitted
bpf: Support ->fill_link_info for perf_event
By introducing support for ->fill_link_info to the perf_event link, users gain the ability to inspect it using `bpftool link show`. While the current approach involves accessing this information via `bpftool perf show`, consolidating link information for all link types in one place offers greater convenience. Additionally, this patch extends support to the generic perf event, which is not currently accommodated by `bpftool perf show`. While only the perf type and config are exposed to userspace, other attributes such as sample_period and sample_freq are ignored. It's important to note that if kptr_restrict is not permitted, the probed address will not be exposed, maintaining security measures. A new enum bpf_perf_event_type is introduced to help the user understand which struct is relevant. Signed-off-by: Yafang Shao <[email protected]> Acked-by: Jiri Olsa <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 57d4853 commit 1b715e1

File tree

4 files changed

+223
-3
lines changed

4 files changed

+223
-3
lines changed

include/uapi/linux/bpf.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,16 @@ enum bpf_link_type {
10571057
MAX_BPF_LINK_TYPE,
10581058
};
10591059

1060+
enum bpf_perf_event_type {
1061+
BPF_PERF_EVENT_UNSPEC = 0,
1062+
BPF_PERF_EVENT_UPROBE = 1,
1063+
BPF_PERF_EVENT_URETPROBE = 2,
1064+
BPF_PERF_EVENT_KPROBE = 3,
1065+
BPF_PERF_EVENT_KRETPROBE = 4,
1066+
BPF_PERF_EVENT_TRACEPOINT = 5,
1067+
BPF_PERF_EVENT_EVENT = 6,
1068+
};
1069+
10601070
/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
10611071
*
10621072
* NONE(default): No further bpf programs allowed in the subtree.
@@ -6444,6 +6454,31 @@ struct bpf_link_info {
64446454
__u32 count; /* in/out: kprobe_multi function count */
64456455
__u32 flags;
64466456
} kprobe_multi;
6457+
struct {
6458+
__u32 type; /* enum bpf_perf_event_type */
6459+
__u32 :32;
6460+
union {
6461+
struct {
6462+
__aligned_u64 file_name; /* in/out */
6463+
__u32 name_len;
6464+
__u32 offset; /* offset from file_name */
6465+
} uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */
6466+
struct {
6467+
__aligned_u64 func_name; /* in/out */
6468+
__u32 name_len;
6469+
__u32 offset; /* offset from func_name */
6470+
__u64 addr;
6471+
} kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */
6472+
struct {
6473+
__aligned_u64 tp_name; /* in/out */
6474+
__u32 name_len;
6475+
} tracepoint; /* BPF_PERF_EVENT_TRACEPOINT */
6476+
struct {
6477+
__u64 config;
6478+
__u32 type;
6479+
} event; /* BPF_PERF_EVENT_EVENT */
6480+
};
6481+
} perf_event;
64476482
};
64486483
} __attribute__((aligned(8)));
64496484

kernel/bpf/syscall.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3364,9 +3364,155 @@ static void bpf_perf_link_dealloc(struct bpf_link *link)
33643364
kfree(perf_link);
33653365
}
33663366

3367+
static int bpf_perf_link_fill_common(const struct perf_event *event,
3368+
char __user *uname, u32 ulen,
3369+
u64 *probe_offset, u64 *probe_addr,
3370+
u32 *fd_type)
3371+
{
3372+
const char *buf;
3373+
u32 prog_id;
3374+
size_t len;
3375+
int err;
3376+
3377+
if (!ulen ^ !uname)
3378+
return -EINVAL;
3379+
if (!uname)
3380+
return 0;
3381+
3382+
err = bpf_get_perf_event_info(event, &prog_id, fd_type, &buf,
3383+
probe_offset, probe_addr);
3384+
if (err)
3385+
return err;
3386+
3387+
if (buf) {
3388+
len = strlen(buf);
3389+
err = bpf_copy_to_user(uname, buf, ulen, len);
3390+
if (err)
3391+
return err;
3392+
} else {
3393+
char zero = '\0';
3394+
3395+
if (put_user(zero, uname))
3396+
return -EFAULT;
3397+
}
3398+
return 0;
3399+
}
3400+
3401+
#ifdef CONFIG_KPROBE_EVENTS
3402+
static int bpf_perf_link_fill_kprobe(const struct perf_event *event,
3403+
struct bpf_link_info *info)
3404+
{
3405+
char __user *uname;
3406+
u64 addr, offset;
3407+
u32 ulen, type;
3408+
int err;
3409+
3410+
uname = u64_to_user_ptr(info->perf_event.kprobe.func_name);
3411+
ulen = info->perf_event.kprobe.name_len;
3412+
err = bpf_perf_link_fill_common(event, uname, ulen, &offset, &addr,
3413+
&type);
3414+
if (err)
3415+
return err;
3416+
if (type == BPF_FD_TYPE_KRETPROBE)
3417+
info->perf_event.type = BPF_PERF_EVENT_KRETPROBE;
3418+
else
3419+
info->perf_event.type = BPF_PERF_EVENT_KPROBE;
3420+
3421+
info->perf_event.kprobe.offset = offset;
3422+
if (!kallsyms_show_value(current_cred()))
3423+
addr = 0;
3424+
info->perf_event.kprobe.addr = addr;
3425+
return 0;
3426+
}
3427+
#endif
3428+
3429+
#ifdef CONFIG_UPROBE_EVENTS
3430+
static int bpf_perf_link_fill_uprobe(const struct perf_event *event,
3431+
struct bpf_link_info *info)
3432+
{
3433+
char __user *uname;
3434+
u64 addr, offset;
3435+
u32 ulen, type;
3436+
int err;
3437+
3438+
uname = u64_to_user_ptr(info->perf_event.uprobe.file_name);
3439+
ulen = info->perf_event.uprobe.name_len;
3440+
err = bpf_perf_link_fill_common(event, uname, ulen, &offset, &addr,
3441+
&type);
3442+
if (err)
3443+
return err;
3444+
3445+
if (type == BPF_FD_TYPE_URETPROBE)
3446+
info->perf_event.type = BPF_PERF_EVENT_URETPROBE;
3447+
else
3448+
info->perf_event.type = BPF_PERF_EVENT_UPROBE;
3449+
info->perf_event.uprobe.offset = offset;
3450+
return 0;
3451+
}
3452+
#endif
3453+
3454+
static int bpf_perf_link_fill_probe(const struct perf_event *event,
3455+
struct bpf_link_info *info)
3456+
{
3457+
#ifdef CONFIG_KPROBE_EVENTS
3458+
if (event->tp_event->flags & TRACE_EVENT_FL_KPROBE)
3459+
return bpf_perf_link_fill_kprobe(event, info);
3460+
#endif
3461+
#ifdef CONFIG_UPROBE_EVENTS
3462+
if (event->tp_event->flags & TRACE_EVENT_FL_UPROBE)
3463+
return bpf_perf_link_fill_uprobe(event, info);
3464+
#endif
3465+
return -EOPNOTSUPP;
3466+
}
3467+
3468+
static int bpf_perf_link_fill_tracepoint(const struct perf_event *event,
3469+
struct bpf_link_info *info)
3470+
{
3471+
char __user *uname;
3472+
u32 ulen;
3473+
3474+
uname = u64_to_user_ptr(info->perf_event.tracepoint.tp_name);
3475+
ulen = info->perf_event.tracepoint.name_len;
3476+
info->perf_event.type = BPF_PERF_EVENT_TRACEPOINT;
3477+
return bpf_perf_link_fill_common(event, uname, ulen, NULL, NULL, NULL);
3478+
}
3479+
3480+
static int bpf_perf_link_fill_perf_event(const struct perf_event *event,
3481+
struct bpf_link_info *info)
3482+
{
3483+
info->perf_event.event.type = event->attr.type;
3484+
info->perf_event.event.config = event->attr.config;
3485+
info->perf_event.type = BPF_PERF_EVENT_EVENT;
3486+
return 0;
3487+
}
3488+
3489+
static int bpf_perf_link_fill_link_info(const struct bpf_link *link,
3490+
struct bpf_link_info *info)
3491+
{
3492+
struct bpf_perf_link *perf_link;
3493+
const struct perf_event *event;
3494+
3495+
perf_link = container_of(link, struct bpf_perf_link, link);
3496+
event = perf_get_event(perf_link->perf_file);
3497+
if (IS_ERR(event))
3498+
return PTR_ERR(event);
3499+
3500+
switch (event->prog->type) {
3501+
case BPF_PROG_TYPE_PERF_EVENT:
3502+
return bpf_perf_link_fill_perf_event(event, info);
3503+
case BPF_PROG_TYPE_TRACEPOINT:
3504+
return bpf_perf_link_fill_tracepoint(event, info);
3505+
case BPF_PROG_TYPE_KPROBE:
3506+
return bpf_perf_link_fill_probe(event, info);
3507+
default:
3508+
return -EOPNOTSUPP;
3509+
}
3510+
}
3511+
33673512
static const struct bpf_link_ops bpf_perf_link_lops = {
33683513
.release = bpf_perf_link_release,
33693514
.dealloc = bpf_perf_link_dealloc,
3515+
.fill_link_info = bpf_perf_link_fill_link_info,
33703516
};
33713517

33723518
static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)

kernel/trace/bpf_trace.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,9 +2369,13 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
23692369
if (is_tracepoint || is_syscall_tp) {
23702370
*buf = is_tracepoint ? event->tp_event->tp->name
23712371
: event->tp_event->name;
2372-
*fd_type = BPF_FD_TYPE_TRACEPOINT;
2373-
*probe_offset = 0x0;
2374-
*probe_addr = 0x0;
2372+
/* We allow NULL pointer for tracepoint */
2373+
if (fd_type)
2374+
*fd_type = BPF_FD_TYPE_TRACEPOINT;
2375+
if (probe_offset)
2376+
*probe_offset = 0x0;
2377+
if (probe_addr)
2378+
*probe_addr = 0x0;
23752379
} else {
23762380
/* kprobe/uprobe */
23772381
err = -EOPNOTSUPP;

tools/include/uapi/linux/bpf.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,16 @@ enum bpf_link_type {
10571057
MAX_BPF_LINK_TYPE,
10581058
};
10591059

1060+
enum bpf_perf_event_type {
1061+
BPF_PERF_EVENT_UNSPEC = 0,
1062+
BPF_PERF_EVENT_UPROBE = 1,
1063+
BPF_PERF_EVENT_URETPROBE = 2,
1064+
BPF_PERF_EVENT_KPROBE = 3,
1065+
BPF_PERF_EVENT_KRETPROBE = 4,
1066+
BPF_PERF_EVENT_TRACEPOINT = 5,
1067+
BPF_PERF_EVENT_EVENT = 6,
1068+
};
1069+
10601070
/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
10611071
*
10621072
* NONE(default): No further bpf programs allowed in the subtree.
@@ -6444,6 +6454,31 @@ struct bpf_link_info {
64446454
__u32 count; /* in/out: kprobe_multi function count */
64456455
__u32 flags;
64466456
} kprobe_multi;
6457+
struct {
6458+
__u32 type; /* enum bpf_perf_event_type */
6459+
__u32 :32;
6460+
union {
6461+
struct {
6462+
__aligned_u64 file_name; /* in/out */
6463+
__u32 name_len;
6464+
__u32 offset; /* offset from file_name */
6465+
} uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */
6466+
struct {
6467+
__aligned_u64 func_name; /* in/out */
6468+
__u32 name_len;
6469+
__u32 offset; /* offset from func_name */
6470+
__u64 addr;
6471+
} kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */
6472+
struct {
6473+
__aligned_u64 tp_name; /* in/out */
6474+
__u32 name_len;
6475+
} tracepoint; /* BPF_PERF_EVENT_TRACEPOINT */
6476+
struct {
6477+
__u64 config;
6478+
__u32 type;
6479+
} event; /* BPF_PERF_EVENT_EVENT */
6480+
};
6481+
} perf_event;
64476482
};
64486483
} __attribute__((aligned(8)));
64496484

0 commit comments

Comments
 (0)