On 7/1/19 2:57 PM, Andrii Nakryiko wrote: > On Mon, Jul 1, 2019 at 10:03 AM Yonghong Song <yhs@xxxxxx> wrote: >> >> >> >> On 6/28/19 8:49 PM, Andrii Nakryiko wrote: >>> bpf_program__attach_perf_event allows to attach BPF program to existing >>> perf event hook, providing most generic and most low-level way to attach BPF >>> programs. It returns struct bpf_link, which should be passed to >>> bpf_link__destroy to detach and free resources, associated with a link. >>> >>> Signed-off-by: Andrii Nakryiko <andriin@xxxxxx> >>> --- >>> tools/lib/bpf/libbpf.c | 61 ++++++++++++++++++++++++++++++++++++++++ >>> tools/lib/bpf/libbpf.h | 3 ++ >>> tools/lib/bpf/libbpf.map | 1 + >>> 3 files changed, 65 insertions(+) >>> >>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c >>> index 455795e6f8af..98c155ec3bfa 100644 >>> --- a/tools/lib/bpf/libbpf.c >>> +++ b/tools/lib/bpf/libbpf.c >>> @@ -32,6 +32,7 @@ >>> #include <linux/limits.h> >>> #include <linux/perf_event.h> >>> #include <linux/ring_buffer.h> >>> +#include <sys/ioctl.h> >>> #include <sys/stat.h> >>> #include <sys/types.h> >>> #include <sys/vfs.h> >>> @@ -3958,6 +3959,66 @@ int bpf_link__destroy(struct bpf_link *link) >>> return err; >>> } >>> >>> +struct bpf_link_fd { >>> + struct bpf_link link; /* has to be at the top of struct */ >>> + int fd; /* hook FD */ >>> +}; >>> + >>> +static int bpf_link__destroy_perf_event(struct bpf_link *link) >>> +{ >>> + struct bpf_link_fd *l = (void *)link; >>> + int err; >>> + >>> + if (l->fd < 0) >>> + return 0; >>> + >>> + err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0); >>> + if (err) >>> + err = -errno; >>> + >>> + close(l->fd); >>> + return err; >>> +} >>> + >>> +struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, >>> + int pfd) >>> +{ >>> + char errmsg[STRERR_BUFSIZE]; >>> + struct bpf_link_fd *link; >>> + int prog_fd, err; >>> + >>> + prog_fd = bpf_program__fd(prog); >>> + if (prog_fd < 0) { >>> + pr_warning("program '%s': can't attach before loaded\n", >>> + bpf_program__title(prog, false)); >>> + return ERR_PTR(-EINVAL); >>> + } >> >> should we check validity of pfd here? >> If pfd < 0, we just return ERR_PTR(-EINVAL)? > > I can add that. I didn't do it, because in general, you can provide fd >> = 0 which is still not a valid FD for PERF_EVENT_IOC_SET_BPF and > PERF_EVENT_IOC_ENABLE, so in general we can't detect this reliably. I just want a check for validity of input parameter which will failure later with dedicated error message. But the same negative pfd will be printed in ioctl error message. I am okay with this. > >> This way, in bpf_link__destroy_perf_event(), we do not need to check >> l->fd < 0 since it will be always nonnegative. > > That check is not needed anyway, because even if pfd < 0, ioctl should > fail and return error. I'll remove that check. > >> >>> + >>> + link = malloc(sizeof(*link)); >>> + if (!link) >>> + return ERR_PTR(-ENOMEM); >>> + link->link.destroy = &bpf_link__destroy_perf_event; >>> + link->fd = pfd; >>> + >>> + if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { >>> + err = -errno; >>> + free(link); >>> + pr_warning("program '%s': failed to attach to pfd %d: %s\n", >>> + bpf_program__title(prog, false), pfd, >>> + libbpf_strerror_r(err, errmsg, sizeof(errmsg))); >>> + return ERR_PTR(err); >>> + } >>> + if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { >>> + err = -errno; >>> + free(link); >>> + pr_warning("program '%s': failed to enable pfd %d: %s\n", >>> + bpf_program__title(prog, false), pfd, >>> + libbpf_strerror_r(err, errmsg, sizeof(errmsg))); >>> + return ERR_PTR(err); >>> + } >>> + return (struct bpf_link *)link; >>> +} >>> + >>> enum bpf_perf_event_ret >>> bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size, >>> void **copy_mem, size_t *copy_size, >>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h >>> index 5082a5ebb0c2..1bf66c4a9330 100644 >>> --- a/tools/lib/bpf/libbpf.h >>> +++ b/tools/lib/bpf/libbpf.h >>> @@ -169,6 +169,9 @@ struct bpf_link; >>> >>> LIBBPF_API int bpf_link__destroy(struct bpf_link *link); >>> >>> +LIBBPF_API struct bpf_link * >>> +bpf_program__attach_perf_event(struct bpf_program *prog, int pfd); >>> + >>> struct bpf_insn; >>> >>> /* >>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map >>> index 3cde850fc8da..756f5aa802e9 100644 >>> --- a/tools/lib/bpf/libbpf.map >>> +++ b/tools/lib/bpf/libbpf.map >>> @@ -169,6 +169,7 @@ LIBBPF_0.0.4 { >>> global: >>> bpf_link__destroy; >>> bpf_object__load_xattr; >>> + bpf_program__attach_perf_event; >>> btf_dump__dump_type; >>> btf_dump__free; >>> btf_dump__new; >>>