libbpf: Use BPF perf link when supported by kernel
Detect kernel support for BPF perf link and prefer it when attaching to perf_event, tracepoint, kprobe/uprobe. Underlying perf_event FD will be kept open until BPF link is destroyed, at which point both perf_event FD and BPF link FD will be closed. This preserves current behavior in which perf_event FD is open for the duration of bpf_link's lifetime and user is able to "disconnect" bpf_link from underlying FD (with bpf_link__disconnect()), so that bpf_link__destroy() doesn't close underlying perf_event FD.When BPF perf link is used, disconnect will keep both perf_event and bpf_link FDs open, so it will be up to (advanced) user to close them. This approach is demonstrated in bpf_cookie.c selftests, added in this patch set. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/20210815070609.987780-10-andrii@kernel.org
This commit is contained in:
parent
d88b71d4a9
commit
668ace0ea5
|
@ -193,6 +193,8 @@ enum kern_feature_id {
|
||||||
FEAT_MODULE_BTF,
|
FEAT_MODULE_BTF,
|
||||||
/* BTF_KIND_FLOAT support */
|
/* BTF_KIND_FLOAT support */
|
||||||
FEAT_BTF_FLOAT,
|
FEAT_BTF_FLOAT,
|
||||||
|
/* BPF perf link support */
|
||||||
|
FEAT_PERF_LINK,
|
||||||
__FEAT_CNT,
|
__FEAT_CNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4337,6 +4339,37 @@ static int probe_module_btf(void)
|
||||||
return !err;
|
return !err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int probe_perf_link(void)
|
||||||
|
{
|
||||||
|
struct bpf_load_program_attr attr;
|
||||||
|
struct bpf_insn insns[] = {
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
};
|
||||||
|
int prog_fd, link_fd, err;
|
||||||
|
|
||||||
|
memset(&attr, 0, sizeof(attr));
|
||||||
|
attr.prog_type = BPF_PROG_TYPE_TRACEPOINT;
|
||||||
|
attr.insns = insns;
|
||||||
|
attr.insns_cnt = ARRAY_SIZE(insns);
|
||||||
|
attr.license = "GPL";
|
||||||
|
prog_fd = bpf_load_program_xattr(&attr, NULL, 0);
|
||||||
|
if (prog_fd < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
/* use invalid perf_event FD to get EBADF, if link is supported;
|
||||||
|
* otherwise EINVAL should be returned
|
||||||
|
*/
|
||||||
|
link_fd = bpf_link_create(prog_fd, -1, BPF_PERF_EVENT, NULL);
|
||||||
|
err = -errno; /* close() can clobber errno */
|
||||||
|
|
||||||
|
if (link_fd >= 0)
|
||||||
|
close(link_fd);
|
||||||
|
close(prog_fd);
|
||||||
|
|
||||||
|
return link_fd < 0 && err == -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
enum kern_feature_result {
|
enum kern_feature_result {
|
||||||
FEAT_UNKNOWN = 0,
|
FEAT_UNKNOWN = 0,
|
||||||
FEAT_SUPPORTED = 1,
|
FEAT_SUPPORTED = 1,
|
||||||
|
@ -4387,6 +4420,9 @@ static struct kern_feature_desc {
|
||||||
[FEAT_BTF_FLOAT] = {
|
[FEAT_BTF_FLOAT] = {
|
||||||
"BTF_KIND_FLOAT support", probe_kern_btf_float,
|
"BTF_KIND_FLOAT support", probe_kern_btf_float,
|
||||||
},
|
},
|
||||||
|
[FEAT_PERF_LINK] = {
|
||||||
|
"BPF perf link support", probe_perf_link,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
|
static bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
|
||||||
|
@ -8951,23 +8987,38 @@ int bpf_link__unpin(struct bpf_link *link)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpf_link__detach_perf_event(struct bpf_link *link)
|
struct bpf_link_perf {
|
||||||
{
|
struct bpf_link link;
|
||||||
int err;
|
int perf_event_fd;
|
||||||
|
};
|
||||||
|
|
||||||
err = ioctl(link->fd, PERF_EVENT_IOC_DISABLE, 0);
|
static int bpf_link_perf_detach(struct bpf_link *link)
|
||||||
if (err)
|
{
|
||||||
|
struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (ioctl(perf_link->perf_event_fd, PERF_EVENT_IOC_DISABLE, 0) < 0)
|
||||||
err = -errno;
|
err = -errno;
|
||||||
|
|
||||||
|
if (perf_link->perf_event_fd != link->fd)
|
||||||
|
close(perf_link->perf_event_fd);
|
||||||
close(link->fd);
|
close(link->fd);
|
||||||
|
|
||||||
return libbpf_err(err);
|
return libbpf_err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bpf_link_perf_dealloc(struct bpf_link *link)
|
||||||
|
{
|
||||||
|
struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
|
||||||
|
|
||||||
|
free(perf_link);
|
||||||
|
}
|
||||||
|
|
||||||
struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, int pfd)
|
struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, int pfd)
|
||||||
{
|
{
|
||||||
char errmsg[STRERR_BUFSIZE];
|
char errmsg[STRERR_BUFSIZE];
|
||||||
struct bpf_link *link;
|
struct bpf_link_perf *link;
|
||||||
int prog_fd, err;
|
int prog_fd, link_fd = -1, err;
|
||||||
|
|
||||||
if (pfd < 0) {
|
if (pfd < 0) {
|
||||||
pr_warn("prog '%s': invalid perf event FD %d\n",
|
pr_warn("prog '%s': invalid perf event FD %d\n",
|
||||||
|
@ -8984,27 +9035,45 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, int pf
|
||||||
link = calloc(1, sizeof(*link));
|
link = calloc(1, sizeof(*link));
|
||||||
if (!link)
|
if (!link)
|
||||||
return libbpf_err_ptr(-ENOMEM);
|
return libbpf_err_ptr(-ENOMEM);
|
||||||
link->detach = &bpf_link__detach_perf_event;
|
link->link.detach = &bpf_link_perf_detach;
|
||||||
link->fd = pfd;
|
link->link.dealloc = &bpf_link_perf_dealloc;
|
||||||
|
link->perf_event_fd = pfd;
|
||||||
|
|
||||||
if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
|
if (kernel_supports(prog->obj, FEAT_PERF_LINK)) {
|
||||||
err = -errno;
|
link_fd = bpf_link_create(prog_fd, pfd, BPF_PERF_EVENT, NULL);
|
||||||
free(link);
|
if (link_fd < 0) {
|
||||||
pr_warn("prog '%s': failed to attach to pfd %d: %s\n",
|
err = -errno;
|
||||||
prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
pr_warn("prog '%s': failed to create BPF link for perf_event FD %d: %d (%s)\n",
|
||||||
if (err == -EPROTO)
|
prog->name, pfd,
|
||||||
pr_warn("prog '%s': try add PERF_SAMPLE_CALLCHAIN to or remove exclude_callchain_[kernel|user] from pfd %d\n",
|
err, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
prog->name, pfd);
|
goto err_out;
|
||||||
return libbpf_err_ptr(err);
|
}
|
||||||
|
link->link.fd = link_fd;
|
||||||
|
} else {
|
||||||
|
if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warn("prog '%s': failed to attach to perf_event FD %d: %s\n",
|
||||||
|
prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
if (err == -EPROTO)
|
||||||
|
pr_warn("prog '%s': try add PERF_SAMPLE_CALLCHAIN to or remove exclude_callchain_[kernel|user] from pfd %d\n",
|
||||||
|
prog->name, pfd);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
link->link.fd = pfd;
|
||||||
}
|
}
|
||||||
if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
|
if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
|
||||||
err = -errno;
|
err = -errno;
|
||||||
free(link);
|
pr_warn("prog '%s': failed to enable perf_event FD %d: %s\n",
|
||||||
pr_warn("prog '%s': failed to enable pfd %d: %s\n",
|
|
||||||
prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
return libbpf_err_ptr(err);
|
goto err_out;
|
||||||
}
|
}
|
||||||
return link;
|
|
||||||
|
return &link->link;
|
||||||
|
err_out:
|
||||||
|
if (link_fd >= 0)
|
||||||
|
close(link_fd);
|
||||||
|
free(link);
|
||||||
|
return libbpf_err_ptr(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue