Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Andrii Nakryiko and committed by
Daniel Borkmann
668ace0e d88b71d4

+91 -22
+91 -22
tools/lib/bpf/libbpf.c
··· 193 193 FEAT_MODULE_BTF, 194 194 /* BTF_KIND_FLOAT support */ 195 195 FEAT_BTF_FLOAT, 196 + /* BPF perf link support */ 197 + FEAT_PERF_LINK, 196 198 __FEAT_CNT, 197 199 }; 198 200 ··· 4339 4337 return !err; 4340 4338 } 4341 4339 4340 + static int probe_perf_link(void) 4341 + { 4342 + struct bpf_load_program_attr attr; 4343 + struct bpf_insn insns[] = { 4344 + BPF_MOV64_IMM(BPF_REG_0, 0), 4345 + BPF_EXIT_INSN(), 4346 + }; 4347 + int prog_fd, link_fd, err; 4348 + 4349 + memset(&attr, 0, sizeof(attr)); 4350 + attr.prog_type = BPF_PROG_TYPE_TRACEPOINT; 4351 + attr.insns = insns; 4352 + attr.insns_cnt = ARRAY_SIZE(insns); 4353 + attr.license = "GPL"; 4354 + prog_fd = bpf_load_program_xattr(&attr, NULL, 0); 4355 + if (prog_fd < 0) 4356 + return -errno; 4357 + 4358 + /* use invalid perf_event FD to get EBADF, if link is supported; 4359 + * otherwise EINVAL should be returned 4360 + */ 4361 + link_fd = bpf_link_create(prog_fd, -1, BPF_PERF_EVENT, NULL); 4362 + err = -errno; /* close() can clobber errno */ 4363 + 4364 + if (link_fd >= 0) 4365 + close(link_fd); 4366 + close(prog_fd); 4367 + 4368 + return link_fd < 0 && err == -EBADF; 4369 + } 4370 + 4342 4371 enum kern_feature_result { 4343 4372 FEAT_UNKNOWN = 0, 4344 4373 FEAT_SUPPORTED = 1, ··· 4419 4386 }, 4420 4387 [FEAT_BTF_FLOAT] = { 4421 4388 "BTF_KIND_FLOAT support", probe_kern_btf_float, 4389 + }, 4390 + [FEAT_PERF_LINK] = { 4391 + "BPF perf link support", probe_perf_link, 4422 4392 }, 4423 4393 }; 4424 4394 ··· 8987 8951 return 0; 8988 8952 } 8989 8953 8990 - static int bpf_link__detach_perf_event(struct bpf_link *link) 8991 - { 8992 - int err; 8954 + struct bpf_link_perf { 8955 + struct bpf_link link; 8956 + int perf_event_fd; 8957 + }; 8993 8958 8994 - err = ioctl(link->fd, PERF_EVENT_IOC_DISABLE, 0); 8995 - if (err) 8959 + static int bpf_link_perf_detach(struct bpf_link *link) 8960 + { 8961 + struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link); 8962 + int err = 0; 8963 + 8964 + if (ioctl(perf_link->perf_event_fd, PERF_EVENT_IOC_DISABLE, 0) < 0) 8996 8965 err = -errno; 8997 8966 8967 + if (perf_link->perf_event_fd != link->fd) 8968 + close(perf_link->perf_event_fd); 8998 8969 close(link->fd); 8970 + 8999 8971 return libbpf_err(err); 8972 + } 8973 + 8974 + static void bpf_link_perf_dealloc(struct bpf_link *link) 8975 + { 8976 + struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link); 8977 + 8978 + free(perf_link); 9000 8979 } 9001 8980 9002 8981 struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, int pfd) 9003 8982 { 9004 8983 char errmsg[STRERR_BUFSIZE]; 9005 - struct bpf_link *link; 9006 - int prog_fd, err; 8984 + struct bpf_link_perf *link; 8985 + int prog_fd, link_fd = -1, err; 9007 8986 9008 8987 if (pfd < 0) { 9009 8988 pr_warn("prog '%s': invalid perf event FD %d\n", ··· 9035 8984 link = calloc(1, sizeof(*link)); 9036 8985 if (!link) 9037 8986 return libbpf_err_ptr(-ENOMEM); 9038 - link->detach = &bpf_link__detach_perf_event; 9039 - link->fd = pfd; 8987 + link->link.detach = &bpf_link_perf_detach; 8988 + link->link.dealloc = &bpf_link_perf_dealloc; 8989 + link->perf_event_fd = pfd; 9040 8990 9041 - if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { 9042 - err = -errno; 9043 - free(link); 9044 - pr_warn("prog '%s': failed to attach to pfd %d: %s\n", 9045 - prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); 9046 - if (err == -EPROTO) 9047 - pr_warn("prog '%s': try add PERF_SAMPLE_CALLCHAIN to or remove exclude_callchain_[kernel|user] from pfd %d\n", 9048 - prog->name, pfd); 9049 - return libbpf_err_ptr(err); 8991 + if (kernel_supports(prog->obj, FEAT_PERF_LINK)) { 8992 + link_fd = bpf_link_create(prog_fd, pfd, BPF_PERF_EVENT, NULL); 8993 + if (link_fd < 0) { 8994 + err = -errno; 8995 + pr_warn("prog '%s': failed to create BPF link for perf_event FD %d: %d (%s)\n", 8996 + prog->name, pfd, 8997 + err, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); 8998 + goto err_out; 8999 + } 9000 + link->link.fd = link_fd; 9001 + } else { 9002 + if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { 9003 + err = -errno; 9004 + pr_warn("prog '%s': failed to attach to perf_event FD %d: %s\n", 9005 + prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); 9006 + if (err == -EPROTO) 9007 + pr_warn("prog '%s': try add PERF_SAMPLE_CALLCHAIN to or remove exclude_callchain_[kernel|user] from pfd %d\n", 9008 + prog->name, pfd); 9009 + goto err_out; 9010 + } 9011 + link->link.fd = pfd; 9050 9012 } 9051 9013 if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { 9052 9014 err = -errno; 9053 - free(link); 9054 - pr_warn("prog '%s': failed to enable pfd %d: %s\n", 9015 + pr_warn("prog '%s': failed to enable perf_event FD %d: %s\n", 9055 9016 prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); 9056 - return libbpf_err_ptr(err); 9017 + goto err_out; 9057 9018 } 9058 - return link; 9019 + 9020 + return &link->link; 9021 + err_out: 9022 + if (link_fd >= 0) 9023 + close(link_fd); 9024 + free(link); 9025 + return libbpf_err_ptr(err); 9059 9026 } 9060 9027 9061 9028 /*