From d66f43666a6811bcc3e529d5a088e1bdde94ca1b Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:58:55 -0700 Subject: libbpf: make libbpf_strerror_r agnostic to sign of error It's often inconvenient to switch sign of error when passing it into libbpf_strerror_r. It's better for it to handle that automatically. Signed-off-by: Andrii Nakryiko Reviewed-by: Stanislav Fomichev Acked-by: Song Liu Signed-off-by: Daniel Borkmann --- tools/lib/bpf/str_error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/bpf/str_error.c b/tools/lib/bpf/str_error.c index 00e48ac5b806..b8064eedc177 100644 --- a/tools/lib/bpf/str_error.c +++ b/tools/lib/bpf/str_error.c @@ -11,7 +11,7 @@ */ char *libbpf_strerror_r(int err, char *dst, int len) { - int ret = strerror_r(err, dst, len); + int ret = strerror_r(err < 0 ? -err : err, dst, len); if (ret) snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret); return dst; -- cgit v1.2.3-59-g8ed1b From 1c2e9efc26f389effc86e9b4d41f85d101f17629 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:58:56 -0700 Subject: libbpf: introduce concept of bpf_link bpf_link is an abstraction of an association of a BPF program and one of many possible BPF attachment points (hooks). This allows to have uniform interface for detaching BPF programs regardless of the nature of link and how it was created. Details of creation and setting up of a specific bpf_link is handled by corresponding attachment methods (bpf_program__attach_xxx) added in subsequent commits. Once successfully created, bpf_link has to be eventually destroyed with bpf_link__destroy(), at which point BPF program is disassociated from a hook and all the relevant resources are freed. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Reviewed-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 17 +++++++++++++++++ tools/lib/bpf/libbpf.h | 4 ++++ tools/lib/bpf/libbpf.map | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 4907997289e9..f308690abd50 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -3941,6 +3941,23 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, return 0; } +struct bpf_link { + int (*destroy)(struct bpf_link *link); +}; + +int bpf_link__destroy(struct bpf_link *link) +{ + int err; + + if (!link) + return 0; + + err = link->destroy(link); + free(link); + + return err; +} + 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 d639f47e3110..5082a5ebb0c2 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -165,6 +165,10 @@ LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path); LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path); LIBBPF_API void bpf_program__unload(struct bpf_program *prog); +struct bpf_link; + +LIBBPF_API int bpf_link__destroy(struct bpf_link *link); + struct bpf_insn; /* diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 2c6d835620d2..3cde850fc8da 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -167,10 +167,11 @@ LIBBPF_0.0.3 { LIBBPF_0.0.4 { global: + bpf_link__destroy; + bpf_object__load_xattr; btf_dump__dump_type; btf_dump__free; btf_dump__new; btf__parse_elf; - bpf_object__load_xattr; libbpf_num_possible_cpus; } LIBBPF_0.0.3; -- cgit v1.2.3-59-g8ed1b From 63f2f5ee856ba9db751123bc5519be099a3849a7 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:58:57 -0700 Subject: libbpf: add ability to attach/detach BPF program to perf event 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 Reviewed-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.h | 3 +++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 67 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index f308690abd50..54ce22f9f777 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -3958,6 +3959,68 @@ 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; + + 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; + + if (pfd < 0) { + pr_warning("program '%s': invalid perf event FD %d\n", + bpf_program__title(prog, false), pfd); + return ERR_PTR(-EINVAL); + } + prog_fd = bpf_program__fd(prog); + if (prog_fd < 0) { + pr_warning("program '%s': can't attach BPF program w/o FD (did you load it?)\n", + bpf_program__title(prog, false)); + return ERR_PTR(-EINVAL); + } + + 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; -- cgit v1.2.3-59-g8ed1b From b26500274767bacbe571c75926fee2489f91ba28 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:58:58 -0700 Subject: libbpf: add kprobe/uprobe attach API Add ability to attach to kernel and user probes and retprobes. Implementation depends on perf event support for kprobes/uprobes. Signed-off-by: Andrii Nakryiko Reviewed-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.h | 7 ++ tools/lib/bpf/libbpf.map | 2 + 3 files changed, 178 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 54ce22f9f777..b5b49ddb152f 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -4021,6 +4021,175 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, return (struct bpf_link *)link; } +/* + * this function is expected to parse integer in the range of [0, 2^31-1] from + * given file using scanf format string fmt. If actual parsed value is + * negative, the result might be indistinguishable from error + */ +static int parse_uint_from_file(const char *file, const char *fmt) +{ + char buf[STRERR_BUFSIZE]; + int err, ret; + FILE *f; + + f = fopen(file, "r"); + if (!f) { + err = -errno; + pr_debug("failed to open '%s': %s\n", file, + libbpf_strerror_r(err, buf, sizeof(buf))); + return err; + } + err = fscanf(f, fmt, &ret); + if (err != 1) { + err = err == EOF ? -EIO : -errno; + pr_debug("failed to parse '%s': %s\n", file, + libbpf_strerror_r(err, buf, sizeof(buf))); + fclose(f); + return err; + } + fclose(f); + return ret; +} + +static int determine_kprobe_perf_type(void) +{ + const char *file = "/sys/bus/event_source/devices/kprobe/type"; + + return parse_uint_from_file(file, "%d\n"); +} + +static int determine_uprobe_perf_type(void) +{ + const char *file = "/sys/bus/event_source/devices/uprobe/type"; + + return parse_uint_from_file(file, "%d\n"); +} + +static int determine_kprobe_retprobe_bit(void) +{ + const char *file = "/sys/bus/event_source/devices/kprobe/format/retprobe"; + + return parse_uint_from_file(file, "config:%d\n"); +} + +static int determine_uprobe_retprobe_bit(void) +{ + const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe"; + + return parse_uint_from_file(file, "config:%d\n"); +} + +static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name, + uint64_t offset, int pid) +{ + struct perf_event_attr attr = {}; + char errmsg[STRERR_BUFSIZE]; + int type, pfd, err; + + type = uprobe ? determine_uprobe_perf_type() + : determine_kprobe_perf_type(); + if (type < 0) { + pr_warning("failed to determine %s perf type: %s\n", + uprobe ? "uprobe" : "kprobe", + libbpf_strerror_r(type, errmsg, sizeof(errmsg))); + return type; + } + if (retprobe) { + int bit = uprobe ? determine_uprobe_retprobe_bit() + : determine_kprobe_retprobe_bit(); + + if (bit < 0) { + pr_warning("failed to determine %s retprobe bit: %s\n", + uprobe ? "uprobe" : "kprobe", + libbpf_strerror_r(bit, errmsg, + sizeof(errmsg))); + return bit; + } + attr.config |= 1 << bit; + } + attr.size = sizeof(attr); + attr.type = type; + attr.config1 = (uint64_t)(void *)name; /* kprobe_func or uprobe_path */ + attr.config2 = offset; /* kprobe_addr or probe_offset */ + + /* pid filter is meaningful only for uprobes */ + pfd = syscall(__NR_perf_event_open, &attr, + pid < 0 ? -1 : pid /* pid */, + pid == -1 ? 0 : -1 /* cpu */, + -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC); + if (pfd < 0) { + err = -errno; + pr_warning("%s perf_event_open() failed: %s\n", + uprobe ? "uprobe" : "kprobe", + libbpf_strerror_r(err, errmsg, sizeof(errmsg))); + return err; + } + return pfd; +} + +struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog, + bool retprobe, + const char *func_name) +{ + char errmsg[STRERR_BUFSIZE]; + struct bpf_link *link; + int pfd, err; + + pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name, + 0 /* offset */, -1 /* pid */); + if (pfd < 0) { + pr_warning("program '%s': failed to create %s '%s' perf event: %s\n", + bpf_program__title(prog, false), + retprobe ? "kretprobe" : "kprobe", func_name, + libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); + return ERR_PTR(pfd); + } + link = bpf_program__attach_perf_event(prog, pfd); + if (IS_ERR(link)) { + close(pfd); + err = PTR_ERR(link); + pr_warning("program '%s': failed to attach to %s '%s': %s\n", + bpf_program__title(prog, false), + retprobe ? "kretprobe" : "kprobe", func_name, + libbpf_strerror_r(err, errmsg, sizeof(errmsg))); + return link; + } + return link; +} + +struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog, + bool retprobe, pid_t pid, + const char *binary_path, + size_t func_offset) +{ + char errmsg[STRERR_BUFSIZE]; + struct bpf_link *link; + int pfd, err; + + pfd = perf_event_open_probe(true /* uprobe */, retprobe, + binary_path, func_offset, pid); + if (pfd < 0) { + pr_warning("program '%s': failed to create %s '%s:0x%zx' perf event: %s\n", + bpf_program__title(prog, false), + retprobe ? "uretprobe" : "uprobe", + binary_path, func_offset, + libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); + return ERR_PTR(pfd); + } + link = bpf_program__attach_perf_event(prog, pfd); + if (IS_ERR(link)) { + close(pfd); + err = PTR_ERR(link); + pr_warning("program '%s': failed to attach to %s '%s:0x%zx': %s\n", + bpf_program__title(prog, false), + retprobe ? "uretprobe" : "uprobe", + binary_path, func_offset, + libbpf_strerror_r(err, errmsg, sizeof(errmsg))); + return link; + } + return 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 1bf66c4a9330..bd767cc11967 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -171,6 +171,13 @@ 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); +LIBBPF_API struct bpf_link * +bpf_program__attach_kprobe(struct bpf_program *prog, bool retprobe, + const char *func_name); +LIBBPF_API struct bpf_link * +bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe, + pid_t pid, const char *binary_path, + size_t func_offset); struct bpf_insn; diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 756f5aa802e9..57a40fb60718 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -169,7 +169,9 @@ LIBBPF_0.0.4 { global: bpf_link__destroy; bpf_object__load_xattr; + bpf_program__attach_kprobe; bpf_program__attach_perf_event; + bpf_program__attach_uprobe; btf_dump__dump_type; btf_dump__free; btf_dump__new; -- cgit v1.2.3-59-g8ed1b From f6de59c17f111b3efc701c95b438e46245c4aeb1 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:58:59 -0700 Subject: libbpf: add tracepoint attach API Allow attaching BPF programs to kernel tracepoint BPF hooks specified by category and name. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Reviewed-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.h | 4 +++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 84 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index b5b49ddb152f..85174fcd99e0 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -4190,6 +4190,85 @@ struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog, return link; } +static int determine_tracepoint_id(const char *tp_category, + const char *tp_name) +{ + char file[PATH_MAX]; + int ret; + + ret = snprintf(file, sizeof(file), + "/sys/kernel/debug/tracing/events/%s/%s/id", + tp_category, tp_name); + if (ret < 0) + return -errno; + if (ret >= sizeof(file)) { + pr_debug("tracepoint %s/%s path is too long\n", + tp_category, tp_name); + return -E2BIG; + } + return parse_uint_from_file(file, "%d\n"); +} + +static int perf_event_open_tracepoint(const char *tp_category, + const char *tp_name) +{ + struct perf_event_attr attr = {}; + char errmsg[STRERR_BUFSIZE]; + int tp_id, pfd, err; + + tp_id = determine_tracepoint_id(tp_category, tp_name); + if (tp_id < 0) { + pr_warning("failed to determine tracepoint '%s/%s' perf event ID: %s\n", + tp_category, tp_name, + libbpf_strerror_r(tp_id, errmsg, sizeof(errmsg))); + return tp_id; + } + + attr.type = PERF_TYPE_TRACEPOINT; + attr.size = sizeof(attr); + attr.config = tp_id; + + pfd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu */, + -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC); + if (pfd < 0) { + err = -errno; + pr_warning("tracepoint '%s/%s' perf_event_open() failed: %s\n", + tp_category, tp_name, + libbpf_strerror_r(err, errmsg, sizeof(errmsg))); + return err; + } + return pfd; +} + +struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog, + const char *tp_category, + const char *tp_name) +{ + char errmsg[STRERR_BUFSIZE]; + struct bpf_link *link; + int pfd, err; + + pfd = perf_event_open_tracepoint(tp_category, tp_name); + if (pfd < 0) { + pr_warning("program '%s': failed to create tracepoint '%s/%s' perf event: %s\n", + bpf_program__title(prog, false), + tp_category, tp_name, + libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); + return ERR_PTR(pfd); + } + link = bpf_program__attach_perf_event(prog, pfd); + if (IS_ERR(link)) { + close(pfd); + err = PTR_ERR(link); + pr_warning("program '%s': failed to attach to tracepoint '%s/%s': %s\n", + bpf_program__title(prog, false), + tp_category, tp_name, + libbpf_strerror_r(err, errmsg, sizeof(errmsg))); + return link; + } + return 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 bd767cc11967..60611f4b4e1d 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -178,6 +178,10 @@ LIBBPF_API struct bpf_link * bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe, pid_t pid, const char *binary_path, size_t func_offset); +LIBBPF_API struct bpf_link * +bpf_program__attach_tracepoint(struct bpf_program *prog, + const char *tp_category, + const char *tp_name); struct bpf_insn; diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 57a40fb60718..3c618b75ef65 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -171,6 +171,7 @@ LIBBPF_0.0.4 { bpf_object__load_xattr; bpf_program__attach_kprobe; bpf_program__attach_perf_event; + bpf_program__attach_tracepoint; bpf_program__attach_uprobe; btf_dump__dump_type; btf_dump__free; -- cgit v1.2.3-59-g8ed1b From 84bf5e1f4f174fc4f06bc747dafa1a7cfa4e9a67 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:59:00 -0700 Subject: libbpf: add raw tracepoint attach API Add a wrapper utilizing bpf_link "infrastructure" to allow attaching BPF programs to raw tracepoints. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Reviewed-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 39 +++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.h | 3 +++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 43 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 85174fcd99e0..981fb5c3c6c9 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -4269,6 +4269,45 @@ struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog, return link; } +static int bpf_link__destroy_fd(struct bpf_link *link) +{ + struct bpf_link_fd *l = (void *)link; + + return close(l->fd); +} + +struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog, + const char *tp_name) +{ + char errmsg[STRERR_BUFSIZE]; + struct bpf_link_fd *link; + int prog_fd, pfd; + + 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); + } + + link = malloc(sizeof(*link)); + if (!link) + return ERR_PTR(-ENOMEM); + link->link.destroy = &bpf_link__destroy_fd; + + pfd = bpf_raw_tracepoint_open(tp_name, prog_fd); + if (pfd < 0) { + pfd = -errno; + free(link); + pr_warning("program '%s': failed to attach to raw tracepoint '%s': %s\n", + bpf_program__title(prog, false), tp_name, + libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); + return ERR_PTR(pfd); + } + link->fd = pfd; + 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 60611f4b4e1d..f55933784f95 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -182,6 +182,9 @@ LIBBPF_API struct bpf_link * bpf_program__attach_tracepoint(struct bpf_program *prog, const char *tp_category, const char *tp_name); +LIBBPF_API struct bpf_link * +bpf_program__attach_raw_tracepoint(struct bpf_program *prog, + const char *tp_name); struct bpf_insn; diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 3c618b75ef65..e6b7d4edbc93 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -171,6 +171,7 @@ LIBBPF_0.0.4 { bpf_object__load_xattr; bpf_program__attach_kprobe; bpf_program__attach_perf_event; + bpf_program__attach_raw_tracepoint; bpf_program__attach_tracepoint; bpf_program__attach_uprobe; btf_dump__dump_type; -- cgit v1.2.3-59-g8ed1b From 8c51b314d2eeae687bf8c28491c9f5e4f05f30f5 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:59:01 -0700 Subject: selftests/bpf: switch test to new attach_perf_event API Use new bpf_program__attach_perf_event() in test previously relying on direct ioctl manipulations. Signed-off-by: Andrii Nakryiko Reviewed-by: Stanislav Fomichev Acked-by: Song Liu Signed-off-by: Daniel Borkmann --- .../bpf/prog_tests/stacktrace_build_id_nmi.c | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c index 1c1a2f75f3d8..9557b7dfb782 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c @@ -17,6 +17,7 @@ static __u64 read_perf_max_sample_freq(void) void test_stacktrace_build_id_nmi(void) { int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd; + const char *prog_name = "tracepoint/random/urandom_read"; const char *file = "./test_stacktrace_build_id.o"; int err, pmu_fd, prog_fd; struct perf_event_attr attr = { @@ -25,7 +26,9 @@ void test_stacktrace_build_id_nmi(void) .config = PERF_COUNT_HW_CPU_CYCLES, }; __u32 key, previous_key, val, duration = 0; + struct bpf_program *prog; struct bpf_object *obj; + struct bpf_link *link; char buf[256]; int i, j; struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH]; @@ -39,6 +42,10 @@ retry: if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) return; + prog = bpf_object__find_program_by_title(obj, prog_name); + if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name)) + goto close_prog; + pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu 0 */, -1 /* group id */, 0 /* flags */); @@ -47,15 +54,12 @@ retry: pmu_fd, errno)) goto close_prog; - err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); - if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", - err, errno)) - goto close_pmu; - - err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); - if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", - err, errno)) - goto disable_pmu; + link = bpf_program__attach_perf_event(prog, pmu_fd); + if (CHECK(IS_ERR(link), "attach_perf_event", + "err %ld\n", PTR_ERR(link))) { + close(pmu_fd); + goto close_prog; + } /* find map fds */ control_map_fd = bpf_find_map(__func__, obj, "control_map"); @@ -134,8 +138,7 @@ retry: * try it one more time. */ if (build_id_matches < 1 && retry--) { - ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); - close(pmu_fd); + bpf_link__destroy(link); bpf_object__close(obj); printf("%s:WARN:Didn't find expected build ID from the map, retrying\n", __func__); @@ -154,11 +157,7 @@ retry: */ disable_pmu: - ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); - -close_pmu: - close(pmu_fd); - + bpf_link__destroy(link); close_prog: bpf_object__close(obj); } -- cgit v1.2.3-59-g8ed1b From 1e8611bbdfc99faf8c9670dbbe582129a9a29973 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:59:02 -0700 Subject: selftests/bpf: add kprobe/uprobe selftests Add tests verifying kprobe/kretprobe/uprobe/uretprobe APIs work as expected. Signed-off-by: Andrii Nakryiko Reviewed-by: Stanislav Fomichev Acked-by: Song Liu Signed-off-by: Daniel Borkmann --- .../selftests/bpf/prog_tests/attach_probe.c | 166 +++++++++++++++++++++ .../selftests/bpf/progs/test_attach_probe.c | 55 +++++++ 2 files changed, 221 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/attach_probe.c create mode 100644 tools/testing/selftests/bpf/progs/test_attach_probe.c (limited to 'tools') diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c new file mode 100644 index 000000000000..a4686395522c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +ssize_t get_base_addr() { + size_t start; + char buf[256]; + FILE *f; + + f = fopen("/proc/self/maps", "r"); + if (!f) + return -errno; + + while (fscanf(f, "%zx-%*x %s %*s\n", &start, buf) == 2) { + if (strcmp(buf, "r-xp") == 0) { + fclose(f); + return start; + } + } + + fclose(f); + return -EINVAL; +} + +#ifdef __x86_64__ +#define SYS_KPROBE_NAME "__x64_sys_nanosleep" +#else +#define SYS_KPROBE_NAME "sys_nanosleep" +#endif + +void test_attach_probe(void) +{ + const char *kprobe_name = "kprobe/sys_nanosleep"; + const char *kretprobe_name = "kretprobe/sys_nanosleep"; + const char *uprobe_name = "uprobe/trigger_func"; + const char *uretprobe_name = "uretprobe/trigger_func"; + const int kprobe_idx = 0, kretprobe_idx = 1; + const int uprobe_idx = 2, uretprobe_idx = 3; + const char *file = "./test_attach_probe.o"; + struct bpf_program *kprobe_prog, *kretprobe_prog; + struct bpf_program *uprobe_prog, *uretprobe_prog; + struct bpf_object *obj; + int err, prog_fd, duration = 0, res; + struct bpf_link *kprobe_link = NULL; + struct bpf_link *kretprobe_link = NULL; + struct bpf_link *uprobe_link = NULL; + struct bpf_link *uretprobe_link = NULL; + int results_map_fd; + size_t uprobe_offset; + ssize_t base_addr; + + base_addr = get_base_addr(); + if (CHECK(base_addr < 0, "get_base_addr", + "failed to find base addr: %zd", base_addr)) + return; + uprobe_offset = (size_t)&get_base_addr - base_addr; + + /* load programs */ + err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd); + if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno)) + return; + + kprobe_prog = bpf_object__find_program_by_title(obj, kprobe_name); + if (CHECK(!kprobe_prog, "find_probe", + "prog '%s' not found\n", kprobe_name)) + goto cleanup; + kretprobe_prog = bpf_object__find_program_by_title(obj, kretprobe_name); + if (CHECK(!kretprobe_prog, "find_probe", + "prog '%s' not found\n", kretprobe_name)) + goto cleanup; + uprobe_prog = bpf_object__find_program_by_title(obj, uprobe_name); + if (CHECK(!uprobe_prog, "find_probe", + "prog '%s' not found\n", uprobe_name)) + goto cleanup; + uretprobe_prog = bpf_object__find_program_by_title(obj, uretprobe_name); + if (CHECK(!uretprobe_prog, "find_probe", + "prog '%s' not found\n", uretprobe_name)) + goto cleanup; + + /* load maps */ + results_map_fd = bpf_find_map(__func__, obj, "results_map"); + if (CHECK(results_map_fd < 0, "find_results_map", + "err %d\n", results_map_fd)) + goto cleanup; + + kprobe_link = bpf_program__attach_kprobe(kprobe_prog, + false /* retprobe */, + SYS_KPROBE_NAME); + if (CHECK(IS_ERR(kprobe_link), "attach_kprobe", + "err %ld\n", PTR_ERR(kprobe_link))) { + kprobe_link = NULL; + goto cleanup; + } + kretprobe_link = bpf_program__attach_kprobe(kretprobe_prog, + true /* retprobe */, + SYS_KPROBE_NAME); + if (CHECK(IS_ERR(kretprobe_link), "attach_kretprobe", + "err %ld\n", PTR_ERR(kretprobe_link))) { + kretprobe_link = NULL; + goto cleanup; + } + uprobe_link = bpf_program__attach_uprobe(uprobe_prog, + false /* retprobe */, + 0 /* self pid */, + "/proc/self/exe", + uprobe_offset); + if (CHECK(IS_ERR(uprobe_link), "attach_uprobe", + "err %ld\n", PTR_ERR(uprobe_link))) { + uprobe_link = NULL; + goto cleanup; + } + uretprobe_link = bpf_program__attach_uprobe(uretprobe_prog, + true /* retprobe */, + -1 /* any pid */, + "/proc/self/exe", + uprobe_offset); + if (CHECK(IS_ERR(uretprobe_link), "attach_uretprobe", + "err %ld\n", PTR_ERR(uretprobe_link))) { + uretprobe_link = NULL; + goto cleanup; + } + + /* trigger & validate kprobe && kretprobe */ + usleep(1); + + err = bpf_map_lookup_elem(results_map_fd, &kprobe_idx, &res); + if (CHECK(err, "get_kprobe_res", + "failed to get kprobe res: %d\n", err)) + goto cleanup; + if (CHECK(res != kprobe_idx + 1, "check_kprobe_res", + "wrong kprobe res: %d\n", res)) + goto cleanup; + + err = bpf_map_lookup_elem(results_map_fd, &kretprobe_idx, &res); + if (CHECK(err, "get_kretprobe_res", + "failed to get kretprobe res: %d\n", err)) + goto cleanup; + if (CHECK(res != kretprobe_idx + 1, "check_kretprobe_res", + "wrong kretprobe res: %d\n", res)) + goto cleanup; + + /* trigger & validate uprobe & uretprobe */ + get_base_addr(); + + err = bpf_map_lookup_elem(results_map_fd, &uprobe_idx, &res); + if (CHECK(err, "get_uprobe_res", + "failed to get uprobe res: %d\n", err)) + goto cleanup; + if (CHECK(res != uprobe_idx + 1, "check_uprobe_res", + "wrong uprobe res: %d\n", res)) + goto cleanup; + + err = bpf_map_lookup_elem(results_map_fd, &uretprobe_idx, &res); + if (CHECK(err, "get_uretprobe_res", + "failed to get uretprobe res: %d\n", err)) + goto cleanup; + if (CHECK(res != uretprobe_idx + 1, "check_uretprobe_res", + "wrong uretprobe res: %d\n", res)) + goto cleanup; + +cleanup: + bpf_link__destroy(kprobe_link); + bpf_link__destroy(kretprobe_link); + bpf_link__destroy(uprobe_link); + bpf_link__destroy(uretprobe_link); + bpf_object__close(obj); +} diff --git a/tools/testing/selftests/bpf/progs/test_attach_probe.c b/tools/testing/selftests/bpf/progs/test_attach_probe.c new file mode 100644 index 000000000000..7a7c5cd728c8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_attach_probe.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Facebook + +#include +#include +#include "bpf_helpers.h" + +struct { + int type; + int max_entries; + int *key; + int *value; +} results_map SEC(".maps") = { + .type = BPF_MAP_TYPE_ARRAY, + .max_entries = 4, +}; + +SEC("kprobe/sys_nanosleep") +int handle_sys_nanosleep_entry(struct pt_regs *ctx) +{ + const int key = 0, value = 1; + + bpf_map_update_elem(&results_map, &key, &value, 0); + return 0; +} + +SEC("kretprobe/sys_nanosleep") +int handle_sys_getpid_return(struct pt_regs *ctx) +{ + const int key = 1, value = 2; + + bpf_map_update_elem(&results_map, &key, &value, 0); + return 0; +} + +SEC("uprobe/trigger_func") +int handle_uprobe_entry(struct pt_regs *ctx) +{ + const int key = 2, value = 3; + + bpf_map_update_elem(&results_map, &key, &value, 0); + return 0; +} + +SEC("uretprobe/trigger_func") +int handle_uprobe_return(struct pt_regs *ctx) +{ + const int key = 3, value = 4; + + bpf_map_update_elem(&results_map, &key, &value, 0); + return 0; +} + +char _license[] SEC("license") = "GPL"; +__u32 _version SEC("version") = 1; -- cgit v1.2.3-59-g8ed1b From 1bdb34274acd4a772865289aeaa41696acb60110 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 1 Jul 2019 16:59:03 -0700 Subject: selftests/bpf: convert existing tracepoint tests to new APIs Convert some existing tests that attach to tracepoints to use bpf_program__attach_tracepoint API instead. Signed-off-by: Andrii Nakryiko Reviewed-by: Stanislav Fomichev Acked-by: Song Liu Signed-off-by: Daniel Borkmann --- .../selftests/bpf/prog_tests/stacktrace_build_id.c | 55 +++++----------------- .../selftests/bpf/prog_tests/stacktrace_map.c | 43 ++++------------- .../bpf/prog_tests/stacktrace_map_raw_tp.c | 15 ++++-- 3 files changed, 32 insertions(+), 81 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c index 3aab2b083c71..ac44fda84833 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c @@ -4,11 +4,13 @@ void test_stacktrace_build_id(void) { int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd; + const char *prog_name = "tracepoint/random/urandom_read"; const char *file = "./test_stacktrace_build_id.o"; - int bytes, efd, err, pmu_fd, prog_fd, stack_trace_len; - struct perf_event_attr attr = {}; + int err, prog_fd, stack_trace_len; __u32 key, previous_key, val, duration = 0; + struct bpf_program *prog; struct bpf_object *obj; + struct bpf_link *link = NULL; char buf[256]; int i, j; struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH]; @@ -18,44 +20,16 @@ void test_stacktrace_build_id(void) retry: err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) - goto out; + return; - /* Get the ID for the sched/sched_switch tracepoint */ - snprintf(buf, sizeof(buf), - "/sys/kernel/debug/tracing/events/random/urandom_read/id"); - efd = open(buf, O_RDONLY, 0); - if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno)) + prog = bpf_object__find_program_by_title(obj, prog_name); + if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name)) goto close_prog; - bytes = read(efd, buf, sizeof(buf)); - close(efd); - if (CHECK(bytes <= 0 || bytes >= sizeof(buf), - "read", "bytes %d errno %d\n", bytes, errno)) + link = bpf_program__attach_tracepoint(prog, "random", "urandom_read"); + if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link))) goto close_prog; - /* Open the perf event and attach bpf progrram */ - attr.config = strtol(buf, NULL, 0); - attr.type = PERF_TYPE_TRACEPOINT; - attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN; - attr.sample_period = 1; - attr.wakeup_events = 1; - pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, - 0 /* cpu 0 */, -1 /* group id */, - 0 /* flags */); - if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", - pmu_fd, errno)) - goto close_prog; - - err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); - if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", - err, errno)) - goto close_pmu; - - err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); - if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", - err, errno)) - goto disable_pmu; - /* find map fds */ control_map_fd = bpf_find_map(__func__, obj, "control_map"); if (CHECK(control_map_fd < 0, "bpf_find_map control_map", @@ -133,8 +107,7 @@ retry: * try it one more time. */ if (build_id_matches < 1 && retry--) { - ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); - close(pmu_fd); + bpf_link__destroy(link); bpf_object__close(obj); printf("%s:WARN:Didn't find expected build ID from the map, retrying\n", __func__); @@ -152,14 +125,8 @@ retry: "err %d errno %d\n", err, errno); disable_pmu: - ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); - -close_pmu: - close(pmu_fd); + bpf_link__destroy(link); close_prog: bpf_object__close(obj); - -out: - return; } diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c index 2bfd50a0d6d1..fc539335c5b3 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c @@ -4,50 +4,26 @@ void test_stacktrace_map(void) { int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd; + const char *prog_name = "tracepoint/sched/sched_switch"; + int err, prog_fd, stack_trace_len; const char *file = "./test_stacktrace_map.o"; - int bytes, efd, err, pmu_fd, prog_fd, stack_trace_len; - struct perf_event_attr attr = {}; __u32 key, val, duration = 0; + struct bpf_program *prog; struct bpf_object *obj; - char buf[256]; + struct bpf_link *link; err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) return; - /* Get the ID for the sched/sched_switch tracepoint */ - snprintf(buf, sizeof(buf), - "/sys/kernel/debug/tracing/events/sched/sched_switch/id"); - efd = open(buf, O_RDONLY, 0); - if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno)) + prog = bpf_object__find_program_by_title(obj, prog_name); + if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name)) goto close_prog; - bytes = read(efd, buf, sizeof(buf)); - close(efd); - if (bytes <= 0 || bytes >= sizeof(buf)) + link = bpf_program__attach_tracepoint(prog, "sched", "sched_switch"); + if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link))) goto close_prog; - /* Open the perf event and attach bpf progrram */ - attr.config = strtol(buf, NULL, 0); - attr.type = PERF_TYPE_TRACEPOINT; - attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN; - attr.sample_period = 1; - attr.wakeup_events = 1; - pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, - 0 /* cpu 0 */, -1 /* group id */, - 0 /* flags */); - if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", - pmu_fd, errno)) - goto close_prog; - - err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); - if (err) - goto disable_pmu; - - err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); - if (err) - goto disable_pmu; - /* find map fds */ control_map_fd = bpf_find_map(__func__, obj, "control_map"); if (control_map_fd < 0) @@ -96,8 +72,7 @@ void test_stacktrace_map(void) disable_pmu: error_cnt++; disable_pmu_noerr: - ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); - close(pmu_fd); + bpf_link__destroy(link); close_prog: bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c index 1f8387d80fd7..fbfa8e76cf63 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c @@ -3,18 +3,25 @@ void test_stacktrace_map_raw_tp(void) { + const char *prog_name = "tracepoint/sched/sched_switch"; int control_map_fd, stackid_hmap_fd, stackmap_fd; const char *file = "./test_stacktrace_map.o"; - int efd, err, prog_fd; __u32 key, val, duration = 0; + int err, prog_fd; + struct bpf_program *prog; struct bpf_object *obj; + struct bpf_link *link = NULL; err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd); if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno)) return; - efd = bpf_raw_tracepoint_open("sched_switch", prog_fd); - if (CHECK(efd < 0, "raw_tp_open", "err %d errno %d\n", efd, errno)) + prog = bpf_object__find_program_by_title(obj, prog_name); + if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name)) + goto close_prog; + + link = bpf_program__attach_raw_tracepoint(prog, "sched_switch"); + if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link))) goto close_prog; /* find map fds */ @@ -55,5 +62,7 @@ void test_stacktrace_map_raw_tp(void) close_prog: error_cnt++; close_prog_noerr: + if (!IS_ERR_OR_NULL(link)) + bpf_link__destroy(link); bpf_object__close(obj); } -- cgit v1.2.3-59-g8ed1b From ef99b02b23ef4308057573f76750e661b5aea3e1 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 5 Jul 2019 08:50:09 -0700 Subject: libbpf: capture value in BTF type info for BTF-defined map defs Change BTF-defined map definitions to capture compile-time integer values as part of BTF type definition, to avoid split of key/value type information and actual type/size/flags initialization for maps. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 58 ++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 30 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 981fb5c3c6c9..2a08eb106221 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1029,40 +1029,40 @@ static const struct btf_type *skip_mods_and_typedefs(const struct btf *btf, } } -static bool get_map_field_int(const char *map_name, - const struct btf *btf, +/* + * Fetch integer attribute of BTF map definition. Such attributes are + * represented using a pointer to an array, in which dimensionality of array + * encodes specified integer value. E.g., int (*type)[BPF_MAP_TYPE_ARRAY]; + * encodes `type => BPF_MAP_TYPE_ARRAY` key/value pair completely using BTF + * type definition, while using only sizeof(void *) space in ELF data section. + */ +static bool get_map_field_int(const char *map_name, const struct btf *btf, const struct btf_type *def, - const struct btf_member *m, - const void *data, __u32 *res) { + const struct btf_member *m, __u32 *res) { const struct btf_type *t = skip_mods_and_typedefs(btf, m->type); const char *name = btf__name_by_offset(btf, m->name_off); - __u32 int_info = *(const __u32 *)(const void *)(t + 1); + const struct btf_array *arr_info; + const struct btf_type *arr_t; - if (BTF_INFO_KIND(t->info) != BTF_KIND_INT) { - pr_warning("map '%s': attr '%s': expected INT, got %u.\n", + if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) { + pr_warning("map '%s': attr '%s': expected PTR, got %u.\n", map_name, name, BTF_INFO_KIND(t->info)); return false; } - if (t->size != 4 || BTF_INT_BITS(int_info) != 32 || - BTF_INT_OFFSET(int_info)) { - pr_warning("map '%s': attr '%s': expected 32-bit non-bitfield integer, " - "got %u-byte (%d-bit) one with bit offset %d.\n", - map_name, name, t->size, BTF_INT_BITS(int_info), - BTF_INT_OFFSET(int_info)); - return false; - } - if (BTF_INFO_KFLAG(def->info) && BTF_MEMBER_BITFIELD_SIZE(m->offset)) { - pr_warning("map '%s': attr '%s': bitfield is not supported.\n", - map_name, name); + + arr_t = btf__type_by_id(btf, t->type); + if (!arr_t) { + pr_warning("map '%s': attr '%s': type [%u] not found.\n", + map_name, name, t->type); return false; } - if (m->offset % 32) { - pr_warning("map '%s': attr '%s': unaligned fields are not supported.\n", - map_name, name); + if (BTF_INFO_KIND(arr_t->info) != BTF_KIND_ARRAY) { + pr_warning("map '%s': attr '%s': expected ARRAY, got %u.\n", + map_name, name, BTF_INFO_KIND(arr_t->info)); return false; } - - *res = *(const __u32 *)(data + m->offset / 8); + arr_info = (const void *)(arr_t + 1); + *res = arr_info->nelems; return true; } @@ -1075,7 +1075,6 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, const struct btf_var_secinfo *vi; const struct btf_var *var_extra; const struct btf_member *m; - const void *def_data; const char *map_name; struct bpf_map *map; int vlen, i; @@ -1132,7 +1131,6 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, pr_debug("map '%s': at sec_idx %d, offset %zu.\n", map_name, map->sec_idx, map->sec_offset); - def_data = data->d_buf + vi->offset; vlen = BTF_INFO_VLEN(def->info); m = (const void *)(def + 1); for (i = 0; i < vlen; i++, m++) { @@ -1145,19 +1143,19 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, } if (strcmp(name, "type") == 0) { if (!get_map_field_int(map_name, obj->btf, def, m, - def_data, &map->def.type)) + &map->def.type)) return -EINVAL; pr_debug("map '%s': found type = %u.\n", map_name, map->def.type); } else if (strcmp(name, "max_entries") == 0) { if (!get_map_field_int(map_name, obj->btf, def, m, - def_data, &map->def.max_entries)) + &map->def.max_entries)) return -EINVAL; pr_debug("map '%s': found max_entries = %u.\n", map_name, map->def.max_entries); } else if (strcmp(name, "map_flags") == 0) { if (!get_map_field_int(map_name, obj->btf, def, m, - def_data, &map->def.map_flags)) + &map->def.map_flags)) return -EINVAL; pr_debug("map '%s': found map_flags = %u.\n", map_name, map->def.map_flags); @@ -1165,7 +1163,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, __u32 sz; if (!get_map_field_int(map_name, obj->btf, def, m, - def_data, &sz)) + &sz)) return -EINVAL; pr_debug("map '%s': found key_size = %u.\n", map_name, sz); @@ -1208,7 +1206,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, __u32 sz; if (!get_map_field_int(map_name, obj->btf, def, m, - def_data, &sz)) + &sz)) return -EINVAL; pr_debug("map '%s': found value_size = %u.\n", map_name, sz); -- cgit v1.2.3-59-g8ed1b From 00acd00814527a1856c92beda17475205bddaba6 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 5 Jul 2019 08:50:10 -0700 Subject: selftests/bpf: add __uint and __type macro for BTF-defined maps Add simple __uint and __type macro that hide details of how type and integer values are captured in BTF-defined maps. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/bpf_helpers.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 1a5b1accf091..5a3d92c8bec8 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -8,6 +8,9 @@ */ #define SEC(NAME) __attribute__((section(NAME), used)) +#define __uint(name, val) int (*name)[val] +#define __type(name, val) val *name + /* helper macro to print out debug messages */ #define bpf_printk(fmt, ...) \ ({ \ -- cgit v1.2.3-59-g8ed1b From bc7430cc8bfb51577e466a8ca02ad87375a70bde Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 5 Jul 2019 08:50:11 -0700 Subject: selftests/bpf: convert selftests using BTF-defined maps to new syntax Convert all the existing selftests that are already using BTF-defined maps to use new syntax (with no static data initialization). Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/progs/bpf_flow.c | 28 +++----- tools/testing/selftests/bpf/progs/netcnt_prog.c | 20 +++--- .../selftests/bpf/progs/socket_cookie_prog.c | 13 ++-- tools/testing/selftests/bpf/progs/test_btf_newkv.c | 13 ++-- .../selftests/bpf/progs/test_get_stack_rawtp.c | 39 ++++------- .../testing/selftests/bpf/progs/test_global_data.c | 37 ++++------ tools/testing/selftests/bpf/progs/test_l4lb.c | 65 +++++++---------- .../selftests/bpf/progs/test_l4lb_noinline.c | 65 +++++++---------- tools/testing/selftests/bpf/progs/test_map_lock.c | 26 +++---- .../bpf/progs/test_select_reuseport_kern.c | 67 +++++++----------- .../selftests/bpf/progs/test_send_signal_kern.c | 26 +++---- .../selftests/bpf/progs/test_sock_fields_kern.c | 78 ++++++++------------- tools/testing/selftests/bpf/progs/test_spin_lock.c | 36 ++++------ .../selftests/bpf/progs/test_stacktrace_build_id.c | 55 ++++++--------- .../selftests/bpf/progs/test_stacktrace_map.c | 52 +++++--------- .../testing/selftests/bpf/progs/test_tcp_estats.c | 13 ++-- .../testing/selftests/bpf/progs/test_tcpbpf_kern.c | 26 +++---- .../selftests/bpf/progs/test_tcpnotify_kern.c | 28 +++----- tools/testing/selftests/bpf/progs/test_xdp.c | 26 +++---- .../selftests/bpf/progs/test_xdp_noinline.c | 81 +++++++++------------- 20 files changed, 300 insertions(+), 494 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/progs/bpf_flow.c b/tools/testing/selftests/bpf/progs/bpf_flow.c index 849f42e548b5..5ae485a6af3f 100644 --- a/tools/testing/selftests/bpf/progs/bpf_flow.c +++ b/tools/testing/selftests/bpf/progs/bpf_flow.c @@ -58,26 +58,18 @@ struct frag_hdr { }; struct { - __u32 type; - __u32 max_entries; - __u32 key_size; - __u32 value_size; -} jmp_table SEC(".maps") = { - .type = BPF_MAP_TYPE_PROG_ARRAY, - .max_entries = 8, - .key_size = sizeof(__u32), - .value_size = sizeof(__u32), -}; + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 8); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} jmp_table SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct bpf_flow_keys *value; -} last_dissection SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct bpf_flow_keys); +} last_dissection SEC(".maps"); static __always_inline int export_flow_keys(struct bpf_flow_keys *keys, int ret) diff --git a/tools/testing/selftests/bpf/progs/netcnt_prog.c b/tools/testing/selftests/bpf/progs/netcnt_prog.c index a25c82a5b7c8..38a997852cad 100644 --- a/tools/testing/selftests/bpf/progs/netcnt_prog.c +++ b/tools/testing/selftests/bpf/progs/netcnt_prog.c @@ -11,20 +11,16 @@ #define NS_PER_SEC 1000000000 struct { - __u32 type; - struct bpf_cgroup_storage_key *key; - struct percpu_net_cnt *value; -} percpu_netcnt SEC(".maps") = { - .type = BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, -}; + __uint(type, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE); + __type(key, struct bpf_cgroup_storage_key); + __type(value, struct percpu_net_cnt); +} percpu_netcnt SEC(".maps"); struct { - __u32 type; - struct bpf_cgroup_storage_key *key; - struct net_cnt *value; -} netcnt SEC(".maps") = { - .type = BPF_MAP_TYPE_CGROUP_STORAGE, -}; + __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE); + __type(key, struct bpf_cgroup_storage_key); + __type(value, struct net_cnt); +} netcnt SEC(".maps"); SEC("cgroup/skb") int bpf_nextcnt(struct __sk_buff *skb) diff --git a/tools/testing/selftests/bpf/progs/socket_cookie_prog.c b/tools/testing/selftests/bpf/progs/socket_cookie_prog.c index 6aabb681fb9a..e4440fdd94cb 100644 --- a/tools/testing/selftests/bpf/progs/socket_cookie_prog.c +++ b/tools/testing/selftests/bpf/progs/socket_cookie_prog.c @@ -13,14 +13,11 @@ struct socket_cookie { }; struct { - __u32 type; - __u32 map_flags; - int *key; - struct socket_cookie *value; -} socket_cookies SEC(".maps") = { - .type = BPF_MAP_TYPE_SK_STORAGE, - .map_flags = BPF_F_NO_PREALLOC, -}; + __uint(type, BPF_MAP_TYPE_SK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, struct socket_cookie); +} socket_cookies SEC(".maps"); SEC("cgroup/connect6") int set_cookie(struct bpf_sock_addr *ctx) diff --git a/tools/testing/selftests/bpf/progs/test_btf_newkv.c b/tools/testing/selftests/bpf/progs/test_btf_newkv.c index 28c16bb583b6..5ee3622ddebb 100644 --- a/tools/testing/selftests/bpf/progs/test_btf_newkv.c +++ b/tools/testing/selftests/bpf/progs/test_btf_newkv.c @@ -21,14 +21,11 @@ struct bpf_map_def SEC("maps") btf_map_legacy = { BPF_ANNOTATE_KV_PAIR(btf_map_legacy, int, struct ipv_counts); struct { - int *key; - struct ipv_counts *value; - unsigned int type; - unsigned int max_entries; -} btf_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 4, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 4); + __type(key, int); + __type(value, struct ipv_counts); +} btf_map SEC(".maps"); struct dummy_tracepoint_args { unsigned long long pad; diff --git a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c index aaa6ec250e15..d06b47a09097 100644 --- a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c +++ b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c @@ -16,26 +16,18 @@ struct stack_trace_t { }; struct { - __u32 type; - __u32 max_entries; - __u32 key_size; - __u32 value_size; -} perfmap SEC(".maps") = { - .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, - .max_entries = 2, - .key_size = sizeof(int), - .value_size = sizeof(__u32), -}; + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(max_entries, 2); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(__u32)); +} perfmap SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct stack_trace_t *value; -} stackdata_map SEC(".maps") = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct stack_trace_t); +} stackdata_map SEC(".maps"); /* Allocate per-cpu space twice the needed. For the code below * usize = bpf_get_stack(ctx, raw_data, max_len, BPF_F_USER_STACK); @@ -56,14 +48,11 @@ struct { * This is an acceptable workaround since there is one entry here. */ struct { - __u32 type; - __u32 max_entries; - __u32 *key; + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); __u64 (*value)[2 * MAX_STACK_RAWTP]; -} rawdata_map SEC(".maps") = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .max_entries = 1, -}; +} rawdata_map SEC(".maps"); SEC("tracepoint/raw_syscalls/sys_enter") int bpf_prog1(void *ctx) diff --git a/tools/testing/selftests/bpf/progs/test_global_data.c b/tools/testing/selftests/bpf/progs/test_global_data.c index 866cc7ddbe43..32a6073acb99 100644 --- a/tools/testing/selftests/bpf/progs/test_global_data.c +++ b/tools/testing/selftests/bpf/progs/test_global_data.c @@ -8,24 +8,18 @@ #include "bpf_helpers.h" struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u64 *value; -} result_number SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 11, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 11); + __type(key, __u32); + __type(value, __u64); +} result_number SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 5); + __type(key, __u32); const char (*value)[32]; -} result_string SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 5, -}; +} result_string SEC(".maps"); struct foo { __u8 a; @@ -34,14 +28,11 @@ struct foo { }; struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct foo *value; -} result_struct SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 5, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 5); + __type(key, __u32); + __type(value, struct foo); +} result_struct SEC(".maps"); /* Relocation tests for __u64s. */ static __u64 num0; diff --git a/tools/testing/selftests/bpf/progs/test_l4lb.c b/tools/testing/selftests/bpf/progs/test_l4lb.c index 848cbb90f581..1d652ee8e73d 100644 --- a/tools/testing/selftests/bpf/progs/test_l4lb.c +++ b/tools/testing/selftests/bpf/progs/test_l4lb.c @@ -170,54 +170,39 @@ struct eth_hdr { }; struct { - __u32 type; - __u32 max_entries; - struct vip *key; - struct vip_meta *value; -} vip_map SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = MAX_VIPS, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, MAX_VIPS); + __type(key, struct vip); + __type(value, struct vip_meta); +} vip_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} ch_rings SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = CH_RINGS_SIZE, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, CH_RINGS_SIZE); + __type(key, __u32); + __type(value, __u32); +} ch_rings SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct real_definition *value; -} reals SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = MAX_REALS, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, MAX_REALS); + __type(key, __u32); + __type(value, struct real_definition); +} reals SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct vip_stats *value; -} stats SEC(".maps") = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .max_entries = MAX_VIPS, -}; + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, MAX_VIPS); + __type(key, __u32); + __type(value, struct vip_stats); +} stats SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct ctl_value *value; -} ctl_array SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = CTL_MAP_SIZE, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, CTL_MAP_SIZE); + __type(key, __u32); + __type(value, struct ctl_value); +} ctl_array SEC(".maps"); static __always_inline __u32 get_packet_hash(struct packet_description *pckt, bool ipv6) diff --git a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c index c63ecf3ca573..2e4efe70b1e5 100644 --- a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c +++ b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c @@ -166,54 +166,39 @@ struct eth_hdr { }; struct { - __u32 type; - __u32 max_entries; - struct vip *key; - struct vip_meta *value; -} vip_map SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = MAX_VIPS, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, MAX_VIPS); + __type(key, struct vip); + __type(value, struct vip_meta); +} vip_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} ch_rings SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = CH_RINGS_SIZE, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, CH_RINGS_SIZE); + __type(key, __u32); + __type(value, __u32); +} ch_rings SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct real_definition *value; -} reals SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = MAX_REALS, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, MAX_REALS); + __type(key, __u32); + __type(value, struct real_definition); +} reals SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct vip_stats *value; -} stats SEC(".maps") = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .max_entries = MAX_VIPS, -}; + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, MAX_VIPS); + __type(key, __u32); + __type(value, struct vip_stats); +} stats SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct ctl_value *value; -} ctl_array SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = CTL_MAP_SIZE, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, CTL_MAP_SIZE); + __type(key, __u32); + __type(value, struct ctl_value); +} ctl_array SEC(".maps"); static __u32 get_packet_hash(struct packet_description *pckt, bool ipv6) diff --git a/tools/testing/selftests/bpf/progs/test_map_lock.c b/tools/testing/selftests/bpf/progs/test_map_lock.c index 40d9c2853393..bb7ce35f691b 100644 --- a/tools/testing/selftests/bpf/progs/test_map_lock.c +++ b/tools/testing/selftests/bpf/progs/test_map_lock.c @@ -12,14 +12,11 @@ struct hmap_elem { }; struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct hmap_elem *value; -} hash_map SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct hmap_elem); +} hash_map SEC(".maps"); struct array_elem { struct bpf_spin_lock lock; @@ -27,14 +24,11 @@ struct array_elem { }; struct { - __u32 type; - __u32 max_entries; - int *key; - struct array_elem *value; -} array_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct array_elem); +} array_map SEC(".maps"); SEC("map_lock_demo") int bpf_map_lock_test(struct __sk_buff *skb) diff --git a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c index 435a9527733e..ea7d84f01235 100644 --- a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c +++ b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c @@ -22,56 +22,39 @@ int _version SEC("version") = 1; #endif struct { - __u32 type; - __u32 max_entries; - __u32 key_size; - __u32 value_size; -} outer_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, - .max_entries = 1, - .key_size = sizeof(__u32), - .value_size = sizeof(__u32), -}; + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} outer_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} result_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = NR_RESULTS, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, NR_RESULTS); + __type(key, __u32); + __type(value, __u32); +} result_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - int *value; -} tmp_index_ovr_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, int); +} tmp_index_ovr_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} linum_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u32); +} linum_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct data_check *value; -} data_check_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct data_check); +} data_check_map SEC(".maps"); #define GOTO_DONE(_result) ({ \ result = (_result); \ diff --git a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c index 6ac68be5d68b..0e6be01157e6 100644 --- a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c +++ b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c @@ -5,24 +5,18 @@ #include "bpf_helpers.h" struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u64 *value; -} info_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} info_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u64 *value; -} status_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} status_map SEC(".maps"); SEC("send_signal_demo") int bpf_send_signal_test(void *ctx) diff --git a/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c b/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c index c3d383d650cb..a47b003623ef 100644 --- a/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c +++ b/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c @@ -28,44 +28,32 @@ enum bpf_linum_array_idx { }; struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct sockaddr_in6 *value; -} addr_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = __NR_BPF_ADDR_ARRAY_IDX, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, __NR_BPF_ADDR_ARRAY_IDX); + __type(key, __u32); + __type(value, struct sockaddr_in6); +} addr_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct bpf_sock *value; -} sock_result_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = __NR_BPF_RESULT_ARRAY_IDX, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, __NR_BPF_RESULT_ARRAY_IDX); + __type(key, __u32); + __type(value, struct bpf_sock); +} sock_result_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct bpf_tcp_sock *value; -} tcp_sock_result_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = __NR_BPF_RESULT_ARRAY_IDX, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, __NR_BPF_RESULT_ARRAY_IDX); + __type(key, __u32); + __type(value, struct bpf_tcp_sock); +} tcp_sock_result_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} linum_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = __NR_BPF_LINUM_ARRAY_IDX, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, __NR_BPF_LINUM_ARRAY_IDX); + __type(key, __u32); + __type(value, __u32); +} linum_map SEC(".maps"); struct bpf_spinlock_cnt { struct bpf_spin_lock lock; @@ -73,24 +61,18 @@ struct bpf_spinlock_cnt { }; struct { - __u32 type; - __u32 map_flags; - int *key; - struct bpf_spinlock_cnt *value; -} sk_pkt_out_cnt SEC(".maps") = { - .type = BPF_MAP_TYPE_SK_STORAGE, - .map_flags = BPF_F_NO_PREALLOC, -}; + __uint(type, BPF_MAP_TYPE_SK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, struct bpf_spinlock_cnt); +} sk_pkt_out_cnt SEC(".maps"); struct { - __u32 type; - __u32 map_flags; - int *key; - struct bpf_spinlock_cnt *value; -} sk_pkt_out_cnt10 SEC(".maps") = { - .type = BPF_MAP_TYPE_SK_STORAGE, - .map_flags = BPF_F_NO_PREALLOC, -}; + __uint(type, BPF_MAP_TYPE_SK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, struct bpf_spinlock_cnt); +} sk_pkt_out_cnt10 SEC(".maps"); static bool is_loopback6(__u32 *a6) { diff --git a/tools/testing/selftests/bpf/progs/test_spin_lock.c b/tools/testing/selftests/bpf/progs/test_spin_lock.c index 0a77ae36d981..a43b999c8da2 100644 --- a/tools/testing/selftests/bpf/progs/test_spin_lock.c +++ b/tools/testing/selftests/bpf/progs/test_spin_lock.c @@ -11,14 +11,11 @@ struct hmap_elem { }; struct { - __u32 type; - __u32 max_entries; - int *key; - struct hmap_elem *value; -} hmap SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct hmap_elem); +} hmap SEC(".maps"); struct cls_elem { struct bpf_spin_lock lock; @@ -26,12 +23,10 @@ struct cls_elem { }; struct { - __u32 type; - struct bpf_cgroup_storage_key *key; - struct cls_elem *value; -} cls_map SEC(".maps") = { - .type = BPF_MAP_TYPE_CGROUP_STORAGE, -}; + __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE); + __type(key, struct bpf_cgroup_storage_key); + __type(value, struct cls_elem); +} cls_map SEC(".maps"); struct bpf_vqueue { struct bpf_spin_lock lock; @@ -42,14 +37,11 @@ struct bpf_vqueue { }; struct { - __u32 type; - __u32 max_entries; - int *key; - struct bpf_vqueue *value; -} vqueue SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct bpf_vqueue); +} vqueue SEC(".maps"); #define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20) diff --git a/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c b/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c index fcf2280bb60c..bbfc8337b6f0 100644 --- a/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c +++ b/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c @@ -9,51 +9,36 @@ #endif struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} control_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u32); +} control_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} stackid_hmap SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = 16384, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 16384); + __type(key, __u32); + __type(value, __u32); +} stackid_hmap SEC(".maps"); typedef struct bpf_stack_build_id stack_trace_t[PERF_MAX_STACK_DEPTH]; struct { - __u32 type; - __u32 max_entries; - __u32 map_flags; - __u32 key_size; - __u32 value_size; -} stackmap SEC(".maps") = { - .type = BPF_MAP_TYPE_STACK_TRACE, - .max_entries = 128, - .map_flags = BPF_F_STACK_BUILD_ID, - .key_size = sizeof(__u32), - .value_size = sizeof(stack_trace_t), -}; + __uint(type, BPF_MAP_TYPE_STACK_TRACE); + __uint(max_entries, 128); + __uint(map_flags, BPF_F_STACK_BUILD_ID); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(stack_trace_t)); +} stackmap SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 128); + __type(key, __u32); /* there seems to be a bug in kernel not handling typedef properly */ struct bpf_stack_build_id (*value)[PERF_MAX_STACK_DEPTH]; -} stack_amap SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 128, -}; +} stack_amap SEC(".maps"); /* taken from /sys/kernel/debug/tracing/events/random/urandom_read/format */ struct random_urandom_args { diff --git a/tools/testing/selftests/bpf/progs/test_stacktrace_map.c b/tools/testing/selftests/bpf/progs/test_stacktrace_map.c index 7ad09adbf648..803c15dc109d 100644 --- a/tools/testing/selftests/bpf/progs/test_stacktrace_map.c +++ b/tools/testing/selftests/bpf/progs/test_stacktrace_map.c @@ -9,48 +9,34 @@ #endif struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} control_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 1, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u32); +} control_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} stackid_hmap SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = 16384, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 16384); + __type(key, __u32); + __type(value, __u32); +} stackid_hmap SEC(".maps"); typedef __u64 stack_trace_t[PERF_MAX_STACK_DEPTH]; struct { - __u32 type; - __u32 max_entries; - __u32 key_size; - __u32 value_size; -} stackmap SEC(".maps") = { - .type = BPF_MAP_TYPE_STACK_TRACE, - .max_entries = 16384, - .key_size = sizeof(__u32), - .value_size = sizeof(stack_trace_t), -}; + __uint(type, BPF_MAP_TYPE_STACK_TRACE); + __uint(max_entries, 16384); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(stack_trace_t)); +} stackmap SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 16384); + __type(key, __u32); __u64 (*value)[PERF_MAX_STACK_DEPTH]; -} stack_amap SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 16384, -}; +} stack_amap SEC(".maps"); /* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */ struct sched_switch_args { diff --git a/tools/testing/selftests/bpf/progs/test_tcp_estats.c b/tools/testing/selftests/bpf/progs/test_tcp_estats.c index df98f7e32832..c8c595da38d4 100644 --- a/tools/testing/selftests/bpf/progs/test_tcp_estats.c +++ b/tools/testing/selftests/bpf/progs/test_tcp_estats.c @@ -149,14 +149,11 @@ struct tcp_estats_basic_event { }; struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct tcp_estats_basic_event *value; -} ev_record_map SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = 1024, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u32); + __type(value, struct tcp_estats_basic_event); +} ev_record_map SEC(".maps"); struct dummy_tracepoint_args { unsigned long long pad; diff --git a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c index 38e10c9fd996..2e233613d1fc 100644 --- a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c +++ b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c @@ -15,24 +15,18 @@ #include "test_tcpbpf.h" struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct tcpbpf_globals *value; -} global_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 4, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 4); + __type(key, __u32); + __type(value, struct tcpbpf_globals); +} global_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - int *value; -} sockopt_results SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 2, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 2); + __type(key, __u32); + __type(value, int); +} sockopt_results SEC(".maps"); static inline void update_event_map(int event) { diff --git a/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c b/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c index d073d37d4e27..08346e7765d5 100644 --- a/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c +++ b/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c @@ -15,26 +15,18 @@ #include "test_tcpnotify.h" struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct tcpnotify_globals *value; -} global_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 4, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 4); + __type(key, __u32); + __type(value, struct tcpnotify_globals); +} global_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 key_size; - __u32 value_size; -} perf_event_map SEC(".maps") = { - .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, - .max_entries = 2, - .key_size = sizeof(int), - .value_size = sizeof(__u32), -}; + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(max_entries, 2); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(__u32)); +} perf_event_map SEC(".maps"); int _version SEC("version") = 1; diff --git a/tools/testing/selftests/bpf/progs/test_xdp.c b/tools/testing/selftests/bpf/progs/test_xdp.c index ec3d2c1c8cf9..0941c655b07b 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp.c +++ b/tools/testing/selftests/bpf/progs/test_xdp.c @@ -23,24 +23,18 @@ int _version SEC("version") = 1; struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u64 *value; -} rxcnt SEC(".maps") = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .max_entries = 256, -}; + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 256); + __type(key, __u32); + __type(value, __u64); +} rxcnt SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - struct vip *key; - struct iptnl_info *value; -} vip2tnl SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = MAX_IPTNL_ENTRIES, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, MAX_IPTNL_ENTRIES); + __type(key, struct vip); + __type(value, struct iptnl_info); +} vip2tnl SEC(".maps"); static __always_inline void count_tx(__u32 protocol) { diff --git a/tools/testing/selftests/bpf/progs/test_xdp_noinline.c b/tools/testing/selftests/bpf/progs/test_xdp_noinline.c index d2eddb5553d1..dad8a7e33eaa 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_noinline.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_noinline.c @@ -164,66 +164,47 @@ struct lb_stats { }; struct { - __u32 type; - __u32 max_entries; - struct vip_definition *key; - struct vip_meta *value; -} vip_map SEC(".maps") = { - .type = BPF_MAP_TYPE_HASH, - .max_entries = 512, -}; + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 512); + __type(key, struct vip_definition); + __type(value, struct vip_meta); +} vip_map SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 map_flags; - struct flow_key *key; - struct real_pos_lru *value; -} lru_cache SEC(".maps") = { - .type = BPF_MAP_TYPE_LRU_HASH, - .max_entries = 300, - .map_flags = 1U << 1, -}; + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 300); + __uint(map_flags, 1U << 1); + __type(key, struct flow_key); + __type(value, struct real_pos_lru); +} lru_cache SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - __u32 *value; -} ch_rings SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 12 * 655, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 12 * 655); + __type(key, __u32); + __type(value, __u32); +} ch_rings SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct real_definition *value; -} reals SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 40, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 40); + __type(key, __u32); + __type(value, struct real_definition); +} reals SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct lb_stats *value; -} stats SEC(".maps") = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .max_entries = 515, -}; + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 515); + __type(key, __u32); + __type(value, struct lb_stats); +} stats SEC(".maps"); struct { - __u32 type; - __u32 max_entries; - __u32 *key; - struct ctl_value *value; -} ctl_array SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 16, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 16); + __type(key, __u32); + __type(value, struct ctl_value); +} ctl_array SEC(".maps"); struct eth_hdr { unsigned char eth_dest[6]; -- cgit v1.2.3-59-g8ed1b From 1639b17c72fa7ad977ccd0ad6c673e3f7048723b Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 5 Jul 2019 08:50:12 -0700 Subject: selftests/bpf: convert legacy BPF maps to BTF-defined ones Convert selftests that were originally left out and new ones added recently to consistently use BTF-defined maps. Reported-by: kernel test robot Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- .../selftests/bpf/progs/get_cgroup_id_kern.c | 26 ++--- tools/testing/selftests/bpf/progs/pyperf.h | 90 ++++++++--------- .../selftests/bpf/progs/sockmap_verdict_prog.c | 48 ++++----- tools/testing/selftests/bpf/progs/strobemeta.h | 68 ++++++------- .../testing/selftests/bpf/progs/test_map_in_map.c | 30 +++--- tools/testing/selftests/bpf/progs/test_obj_id.c | 12 +-- tools/testing/selftests/bpf/progs/test_xdp_loop.c | 26 ++--- .../testing/selftests/bpf/progs/xdp_redirect_map.c | 12 +-- tools/testing/selftests/bpf/progs/xdping_kern.c | 12 +-- tools/testing/selftests/bpf/test_queue_stack_map.h | 30 +++--- tools/testing/selftests/bpf/test_sockmap_kern.h | 110 ++++++++++----------- 11 files changed, 228 insertions(+), 236 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c b/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c index 014dba10b8a5..16c54ade6888 100644 --- a/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c +++ b/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c @@ -4,19 +4,19 @@ #include #include "bpf_helpers.h" -struct bpf_map_def SEC("maps") cg_ids = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(__u64), - .max_entries = 1, -}; - -struct bpf_map_def SEC("maps") pidmap = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(__u32), - .max_entries = 1, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} cg_ids SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u32); +} pidmap SEC(".maps"); SEC("tracepoint/syscalls/sys_enter_nanosleep") int trace(void *ctx) diff --git a/tools/testing/selftests/bpf/progs/pyperf.h b/tools/testing/selftests/bpf/progs/pyperf.h index abf6224649be..003fe106fc70 100644 --- a/tools/testing/selftests/bpf/progs/pyperf.h +++ b/tools/testing/selftests/bpf/progs/pyperf.h @@ -58,14 +58,6 @@ typedef struct { } Event; -struct bpf_elf_map { - __u32 type; - __u32 size_key; - __u32 size_value; - __u32 max_elem; - __u32 flags; -}; - typedef int pid_t; typedef struct { @@ -118,47 +110,47 @@ static __always_inline bool get_frame_data(void *frame_ptr, PidData *pidData, return true; } -struct bpf_elf_map SEC("maps") pidmap = { - .type = BPF_MAP_TYPE_HASH, - .size_key = sizeof(int), - .size_value = sizeof(PidData), - .max_elem = 1, -}; - -struct bpf_elf_map SEC("maps") eventmap = { - .type = BPF_MAP_TYPE_HASH, - .size_key = sizeof(int), - .size_value = sizeof(Event), - .max_elem = 1, -}; - -struct bpf_elf_map SEC("maps") symbolmap = { - .type = BPF_MAP_TYPE_HASH, - .size_key = sizeof(Symbol), - .size_value = sizeof(int), - .max_elem = 1, -}; - -struct bpf_elf_map SEC("maps") statsmap = { - .type = BPF_MAP_TYPE_ARRAY, - .size_key = sizeof(Stats), - .size_value = sizeof(int), - .max_elem = 1, -}; - -struct bpf_elf_map SEC("maps") perfmap = { - .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, - .size_key = sizeof(int), - .size_value = sizeof(int), - .max_elem = 32, -}; - -struct bpf_elf_map SEC("maps") stackmap = { - .type = BPF_MAP_TYPE_STACK_TRACE, - .size_key = sizeof(int), - .size_value = sizeof(long long) * 127, - .max_elem = 1000, -}; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, PidData); +} pidmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, Event); +} eventmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, Symbol); + __type(value, int); +} symbolmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, Stats); +} statsmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(max_entries, 32); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} perfmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_STACK_TRACE); + __uint(max_entries, 1000); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(long long) * 127); +} stackmap SEC(".maps"); static __always_inline int __on_event(struct pt_regs *ctx) { diff --git a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c index d85c874ef25e..433e23918a62 100644 --- a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c +++ b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c @@ -4,33 +4,33 @@ int _version SEC("version") = 1; -struct bpf_map_def SEC("maps") sock_map_rx = { - .type = BPF_MAP_TYPE_SOCKMAP, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 20, -}; +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 20); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} sock_map_rx SEC(".maps"); -struct bpf_map_def SEC("maps") sock_map_tx = { - .type = BPF_MAP_TYPE_SOCKMAP, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 20, -}; +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 20); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} sock_map_tx SEC(".maps"); -struct bpf_map_def SEC("maps") sock_map_msg = { - .type = BPF_MAP_TYPE_SOCKMAP, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 20, -}; +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 20); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} sock_map_msg SEC(".maps"); -struct bpf_map_def SEC("maps") sock_map_break = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 20, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 20); + __type(key, int); + __type(value, int); +} sock_map_break SEC(".maps"); SEC("sk_skb2") int bpf_prog2(struct __sk_buff *skb) diff --git a/tools/testing/selftests/bpf/progs/strobemeta.h b/tools/testing/selftests/bpf/progs/strobemeta.h index 553bc3b62e89..8a399bdfd920 100644 --- a/tools/testing/selftests/bpf/progs/strobemeta.h +++ b/tools/testing/selftests/bpf/progs/strobemeta.h @@ -204,40 +204,40 @@ struct strobelight_bpf_sample { char dummy_safeguard; }; -struct bpf_map_def SEC("maps") samples = { - .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 32, -}; - -struct bpf_map_def SEC("maps") stacks_0 = { - .type = BPF_MAP_TYPE_STACK_TRACE, - .key_size = sizeof(uint32_t), - .value_size = sizeof(uint64_t) * PERF_MAX_STACK_DEPTH, - .max_entries = 16, -}; - -struct bpf_map_def SEC("maps") stacks_1 = { - .type = BPF_MAP_TYPE_STACK_TRACE, - .key_size = sizeof(uint32_t), - .value_size = sizeof(uint64_t) * PERF_MAX_STACK_DEPTH, - .max_entries = 16, -}; - -struct bpf_map_def SEC("maps") sample_heap = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .key_size = sizeof(uint32_t), - .value_size = sizeof(struct strobelight_bpf_sample), - .max_entries = 1, -}; - -struct bpf_map_def SEC("maps") strobemeta_cfgs = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .key_size = sizeof(pid_t), - .value_size = sizeof(struct strobemeta_cfg), - .max_entries = STROBE_MAX_CFGS, -}; +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(max_entries, 32); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} samples SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_STACK_TRACE); + __uint(max_entries, 16); + __uint(key_size, sizeof(uint32_t)); + __uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH); +} stacks_0 SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_STACK_TRACE); + __uint(max_entries, 16); + __uint(key_size, sizeof(uint32_t)); + __uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH); +} stacks_1 SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, uint32_t); + __type(value, struct strobelight_bpf_sample); +} sample_heap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, STROBE_MAX_CFGS); + __type(key, pid_t); + __type(value, struct strobemeta_cfg); +} strobemeta_cfgs SEC(".maps"); /* Type for the dtv. */ /* https://github.com/lattera/glibc/blob/master/nptl/sysdeps/x86_64/tls.h#L34 */ diff --git a/tools/testing/selftests/bpf/progs/test_map_in_map.c b/tools/testing/selftests/bpf/progs/test_map_in_map.c index 2985f262846e..113226115365 100644 --- a/tools/testing/selftests/bpf/progs/test_map_in_map.c +++ b/tools/testing/selftests/bpf/progs/test_map_in_map.c @@ -5,23 +5,23 @@ #include #include "bpf_helpers.h" -struct bpf_map_def SEC("maps") mim_array = { - .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, - .key_size = sizeof(int), +struct { + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); + __uint(max_entries, 1); + __uint(map_flags, 0); + __uint(key_size, sizeof(__u32)); /* must be sizeof(__u32) for map in map */ - .value_size = sizeof(__u32), - .max_entries = 1, - .map_flags = 0, -}; - -struct bpf_map_def SEC("maps") mim_hash = { - .type = BPF_MAP_TYPE_HASH_OF_MAPS, - .key_size = sizeof(int), + __uint(value_size, sizeof(__u32)); +} mim_array SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); + __uint(max_entries, 1); + __uint(map_flags, 0); + __uint(key_size, sizeof(int)); /* must be sizeof(__u32) for map in map */ - .value_size = sizeof(__u32), - .max_entries = 1, - .map_flags = 0, -}; + __uint(value_size, sizeof(__u32)); +} mim_hash SEC(".maps"); SEC("xdp_mimtest") int xdp_mimtest0(struct xdp_md *ctx) diff --git a/tools/testing/selftests/bpf/progs/test_obj_id.c b/tools/testing/selftests/bpf/progs/test_obj_id.c index 726340fa6fe0..3d30c02bdae9 100644 --- a/tools/testing/selftests/bpf/progs/test_obj_id.c +++ b/tools/testing/selftests/bpf/progs/test_obj_id.c @@ -13,12 +13,12 @@ int _version SEC("version") = 1; -struct bpf_map_def SEC("maps") test_map_id = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(__u64), - .max_entries = 1, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} test_map_id SEC(".maps"); SEC("test_obj_id_dummy") int test_obj_id(struct __sk_buff *skb) diff --git a/tools/testing/selftests/bpf/progs/test_xdp_loop.c b/tools/testing/selftests/bpf/progs/test_xdp_loop.c index 7fa4677df22e..97175f73c3fe 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_loop.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_loop.c @@ -18,19 +18,19 @@ int _version SEC("version") = 1; -struct bpf_map_def SEC("maps") rxcnt = { - .type = BPF_MAP_TYPE_PERCPU_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(__u64), - .max_entries = 256, -}; - -struct bpf_map_def SEC("maps") vip2tnl = { - .type = BPF_MAP_TYPE_HASH, - .key_size = sizeof(struct vip), - .value_size = sizeof(struct iptnl_info), - .max_entries = MAX_IPTNL_ENTRIES, -}; +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 256); + __type(key, __u32); + __type(value, __u64); +} rxcnt SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, MAX_IPTNL_ENTRIES); + __type(key, struct vip); + __type(value, struct iptnl_info); +} vip2tnl SEC(".maps"); static __always_inline void count_tx(__u32 protocol) { diff --git a/tools/testing/selftests/bpf/progs/xdp_redirect_map.c b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c index e87a985b9df9..1c5f298d7196 100644 --- a/tools/testing/selftests/bpf/progs/xdp_redirect_map.c +++ b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c @@ -3,12 +3,12 @@ #include #include "bpf_helpers.h" -struct bpf_map_def SEC("maps") tx_port = { - .type = BPF_MAP_TYPE_DEVMAP, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 8, -}; +struct { + __uint(type, BPF_MAP_TYPE_DEVMAP); + __uint(max_entries, 8); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} tx_port SEC(".maps"); SEC("redirect_map_0") int xdp_redirect_map_0(struct xdp_md *xdp) diff --git a/tools/testing/selftests/bpf/progs/xdping_kern.c b/tools/testing/selftests/bpf/progs/xdping_kern.c index 87393e7c667c..112a2857f4e2 100644 --- a/tools/testing/selftests/bpf/progs/xdping_kern.c +++ b/tools/testing/selftests/bpf/progs/xdping_kern.c @@ -17,12 +17,12 @@ #include "xdping.h" -struct bpf_map_def SEC("maps") ping_map = { - .type = BPF_MAP_TYPE_HASH, - .key_size = sizeof(__u32), - .value_size = sizeof(struct pinginfo), - .max_entries = 256, -}; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256); + __type(key, __u32); + __type(value, struct pinginfo); +} ping_map SEC(".maps"); static __always_inline void swap_src_dst_mac(void *data) { diff --git a/tools/testing/selftests/bpf/test_queue_stack_map.h b/tools/testing/selftests/bpf/test_queue_stack_map.h index 295b9b3bc5c7..0e014d3b2b36 100644 --- a/tools/testing/selftests/bpf/test_queue_stack_map.h +++ b/tools/testing/selftests/bpf/test_queue_stack_map.h @@ -10,21 +10,21 @@ int _version SEC("version") = 1; -struct bpf_map_def __attribute__ ((section("maps"), used)) map_in = { - .type = MAP_TYPE, - .key_size = 0, - .value_size = sizeof(__u32), - .max_entries = 32, - .map_flags = 0, -}; - -struct bpf_map_def __attribute__ ((section("maps"), used)) map_out = { - .type = MAP_TYPE, - .key_size = 0, - .value_size = sizeof(__u32), - .max_entries = 32, - .map_flags = 0, -}; +struct { + __uint(type, MAP_TYPE); + __uint(max_entries, 32); + __uint(map_flags, 0); + __uint(key_size, 0); + __uint(value_size, sizeof(__u32)); +} map_in SEC(".maps"); + +struct { + __uint(type, MAP_TYPE); + __uint(max_entries, 32); + __uint(map_flags, 0); + __uint(key_size, 0); + __uint(value_size, sizeof(__u32)); +} map_out SEC(".maps"); SEC("test") int _test(struct __sk_buff *skb) diff --git a/tools/testing/selftests/bpf/test_sockmap_kern.h b/tools/testing/selftests/bpf/test_sockmap_kern.h index 4e7d3da21357..d008b41b7d8d 100644 --- a/tools/testing/selftests/bpf/test_sockmap_kern.h +++ b/tools/testing/selftests/bpf/test_sockmap_kern.h @@ -28,61 +28,61 @@ * are established and verdicts are decided. */ -struct bpf_map_def SEC("maps") sock_map = { - .type = TEST_MAP_TYPE, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 20, -}; - -struct bpf_map_def SEC("maps") sock_map_txmsg = { - .type = TEST_MAP_TYPE, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 20, -}; - -struct bpf_map_def SEC("maps") sock_map_redir = { - .type = TEST_MAP_TYPE, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 20, -}; - -struct bpf_map_def SEC("maps") sock_apply_bytes = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 1 -}; - -struct bpf_map_def SEC("maps") sock_cork_bytes = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 1 -}; - -struct bpf_map_def SEC("maps") sock_bytes = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 6 -}; - -struct bpf_map_def SEC("maps") sock_redir_flags = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 1 -}; - -struct bpf_map_def SEC("maps") sock_skb_opts = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 1 -}; +struct { + __uint(type, TEST_MAP_TYPE); + __uint(max_entries, 20); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} sock_map SEC(".maps"); + +struct { + __uint(type, TEST_MAP_TYPE); + __uint(max_entries, 20); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} sock_map_txmsg SEC(".maps"); + +struct { + __uint(type, TEST_MAP_TYPE); + __uint(max_entries, 20); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} sock_map_redir SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} sock_apply_bytes SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} sock_cork_bytes SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 6); + __type(key, int); + __type(value, int); +} sock_bytes SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} sock_redir_flags SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} sock_skb_opts SEC(".maps"); SEC("sk_skb1") int bpf_prog1(struct __sk_buff *skb) -- cgit v1.2.3-59-g8ed1b From ba95c7452439756d4f6dceb5a188b7c31dbbe5b6 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Fri, 5 Jul 2019 18:54:33 +0100 Subject: tools: bpftool: add "prog run" subcommand to test-run programs Add a new "bpftool prog run" subcommand to run a loaded program on input data (and possibly with input context) passed by the user. Print output data (and output context if relevant) into a file or into the console. Print return value and duration for the test run into the console. A "repeat" argument can be passed to run the program several times in a row. The command does not perform any kind of verification based on program type (Is this program type allowed to use an input context?) or on data consistency (Can I work with empty input data?), this is left to the kernel. Example invocation: # perl -e 'print "\x0" x 14' | ./bpftool prog run \ pinned /sys/fs/bpf/sample_ret0 \ data_in - data_out - repeat 5 0000000 0000 0000 0000 0000 0000 0000 0000 | ........ ...... Return value: 0, duration (average): 260ns When one of data_in or ctx_in is "-", bpftool reads from standard input, in binary format. Other formats (JSON, hexdump) might be supported (via an optional command line keyword like "data_fmt_in") in the future if relevant, but this would require doing more parsing in bpftool. v2: - Fix argument names for function check_single_stdin(). (Yonghong) Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 34 +++ tools/bpf/bpftool/bash-completion/bpftool | 28 +- tools/bpf/bpftool/main.c | 29 ++ tools/bpf/bpftool/main.h | 1 + tools/bpf/bpftool/prog.c | 348 ++++++++++++++++++++++- tools/include/linux/sizes.h | 48 ++++ 6 files changed, 485 insertions(+), 3 deletions(-) create mode 100644 tools/include/linux/sizes.h (limited to 'tools') diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 1df637f85f94..7a374b3c851d 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -29,6 +29,7 @@ PROG COMMANDS | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog tracelog** +| **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*] | **bpftool** **prog help** | | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } @@ -146,6 +147,39 @@ DESCRIPTION streaming data from BPF programs to user space, one can use perf events (see also **bpftool-map**\ (8)). + **bpftool prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*] + Run BPF program *PROG* in the kernel testing infrastructure + for BPF, meaning that the program works on the data and + context provided by the user, and not on actual packets or + monitored functions etc. Return value and duration for the + test run are printed out to the console. + + Input data is read from the *FILE* passed with **data_in**. + If this *FILE* is "**-**", input data is read from standard + input. Input context, if any, is read from *FILE* passed with + **ctx_in**. Again, "**-**" can be used to read from standard + input, but only if standard input is not already in use for + input data. If a *FILE* is passed with **data_out**, output + data is written to that file. Similarly, output context is + written to the *FILE* passed with **ctx_out**. For both + output flows, "**-**" can be used to print to the standard + output (as plain text, or JSON if relevant option was + passed). If output keywords are omitted, output data and + context are discarded. Keywords **data_size_out** and + **ctx_size_out** are used to pass the size (in bytes) for the + output buffers to the kernel, although the default of 32 kB + should be more than enough for most cases. + + Keyword **repeat** is used to indicate the number of + consecutive runs to perform. Note that output data and + context printed to files correspond to the last of those + runs. The duration printed out at the end of the runs is an + average over all runs performed by the command. + + Not all program types support test run. Among those which do, + not all of them can take the **ctx_in**/**ctx_out** + arguments. bpftool does not perform checks on program types. + **bpftool prog help** Print short help message. diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index ba37095e1f62..965a8658cca3 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -408,10 +408,34 @@ _bpftool() tracelog) return 0 ;; + run) + if [[ ${#words[@]} -lt 5 ]]; then + _filedir + return 0 + fi + case $prev in + id) + _bpftool_get_prog_ids + return 0 + ;; + data_in|data_out|ctx_in|ctx_out) + _filedir + return 0 + ;; + repeat|data_size_out|ctx_size_out) + return 0 + ;; + *) + _bpftool_once_attr 'data_in data_out data_size_out \ + ctx_in ctx_out ctx_size_out repeat' + return 0 + ;; + esac + ;; *) [[ $prev == $object ]] && \ - COMPREPLY=( $( compgen -W 'dump help pin attach detach load \ - show list tracelog' -- "$cur" ) ) + COMPREPLY=( $( compgen -W 'dump help pin attach detach \ + load show list tracelog run' -- "$cur" ) ) ;; esac ;; diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 4879f6395c7e..e916ff25697f 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -117,6 +117,35 @@ bool is_prefix(const char *pfx, const char *str) return !memcmp(str, pfx, strlen(pfx)); } +/* Last argument MUST be NULL pointer */ +int detect_common_prefix(const char *arg, ...) +{ + unsigned int count = 0; + const char *ref; + char msg[256]; + va_list ap; + + snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg); + va_start(ap, arg); + while ((ref = va_arg(ap, const char *))) { + if (!is_prefix(arg, ref)) + continue; + count++; + if (count > 1) + strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1); + strncat(msg, ref, sizeof(msg) - strlen(msg) - 1); + } + va_end(ap); + strncat(msg, "'", sizeof(msg) - strlen(msg) - 1); + + if (count >= 2) { + p_err(msg); + return -1; + } + + return 0; +} + void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep) { unsigned char *data = arg; diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 9c5d9c80f71e..3ef0d9051e10 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -101,6 +101,7 @@ void p_err(const char *fmt, ...); void p_info(const char *fmt, ...); bool is_prefix(const char *pfx, const char *str); +int detect_common_prefix(const char *arg, ...); void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep); void usage(void) __noreturn; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 9b0db5d14e31..66f04a4846a5 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -748,6 +749,344 @@ static int do_detach(int argc, char **argv) return 0; } +static int check_single_stdin(char *file_data_in, char *file_ctx_in) +{ + if (file_data_in && file_ctx_in && + !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) { + p_err("cannot use standard input for both data_in and ctx_in"); + return -1; + } + + return 0; +} + +static int get_run_data(const char *fname, void **data_ptr, unsigned int *size) +{ + size_t block_size = 256; + size_t buf_size = block_size; + size_t nb_read = 0; + void *tmp; + FILE *f; + + if (!fname) { + *data_ptr = NULL; + *size = 0; + return 0; + } + + if (!strcmp(fname, "-")) + f = stdin; + else + f = fopen(fname, "r"); + if (!f) { + p_err("failed to open %s: %s", fname, strerror(errno)); + return -1; + } + + *data_ptr = malloc(block_size); + if (!*data_ptr) { + p_err("failed to allocate memory for data_in/ctx_in: %s", + strerror(errno)); + goto err_fclose; + } + + while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) { + if (feof(f)) + break; + if (ferror(f)) { + p_err("failed to read data_in/ctx_in from %s: %s", + fname, strerror(errno)); + goto err_free; + } + if (nb_read > buf_size - block_size) { + if (buf_size == UINT32_MAX) { + p_err("data_in/ctx_in is too long (max: %d)", + UINT32_MAX); + goto err_free; + } + /* No space for fread()-ing next chunk; realloc() */ + buf_size *= 2; + tmp = realloc(*data_ptr, buf_size); + if (!tmp) { + p_err("failed to reallocate data_in/ctx_in: %s", + strerror(errno)); + goto err_free; + } + *data_ptr = tmp; + } + } + if (f != stdin) + fclose(f); + + *size = nb_read; + return 0; + +err_free: + free(*data_ptr); + *data_ptr = NULL; +err_fclose: + if (f != stdin) + fclose(f); + return -1; +} + +static void hex_print(void *data, unsigned int size, FILE *f) +{ + size_t i, j; + char c; + + for (i = 0; i < size; i += 16) { + /* Row offset */ + fprintf(f, "%07zx\t", i); + + /* Hexadecimal values */ + for (j = i; j < i + 16 && j < size; j++) + fprintf(f, "%02x%s", *(uint8_t *)(data + j), + j % 2 ? " " : ""); + for (; j < i + 16; j++) + fprintf(f, " %s", j % 2 ? " " : ""); + + /* ASCII values (if relevant), '.' otherwise */ + fprintf(f, "| "); + for (j = i; j < i + 16 && j < size; j++) { + c = *(char *)(data + j); + if (c < ' ' || c > '~') + c = '.'; + fprintf(f, "%c%s", c, j == i + 7 ? " " : ""); + } + + fprintf(f, "\n"); + } +} + +static int +print_run_output(void *data, unsigned int size, const char *fname, + const char *json_key) +{ + size_t nb_written; + FILE *f; + + if (!fname) + return 0; + + if (!strcmp(fname, "-")) { + f = stdout; + if (json_output) { + jsonw_name(json_wtr, json_key); + print_data_json(data, size); + } else { + hex_print(data, size, f); + } + return 0; + } + + f = fopen(fname, "w"); + if (!f) { + p_err("failed to open %s: %s", fname, strerror(errno)); + return -1; + } + + nb_written = fwrite(data, 1, size, f); + fclose(f); + if (nb_written != size) { + p_err("failed to write output data/ctx: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int alloc_run_data(void **data_ptr, unsigned int size_out) +{ + *data_ptr = calloc(size_out, 1); + if (!*data_ptr) { + p_err("failed to allocate memory for output data/ctx: %s", + strerror(errno)); + return -1; + } + + return 0; +} + +static int do_run(int argc, char **argv) +{ + char *data_fname_in = NULL, *data_fname_out = NULL; + char *ctx_fname_in = NULL, *ctx_fname_out = NULL; + struct bpf_prog_test_run_attr test_attr = {0}; + const unsigned int default_size = SZ_32K; + void *data_in = NULL, *data_out = NULL; + void *ctx_in = NULL, *ctx_out = NULL; + unsigned int repeat = 1; + int fd, err; + + if (!REQ_ARGS(4)) + return -1; + + fd = prog_parse_fd(&argc, &argv); + if (fd < 0) + return -1; + + while (argc) { + if (detect_common_prefix(*argv, "data_in", "data_out", + "data_size_out", NULL)) + return -1; + if (detect_common_prefix(*argv, "ctx_in", "ctx_out", + "ctx_size_out", NULL)) + return -1; + + if (is_prefix(*argv, "data_in")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + data_fname_in = GET_ARG(); + if (check_single_stdin(data_fname_in, ctx_fname_in)) + return -1; + } else if (is_prefix(*argv, "data_out")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + data_fname_out = GET_ARG(); + } else if (is_prefix(*argv, "data_size_out")) { + char *endptr; + + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + test_attr.data_size_out = strtoul(*argv, &endptr, 0); + if (*endptr) { + p_err("can't parse %s as output data size", + *argv); + return -1; + } + NEXT_ARG(); + } else if (is_prefix(*argv, "ctx_in")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + ctx_fname_in = GET_ARG(); + if (check_single_stdin(data_fname_in, ctx_fname_in)) + return -1; + } else if (is_prefix(*argv, "ctx_out")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + ctx_fname_out = GET_ARG(); + } else if (is_prefix(*argv, "ctx_size_out")) { + char *endptr; + + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + test_attr.ctx_size_out = strtoul(*argv, &endptr, 0); + if (*endptr) { + p_err("can't parse %s as output context size", + *argv); + return -1; + } + NEXT_ARG(); + } else if (is_prefix(*argv, "repeat")) { + char *endptr; + + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + repeat = strtoul(*argv, &endptr, 0); + if (*endptr) { + p_err("can't parse %s as repeat number", + *argv); + return -1; + } + NEXT_ARG(); + } else { + p_err("expected no more arguments, 'data_in', 'data_out', 'data_size_out', 'ctx_in', 'ctx_out', 'ctx_size_out' or 'repeat', got: '%s'?", + *argv); + return -1; + } + } + + err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in); + if (err) + return -1; + + if (data_in) { + if (!test_attr.data_size_out) + test_attr.data_size_out = default_size; + err = alloc_run_data(&data_out, test_attr.data_size_out); + if (err) + goto free_data_in; + } + + err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in); + if (err) + goto free_data_out; + + if (ctx_in) { + if (!test_attr.ctx_size_out) + test_attr.ctx_size_out = default_size; + err = alloc_run_data(&ctx_out, test_attr.ctx_size_out); + if (err) + goto free_ctx_in; + } + + test_attr.prog_fd = fd; + test_attr.repeat = repeat; + test_attr.data_in = data_in; + test_attr.data_out = data_out; + test_attr.ctx_in = ctx_in; + test_attr.ctx_out = ctx_out; + + err = bpf_prog_test_run_xattr(&test_attr); + if (err) { + p_err("failed to run program: %s", strerror(errno)); + goto free_ctx_out; + } + + err = 0; + + if (json_output) + jsonw_start_object(json_wtr); /* root */ + + /* Do not exit on errors occurring when printing output data/context, + * we still want to print return value and duration for program run. + */ + if (test_attr.data_size_out) + err += print_run_output(test_attr.data_out, + test_attr.data_size_out, + data_fname_out, "data_out"); + if (test_attr.ctx_size_out) + err += print_run_output(test_attr.ctx_out, + test_attr.ctx_size_out, + ctx_fname_out, "ctx_out"); + + if (json_output) { + jsonw_uint_field(json_wtr, "retval", test_attr.retval); + jsonw_uint_field(json_wtr, "duration", test_attr.duration); + jsonw_end_object(json_wtr); /* root */ + } else { + fprintf(stdout, "Return value: %u, duration%s: %uns\n", + test_attr.retval, + repeat > 1 ? " (average)" : "", test_attr.duration); + } + +free_ctx_out: + free(ctx_out); +free_ctx_in: + free(ctx_in); +free_data_out: + free(data_out); +free_data_in: + free(data_in); + + return err; +} + static int load_with_options(int argc, char **argv, bool first_prog_only) { struct bpf_object_load_attr load_attr = { 0 }; @@ -1058,6 +1397,11 @@ static int do_help(int argc, char **argv) " [pinmaps MAP_DIR]\n" " %s %s attach PROG ATTACH_TYPE [MAP]\n" " %s %s detach PROG ATTACH_TYPE [MAP]\n" + " %s %s run PROG \\\n" + " data_in FILE \\\n" + " [data_out FILE [data_size_out L]] \\\n" + " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n" + " [repeat N]\n" " %s %s tracelog\n" " %s %s help\n" "\n" @@ -1079,7 +1423,8 @@ static int do_help(int argc, char **argv) "", bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], - bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); + bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], + bin_name, argv[-2]); return 0; } @@ -1095,6 +1440,7 @@ static const struct cmd cmds[] = { { "attach", do_attach }, { "detach", do_detach }, { "tracelog", do_tracelog }, + { "run", do_run }, { 0 } }; diff --git a/tools/include/linux/sizes.h b/tools/include/linux/sizes.h new file mode 100644 index 000000000000..1cbb4c4d016e --- /dev/null +++ b/tools/include/linux/sizes.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * include/linux/sizes.h + */ +#ifndef __LINUX_SIZES_H__ +#define __LINUX_SIZES_H__ + +#include + +#define SZ_1 0x00000001 +#define SZ_2 0x00000002 +#define SZ_4 0x00000004 +#define SZ_8 0x00000008 +#define SZ_16 0x00000010 +#define SZ_32 0x00000020 +#define SZ_64 0x00000040 +#define SZ_128 0x00000080 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_2K 0x00000800 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_32K 0x00008000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + +#define SZ_4G _AC(0x100000000, ULL) + +#endif /* __LINUX_SIZES_H__ */ -- cgit v1.2.3-59-g8ed1b From aa52bcbe0e72fac36b1862db08b9c09c4caefae3 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 5 Jul 2019 14:10:31 +0200 Subject: tools: bpftool: Fix json dump crash on powerpc Michael reported crash with by bpf program in json mode on powerpc: # bpftool prog -p dump jited id 14 [{ "name": "0xd00000000a9aa760", "insns": [{ "pc": "0x0", "operation": "nop", "operands": [null ] },{ "pc": "0x4", "operation": "nop", "operands": [null ] },{ "pc": "0x8", "operation": "mflr", Segmentation fault (core dumped) The code is assuming char pointers in format, which is not always true at least for powerpc. Fixing this by dumping the whole string into buffer based on its format. Please note that libopcodes code does not check return values from fprintf callback, but as per Jakub suggestion returning -1 on allocation failure so we do the best effort to propagate the error. Fixes: 107f041212c1 ("tools: bpftool: add JSON output for `bpftool prog dump jited *` command") Reported-by: Michael Petlan Signed-off-by: Jiri Olsa Reviewed-by: Quentin Monnet Reviewed-by: Jakub Kicinski Signed-off-by: Daniel Borkmann --- tools/bpf/bpftool/jit_disasm.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index 3ef3093560ba..bfed711258ce 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -11,6 +11,8 @@ * Licensed under the GNU General Public License, version 2.0 (GPLv2) */ +#define _GNU_SOURCE +#include #include #include #include @@ -44,11 +46,13 @@ static int fprintf_json(void *out, const char *fmt, ...) char *s; va_start(ap, fmt); + if (vasprintf(&s, fmt, ap) < 0) + return -1; + va_end(ap); + if (!oper_count) { int i; - s = va_arg(ap, char *); - /* Strip trailing spaces */ i = strlen(s) - 1; while (s[i] == ' ') @@ -61,11 +65,10 @@ static int fprintf_json(void *out, const char *fmt, ...) } else if (!strcmp(fmt, ",")) { /* Skip */ } else { - s = va_arg(ap, char *); jsonw_string(json_wtr, s); oper_count++; } - va_end(ap); + free(s); return 0; } -- cgit v1.2.3-59-g8ed1b From d6dbce8fdd2b71a7aafc50b2dc5a67cc449428de Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 3 Jul 2019 14:29:07 -0700 Subject: selftests/bpf: fix test_align liveliness expectations Commit 2589726d12a1 ("bpf: introduce bounded loops") caused a change in the way some registers liveliness is reported in the test_align. Add missing "_w" to a couple of tests. Note, there are no offset changes! Fixes: 2589726d12a1 ("bpf: introduce bounded loops") Signed-off-by: Stanislav Fomichev Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/test_align.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c index 3c789d03b629..0262f7b374f9 100644 --- a/tools/testing/selftests/bpf/test_align.c +++ b/tools/testing/selftests/bpf/test_align.c @@ -180,7 +180,7 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {7, "R0=pkt(id=0,off=8,r=8,imm=0)"}, + {7, "R0_w=pkt(id=0,off=8,r=8,imm=0)"}, {7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, {8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, {9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, @@ -315,7 +315,7 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {8, "R2=pkt(id=0,off=0,r=8,imm=0)"}, + {8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, {8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, /* Offset is added to packet pointer R5, resulting in * known fixed offset, and variable offset from R6. @@ -405,7 +405,7 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {8, "R2=pkt(id=0,off=0,r=8,imm=0)"}, + {8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, {8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, /* Adding 14 makes R6 be (4n+2) */ {9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, @@ -473,12 +473,12 @@ static struct bpf_align_test tests[] = { /* (4n) + 14 == (4n+2). We blow our bounds, because * the add could overflow. */ - {7, "R5=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"}, + {7, "R5_w=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"}, /* Checked s>=0 */ {9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, /* packet pointer + nonnegative (4n+2) */ {11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, - {13, "R4=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, + {13, "R4_w=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, /* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine. * We checked the bounds, but it might have been able * to overflow if the packet pointer started in the @@ -486,7 +486,7 @@ static struct bpf_align_test tests[] = { * So we did not get a 'range' on R6, and the access * attempt will fail. */ - {15, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, + {15, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, } }, { @@ -521,7 +521,7 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {7, "R2=pkt(id=0,off=0,r=8,imm=0)"}, + {7, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, {9, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, /* Adding 14 makes R6 be (4n+2) */ {10, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, @@ -574,7 +574,7 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {7, "R2=pkt(id=0,off=0,r=8,imm=0)"}, + {7, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, {10, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"}, /* Adding 14 makes R6 be (4n+2) */ {11, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"}, -- cgit v1.2.3-59-g8ed1b From c3ec002e633e25f6e0ce6f97346dd6f38afcbd6a Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 3 Jul 2019 13:09:52 -0700 Subject: selftests/bpf: add test_tcp_rtt to .gitignore Forgot to add it in the original patch. Fixes: b55873984dab ("selftests/bpf: test BPF_SOCK_OPS_RTT_CB") Reported-by: Andrii Nakryiko Signed-off-by: Stanislav Fomichev Acked-by: Andrii Nakryiko Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/.gitignore | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index a2f7f79c7908..90f70d2c7c22 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -42,3 +42,4 @@ xdping test_sockopt test_sockopt_sk test_sockopt_multi +test_tcp_rtt -- cgit v1.2.3-59-g8ed1b From fb84b8224655309e7d38f989e426b82a4543f115 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 6 Jul 2019 11:06:24 -0700 Subject: libbpf: add perf buffer API BPF_MAP_TYPE_PERF_EVENT_ARRAY map is often used to send data from BPF program to user space for additional processing. libbpf already has very low-level API to read single CPU perf buffer, bpf_perf_event_read_simple(), but it's hard to use and requires a lot of code to set everything up. This patch adds perf_buffer abstraction on top of it, abstracting setting up and polling per-CPU logic into simple and convenient API, similar to what BCC provides. perf_buffer__new() sets up per-CPU ring buffers and updates corresponding BPF map entries. It accepts two user-provided callbacks: one for handling raw samples and one for get notifications of lost samples due to buffer overflow. perf_buffer__new_raw() is similar, but provides more control over how perf events are set up (by accepting user-provided perf_event_attr), how they are handled (perf_event_header pointer is passed directly to user-provided callback), and on which CPUs ring buffers are created (it's possible to provide a list of CPUs and corresponding map keys to update). This API allows advanced users fuller control. perf_buffer__poll() is used to fetch ring buffer data across all CPUs, utilizing epoll instance. perf_buffer__free() does corresponding clean up and unsets FDs from BPF map. All APIs are not thread-safe. User should ensure proper locking/coordination if used in multi-threaded set up. Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.h | 49 +++++++ tools/lib/bpf/libbpf.map | 4 + 3 files changed, 419 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 2a08eb106221..ae569b50e2e0 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -4354,6 +4356,370 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size, return ret; } +struct perf_buffer; + +struct perf_buffer_params { + struct perf_event_attr *attr; + /* if event_cb is specified, it takes precendence */ + perf_buffer_event_fn event_cb; + /* sample_cb and lost_cb are higher-level common-case callbacks */ + perf_buffer_sample_fn sample_cb; + perf_buffer_lost_fn lost_cb; + void *ctx; + int cpu_cnt; + int *cpus; + int *map_keys; +}; + +struct perf_cpu_buf { + struct perf_buffer *pb; + void *base; /* mmap()'ed memory */ + void *buf; /* for reconstructing segmented data */ + size_t buf_size; + int fd; + int cpu; + int map_key; +}; + +struct perf_buffer { + perf_buffer_event_fn event_cb; + perf_buffer_sample_fn sample_cb; + perf_buffer_lost_fn lost_cb; + void *ctx; /* passed into callbacks */ + + size_t page_size; + size_t mmap_size; + struct perf_cpu_buf **cpu_bufs; + struct epoll_event *events; + int cpu_cnt; + int epoll_fd; /* perf event FD */ + int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */ +}; + +static void perf_buffer__free_cpu_buf(struct perf_buffer *pb, + struct perf_cpu_buf *cpu_buf) +{ + if (!cpu_buf) + return; + if (cpu_buf->base && + munmap(cpu_buf->base, pb->mmap_size + pb->page_size)) + pr_warning("failed to munmap cpu_buf #%d\n", cpu_buf->cpu); + if (cpu_buf->fd >= 0) { + ioctl(cpu_buf->fd, PERF_EVENT_IOC_DISABLE, 0); + close(cpu_buf->fd); + } + free(cpu_buf->buf); + free(cpu_buf); +} + +void perf_buffer__free(struct perf_buffer *pb) +{ + int i; + + if (!pb) + return; + if (pb->cpu_bufs) { + for (i = 0; i < pb->cpu_cnt && pb->cpu_bufs[i]; i++) { + struct perf_cpu_buf *cpu_buf = pb->cpu_bufs[i]; + + bpf_map_delete_elem(pb->map_fd, &cpu_buf->map_key); + perf_buffer__free_cpu_buf(pb, cpu_buf); + } + free(pb->cpu_bufs); + } + if (pb->epoll_fd >= 0) + close(pb->epoll_fd); + free(pb->events); + free(pb); +} + +static struct perf_cpu_buf * +perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr, + int cpu, int map_key) +{ + struct perf_cpu_buf *cpu_buf; + char msg[STRERR_BUFSIZE]; + int err; + + cpu_buf = calloc(1, sizeof(*cpu_buf)); + if (!cpu_buf) + return ERR_PTR(-ENOMEM); + + cpu_buf->pb = pb; + cpu_buf->cpu = cpu; + cpu_buf->map_key = map_key; + + cpu_buf->fd = syscall(__NR_perf_event_open, attr, -1 /* pid */, cpu, + -1, PERF_FLAG_FD_CLOEXEC); + if (cpu_buf->fd < 0) { + err = -errno; + pr_warning("failed to open perf buffer event on cpu #%d: %s\n", + cpu, libbpf_strerror_r(err, msg, sizeof(msg))); + goto error; + } + + cpu_buf->base = mmap(NULL, pb->mmap_size + pb->page_size, + PROT_READ | PROT_WRITE, MAP_SHARED, + cpu_buf->fd, 0); + if (cpu_buf->base == MAP_FAILED) { + cpu_buf->base = NULL; + err = -errno; + pr_warning("failed to mmap perf buffer on cpu #%d: %s\n", + cpu, libbpf_strerror_r(err, msg, sizeof(msg))); + goto error; + } + + if (ioctl(cpu_buf->fd, PERF_EVENT_IOC_ENABLE, 0) < 0) { + err = -errno; + pr_warning("failed to enable perf buffer event on cpu #%d: %s\n", + cpu, libbpf_strerror_r(err, msg, sizeof(msg))); + goto error; + } + + return cpu_buf; + +error: + perf_buffer__free_cpu_buf(pb, cpu_buf); + return (struct perf_cpu_buf *)ERR_PTR(err); +} + +static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, + struct perf_buffer_params *p); + +struct perf_buffer *perf_buffer__new(int map_fd, size_t page_cnt, + const struct perf_buffer_opts *opts) +{ + struct perf_buffer_params p = {}; + struct perf_event_attr attr = { + .config = PERF_COUNT_SW_BPF_OUTPUT, + .type = PERF_TYPE_SOFTWARE, + .sample_type = PERF_SAMPLE_RAW, + .sample_period = 1, + .wakeup_events = 1, + }; + + p.attr = &attr; + p.sample_cb = opts ? opts->sample_cb : NULL; + p.lost_cb = opts ? opts->lost_cb : NULL; + p.ctx = opts ? opts->ctx : NULL; + + return __perf_buffer__new(map_fd, page_cnt, &p); +} + +struct perf_buffer * +perf_buffer__new_raw(int map_fd, size_t page_cnt, + const struct perf_buffer_raw_opts *opts) +{ + struct perf_buffer_params p = {}; + + p.attr = opts->attr; + p.event_cb = opts->event_cb; + p.ctx = opts->ctx; + p.cpu_cnt = opts->cpu_cnt; + p.cpus = opts->cpus; + p.map_keys = opts->map_keys; + + return __perf_buffer__new(map_fd, page_cnt, &p); +} + +static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, + struct perf_buffer_params *p) +{ + struct bpf_map_info map = {}; + char msg[STRERR_BUFSIZE]; + struct perf_buffer *pb; + __u32 map_info_len; + int err, i; + + if (page_cnt & (page_cnt - 1)) { + pr_warning("page count should be power of two, but is %zu\n", + page_cnt); + return ERR_PTR(-EINVAL); + } + + map_info_len = sizeof(map); + err = bpf_obj_get_info_by_fd(map_fd, &map, &map_info_len); + if (err) { + err = -errno; + pr_warning("failed to get map info for map FD %d: %s\n", + map_fd, libbpf_strerror_r(err, msg, sizeof(msg))); + return ERR_PTR(err); + } + + if (map.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { + pr_warning("map '%s' should be BPF_MAP_TYPE_PERF_EVENT_ARRAY\n", + map.name); + return ERR_PTR(-EINVAL); + } + + pb = calloc(1, sizeof(*pb)); + if (!pb) + return ERR_PTR(-ENOMEM); + + pb->event_cb = p->event_cb; + pb->sample_cb = p->sample_cb; + pb->lost_cb = p->lost_cb; + pb->ctx = p->ctx; + + pb->page_size = getpagesize(); + pb->mmap_size = pb->page_size * page_cnt; + pb->map_fd = map_fd; + + pb->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (pb->epoll_fd < 0) { + err = -errno; + pr_warning("failed to create epoll instance: %s\n", + libbpf_strerror_r(err, msg, sizeof(msg))); + goto error; + } + + if (p->cpu_cnt > 0) { + pb->cpu_cnt = p->cpu_cnt; + } else { + pb->cpu_cnt = libbpf_num_possible_cpus(); + if (pb->cpu_cnt < 0) { + err = pb->cpu_cnt; + goto error; + } + if (map.max_entries < pb->cpu_cnt) + pb->cpu_cnt = map.max_entries; + } + + pb->events = calloc(pb->cpu_cnt, sizeof(*pb->events)); + if (!pb->events) { + err = -ENOMEM; + pr_warning("failed to allocate events: out of memory\n"); + goto error; + } + pb->cpu_bufs = calloc(pb->cpu_cnt, sizeof(*pb->cpu_bufs)); + if (!pb->cpu_bufs) { + err = -ENOMEM; + pr_warning("failed to allocate buffers: out of memory\n"); + goto error; + } + + for (i = 0; i < pb->cpu_cnt; i++) { + struct perf_cpu_buf *cpu_buf; + int cpu, map_key; + + cpu = p->cpu_cnt > 0 ? p->cpus[i] : i; + map_key = p->cpu_cnt > 0 ? p->map_keys[i] : i; + + cpu_buf = perf_buffer__open_cpu_buf(pb, p->attr, cpu, map_key); + if (IS_ERR(cpu_buf)) { + err = PTR_ERR(cpu_buf); + goto error; + } + + pb->cpu_bufs[i] = cpu_buf; + + err = bpf_map_update_elem(pb->map_fd, &map_key, + &cpu_buf->fd, 0); + if (err) { + err = -errno; + pr_warning("failed to set cpu #%d, key %d -> perf FD %d: %s\n", + cpu, map_key, cpu_buf->fd, + libbpf_strerror_r(err, msg, sizeof(msg))); + goto error; + } + + pb->events[i].events = EPOLLIN; + pb->events[i].data.ptr = cpu_buf; + if (epoll_ctl(pb->epoll_fd, EPOLL_CTL_ADD, cpu_buf->fd, + &pb->events[i]) < 0) { + err = -errno; + pr_warning("failed to epoll_ctl cpu #%d perf FD %d: %s\n", + cpu, cpu_buf->fd, + libbpf_strerror_r(err, msg, sizeof(msg))); + goto error; + } + } + + return pb; + +error: + if (pb) + perf_buffer__free(pb); + return ERR_PTR(err); +} + +struct perf_sample_raw { + struct perf_event_header header; + uint32_t size; + char data[0]; +}; + +struct perf_sample_lost { + struct perf_event_header header; + uint64_t id; + uint64_t lost; + uint64_t sample_id; +}; + +static enum bpf_perf_event_ret +perf_buffer__process_record(struct perf_event_header *e, void *ctx) +{ + struct perf_cpu_buf *cpu_buf = ctx; + struct perf_buffer *pb = cpu_buf->pb; + void *data = e; + + /* user wants full control over parsing perf event */ + if (pb->event_cb) + return pb->event_cb(pb->ctx, cpu_buf->cpu, e); + + switch (e->type) { + case PERF_RECORD_SAMPLE: { + struct perf_sample_raw *s = data; + + if (pb->sample_cb) + pb->sample_cb(pb->ctx, cpu_buf->cpu, s->data, s->size); + break; + } + case PERF_RECORD_LOST: { + struct perf_sample_lost *s = data; + + if (pb->lost_cb) + pb->lost_cb(pb->ctx, cpu_buf->cpu, s->lost); + break; + } + default: + pr_warning("unknown perf sample type %d\n", e->type); + return LIBBPF_PERF_EVENT_ERROR; + } + return LIBBPF_PERF_EVENT_CONT; +} + +static int perf_buffer__process_records(struct perf_buffer *pb, + struct perf_cpu_buf *cpu_buf) +{ + enum bpf_perf_event_ret ret; + + ret = bpf_perf_event_read_simple(cpu_buf->base, pb->mmap_size, + pb->page_size, &cpu_buf->buf, + &cpu_buf->buf_size, + perf_buffer__process_record, cpu_buf); + if (ret != LIBBPF_PERF_EVENT_CONT) + return ret; + return 0; +} + +int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms) +{ + int i, cnt, err; + + cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, timeout_ms); + for (i = 0; i < cnt; i++) { + struct perf_cpu_buf *cpu_buf = pb->events[i].data.ptr; + + err = perf_buffer__process_records(pb, cpu_buf); + if (err) { + pr_warning("error while processing records: %d\n", err); + return err; + } + } + return cnt < 0 ? -errno : cnt; +} + struct bpf_prog_info_array_desc { int array_offset; /* e.g. offset of jited_prog_insns */ int count_offset; /* e.g. offset of jited_prog_len */ diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index f55933784f95..5cbf459ece0b 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -358,6 +358,26 @@ LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type, LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags); LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags); +struct perf_buffer; + +typedef void (*perf_buffer_sample_fn)(void *ctx, int cpu, + void *data, __u32 size); +typedef void (*perf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt); + +/* common use perf buffer options */ +struct perf_buffer_opts { + /* if specified, sample_cb is called for each sample */ + perf_buffer_sample_fn sample_cb; + /* if specified, lost_cb is called for each batch of lost samples */ + perf_buffer_lost_fn lost_cb; + /* ctx is provided to sample_cb and lost_cb */ + void *ctx; +}; + +LIBBPF_API struct perf_buffer * +perf_buffer__new(int map_fd, size_t page_cnt, + const struct perf_buffer_opts *opts); + enum bpf_perf_event_ret { LIBBPF_PERF_EVENT_DONE = 0, LIBBPF_PERF_EVENT_ERROR = -1, @@ -365,6 +385,35 @@ enum bpf_perf_event_ret { }; struct perf_event_header; + +typedef enum bpf_perf_event_ret +(*perf_buffer_event_fn)(void *ctx, int cpu, struct perf_event_header *event); + +/* raw perf buffer options, giving most power and control */ +struct perf_buffer_raw_opts { + /* perf event attrs passed directly into perf_event_open() */ + struct perf_event_attr *attr; + /* raw event callback */ + perf_buffer_event_fn event_cb; + /* ctx is provided to event_cb */ + void *ctx; + /* if cpu_cnt == 0, open all on all possible CPUs (up to the number of + * max_entries of given PERF_EVENT_ARRAY map) + */ + int cpu_cnt; + /* if cpu_cnt > 0, cpus is an array of CPUs to open ring buffers on */ + int *cpus; + /* if cpu_cnt > 0, map_keys specify map keys to set per-CPU FDs for */ + int *map_keys; +}; + +LIBBPF_API struct perf_buffer * +perf_buffer__new_raw(int map_fd, size_t page_cnt, + const struct perf_buffer_raw_opts *opts); + +LIBBPF_API void perf_buffer__free(struct perf_buffer *pb); +LIBBPF_API int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms); + typedef enum bpf_perf_event_ret (*bpf_perf_event_print_t)(struct perf_event_header *hdr, void *private_data); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index e6b7d4edbc93..f9d316e873d8 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -179,4 +179,8 @@ LIBBPF_0.0.4 { btf_dump__new; btf__parse_elf; libbpf_num_possible_cpus; + perf_buffer__free; + perf_buffer__new; + perf_buffer__new_raw; + perf_buffer__poll; } LIBBPF_0.0.3; -- cgit v1.2.3-59-g8ed1b From d7ff34d5fb5a2f6722786863c4afa79c51f8edf2 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 6 Jul 2019 11:06:25 -0700 Subject: libbpf: auto-set PERF_EVENT_ARRAY size to number of CPUs For BPF_MAP_TYPE_PERF_EVENT_ARRAY typically correct size is number of possible CPUs. This is impossible to specify at compilation time. This change adds automatic setting of PERF_EVENT_ARRAY size to number of system CPUs, unless non-zero size is specified explicitly. This allows to adjust size for advanced specific cases, while providing convenient and logical defaults. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/lib/bpf/libbpf.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index ae569b50e2e0..ed07789b3e62 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2116,6 +2116,7 @@ static int bpf_object__create_maps(struct bpf_object *obj) { struct bpf_create_map_attr create_attr = {}; + int nr_cpus = 0; unsigned int i; int err; @@ -2138,7 +2139,22 @@ bpf_object__create_maps(struct bpf_object *obj) create_attr.map_flags = def->map_flags; create_attr.key_size = def->key_size; create_attr.value_size = def->value_size; - create_attr.max_entries = def->max_entries; + if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY && + !def->max_entries) { + if (!nr_cpus) + nr_cpus = libbpf_num_possible_cpus(); + if (nr_cpus < 0) { + pr_warning("failed to determine number of system CPUs: %d\n", + nr_cpus); + err = nr_cpus; + goto err_out; + } + pr_debug("map '%s': setting size to %d\n", + map->name, nr_cpus); + create_attr.max_entries = nr_cpus; + } else { + create_attr.max_entries = def->max_entries; + } create_attr.btf_fd = 0; create_attr.btf_key_type_id = 0; create_attr.btf_value_type_id = 0; @@ -2155,9 +2171,10 @@ bpf_object__create_maps(struct bpf_object *obj) *pfd = bpf_create_map_xattr(&create_attr); if (*pfd < 0 && (create_attr.btf_key_type_id || create_attr.btf_value_type_id)) { - cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); + err = -errno; + cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); pr_warning("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n", - map->name, cp, errno); + map->name, cp, err); create_attr.btf_fd = 0; create_attr.btf_key_type_id = 0; create_attr.btf_value_type_id = 0; @@ -2169,11 +2186,11 @@ bpf_object__create_maps(struct bpf_object *obj) if (*pfd < 0) { size_t j; - err = *pfd; + err = -errno; err_out: - cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); - pr_warning("failed to create map (name: '%s'): %s\n", - map->name, cp); + cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); + pr_warning("failed to create map (name: '%s'): %s(%d)\n", + map->name, cp, err); for (j = 0; j < i; j++) zclose(obj->maps[j].fd); return err; -- cgit v1.2.3-59-g8ed1b From ee5cf82ce04a60d71b0dc219804f6cf1e6ba6d14 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 6 Jul 2019 11:06:26 -0700 Subject: selftests/bpf: test perf buffer API Add test verifying perf buffer API functionality. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- .../testing/selftests/bpf/prog_tests/perf_buffer.c | 100 +++++++++++++++++++++ .../testing/selftests/bpf/progs/test_perf_buffer.c | 25 ++++++ 2 files changed, 125 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/perf_buffer.c create mode 100644 tools/testing/selftests/bpf/progs/test_perf_buffer.c (limited to 'tools') diff --git a/tools/testing/selftests/bpf/prog_tests/perf_buffer.c b/tools/testing/selftests/bpf/prog_tests/perf_buffer.c new file mode 100644 index 000000000000..3f1ef95865ff --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/perf_buffer.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include +#include +#include +#include + +#ifdef __x86_64__ +#define SYS_KPROBE_NAME "__x64_sys_nanosleep" +#else +#define SYS_KPROBE_NAME "sys_nanosleep" +#endif + +static void on_sample(void *ctx, int cpu, void *data, __u32 size) +{ + int cpu_data = *(int *)data, duration = 0; + cpu_set_t *cpu_seen = ctx; + + if (cpu_data != cpu) + CHECK(cpu_data != cpu, "check_cpu_data", + "cpu_data %d != cpu %d\n", cpu_data, cpu); + + CPU_SET(cpu, cpu_seen); +} + +void test_perf_buffer(void) +{ + int err, prog_fd, nr_cpus, i, duration = 0; + const char *prog_name = "kprobe/sys_nanosleep"; + const char *file = "./test_perf_buffer.o"; + struct perf_buffer_opts pb_opts = {}; + struct bpf_map *perf_buf_map; + cpu_set_t cpu_set, cpu_seen; + struct bpf_program *prog; + struct bpf_object *obj; + struct perf_buffer *pb; + struct bpf_link *link; + + nr_cpus = libbpf_num_possible_cpus(); + if (CHECK(nr_cpus < 0, "nr_cpus", "err %d\n", nr_cpus)) + return; + + /* load program */ + err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd); + if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno)) + return; + + prog = bpf_object__find_program_by_title(obj, prog_name); + if (CHECK(!prog, "find_probe", "prog '%s' not found\n", prog_name)) + goto out_close; + + /* load map */ + perf_buf_map = bpf_object__find_map_by_name(obj, "perf_buf_map"); + if (CHECK(!perf_buf_map, "find_perf_buf_map", "not found\n")) + goto out_close; + + /* attach kprobe */ + link = bpf_program__attach_kprobe(prog, false /* retprobe */, + SYS_KPROBE_NAME); + if (CHECK(IS_ERR(link), "attach_kprobe", "err %ld\n", PTR_ERR(link))) + goto out_close; + + /* set up perf buffer */ + pb_opts.sample_cb = on_sample; + pb_opts.ctx = &cpu_seen; + pb = perf_buffer__new(bpf_map__fd(perf_buf_map), 1, &pb_opts); + if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb))) + goto out_detach; + + /* trigger kprobe on every CPU */ + CPU_ZERO(&cpu_seen); + for (i = 0; i < nr_cpus; i++) { + CPU_ZERO(&cpu_set); + CPU_SET(i, &cpu_set); + + err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), + &cpu_set); + if (err && CHECK(err, "set_affinity", "cpu #%d, err %d\n", + i, err)) + goto out_detach; + + usleep(1); + } + + /* read perf buffer */ + err = perf_buffer__poll(pb, 100); + if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err)) + goto out_free_pb; + + if (CHECK(CPU_COUNT(&cpu_seen) != nr_cpus, "seen_cpu_cnt", + "expect %d, seen %d\n", nr_cpus, CPU_COUNT(&cpu_seen))) + goto out_free_pb; + +out_free_pb: + perf_buffer__free(pb); +out_detach: + bpf_link__destroy(link); +out_close: + bpf_object__close(obj); +} diff --git a/tools/testing/selftests/bpf/progs/test_perf_buffer.c b/tools/testing/selftests/bpf/progs/test_perf_buffer.c new file mode 100644 index 000000000000..876c27deb65a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_perf_buffer.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} perf_buf_map SEC(".maps"); + +SEC("kprobe/sys_nanosleep") +int handle_sys_nanosleep_entry(struct pt_regs *ctx) +{ + int cpu = bpf_get_smp_processor_id(); + + bpf_perf_event_output(ctx, &perf_buf_map, BPF_F_CURRENT_CPU, + &cpu, sizeof(cpu)); + return 0; +} + +char _license[] SEC("license") = "GPL"; +__u32 _version SEC("version") = 1; -- cgit v1.2.3-59-g8ed1b From 9b190f185d2f6d65b4c16074db0c4eafa32c7ae1 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 6 Jul 2019 11:06:27 -0700 Subject: tools/bpftool: switch map event_pipe to libbpf's perf_buffer Switch event_pipe implementation to rely on new libbpf perf buffer API (it's raw low-level variant). Signed-off-by: Andrii Nakryiko Acked-by: Jakub Kicinski Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/bpf/bpftool/map_perf_ring.c | 201 ++++++++++++-------------------------- 1 file changed, 64 insertions(+), 137 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/map_perf_ring.c b/tools/bpf/bpftool/map_perf_ring.c index 0507dfaf7a8f..3f108ab17797 100644 --- a/tools/bpf/bpftool/map_perf_ring.c +++ b/tools/bpf/bpftool/map_perf_ring.c @@ -28,7 +28,7 @@ #define MMAP_PAGE_CNT 16 -static bool stop; +static volatile bool stop; struct event_ring_info { int fd; @@ -44,32 +44,44 @@ struct perf_event_sample { unsigned char data[]; }; +struct perf_event_lost { + struct perf_event_header header; + __u64 id; + __u64 lost; +}; + static void int_exit(int signo) { fprintf(stderr, "Stopping...\n"); stop = true; } +struct event_pipe_ctx { + bool all_cpus; + int cpu; + int idx; +}; + static enum bpf_perf_event_ret -print_bpf_output(struct perf_event_header *event, void *private_data) +print_bpf_output(void *private_data, int cpu, struct perf_event_header *event) { - struct perf_event_sample *e = container_of(event, struct perf_event_sample, + struct perf_event_sample *e = container_of(event, + struct perf_event_sample, header); - struct event_ring_info *ring = private_data; - struct { - struct perf_event_header header; - __u64 id; - __u64 lost; - } *lost = (typeof(lost))event; + struct perf_event_lost *lost = container_of(event, + struct perf_event_lost, + header); + struct event_pipe_ctx *ctx = private_data; + int idx = ctx->all_cpus ? cpu : ctx->idx; if (json_output) { jsonw_start_object(json_wtr); jsonw_name(json_wtr, "type"); jsonw_uint(json_wtr, e->header.type); jsonw_name(json_wtr, "cpu"); - jsonw_uint(json_wtr, ring->cpu); + jsonw_uint(json_wtr, cpu); jsonw_name(json_wtr, "index"); - jsonw_uint(json_wtr, ring->key); + jsonw_uint(json_wtr, idx); if (e->header.type == PERF_RECORD_SAMPLE) { jsonw_name(json_wtr, "timestamp"); jsonw_uint(json_wtr, e->time); @@ -89,7 +101,7 @@ print_bpf_output(struct perf_event_header *event, void *private_data) if (e->header.type == PERF_RECORD_SAMPLE) { printf("== @%lld.%09lld CPU: %d index: %d =====\n", e->time / 1000000000ULL, e->time % 1000000000ULL, - ring->cpu, ring->key); + cpu, idx); fprint_hex(stdout, e->data, e->size, " "); printf("\n"); } else if (e->header.type == PERF_RECORD_LOST) { @@ -103,87 +115,25 @@ print_bpf_output(struct perf_event_header *event, void *private_data) return LIBBPF_PERF_EVENT_CONT; } -static void -perf_event_read(struct event_ring_info *ring, void **buf, size_t *buf_len) -{ - enum bpf_perf_event_ret ret; - - ret = bpf_perf_event_read_simple(ring->mem, - MMAP_PAGE_CNT * get_page_size(), - get_page_size(), buf, buf_len, - print_bpf_output, ring); - if (ret != LIBBPF_PERF_EVENT_CONT) { - fprintf(stderr, "perf read loop failed with %d\n", ret); - stop = true; - } -} - -static int perf_mmap_size(void) -{ - return get_page_size() * (MMAP_PAGE_CNT + 1); -} - -static void *perf_event_mmap(int fd) -{ - int mmap_size = perf_mmap_size(); - void *base; - - base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (base == MAP_FAILED) { - p_err("event mmap failed: %s\n", strerror(errno)); - return NULL; - } - - return base; -} - -static void perf_event_unmap(void *mem) -{ - if (munmap(mem, perf_mmap_size())) - fprintf(stderr, "Can't unmap ring memory!\n"); -} - -static int bpf_perf_event_open(int map_fd, int key, int cpu) +int do_event_pipe(int argc, char **argv) { - struct perf_event_attr attr = { + struct perf_event_attr perf_attr = { .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME, .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_BPF_OUTPUT, + .sample_period = 1, + .wakeup_events = 1, }; - int pmu_fd; - - pmu_fd = sys_perf_event_open(&attr, -1, cpu, -1, 0); - if (pmu_fd < 0) { - p_err("failed to open perf event %d for CPU %d", key, cpu); - return -1; - } - - if (bpf_map_update_elem(map_fd, &key, &pmu_fd, BPF_ANY)) { - p_err("failed to update map for event %d for CPU %d", key, cpu); - goto err_close; - } - if (ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) { - p_err("failed to enable event %d for CPU %d", key, cpu); - goto err_close; - } - - return pmu_fd; - -err_close: - close(pmu_fd); - return -1; -} - -int do_event_pipe(int argc, char **argv) -{ - int i, nfds, map_fd, index = -1, cpu = -1; struct bpf_map_info map_info = {}; - struct event_ring_info *rings; - size_t tmp_buf_sz = 0; - void *tmp_buf = NULL; - struct pollfd *pfds; + struct perf_buffer_raw_opts opts = {}; + struct event_pipe_ctx ctx = { + .all_cpus = true, + .cpu = -1, + .idx = -1, + }; + struct perf_buffer *pb; __u32 map_info_len; - bool do_all = true; + int err, map_fd; map_info_len = sizeof(map_info); map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len); @@ -205,7 +155,7 @@ int do_event_pipe(int argc, char **argv) char *endptr; NEXT_ARG(); - cpu = strtoul(*argv, &endptr, 0); + ctx.cpu = strtoul(*argv, &endptr, 0); if (*endptr) { p_err("can't parse %s as CPU ID", **argv); goto err_close_map; @@ -216,7 +166,7 @@ int do_event_pipe(int argc, char **argv) char *endptr; NEXT_ARG(); - index = strtoul(*argv, &endptr, 0); + ctx.idx = strtoul(*argv, &endptr, 0); if (*endptr) { p_err("can't parse %s as index", **argv); goto err_close_map; @@ -228,45 +178,32 @@ int do_event_pipe(int argc, char **argv) goto err_close_map; } - do_all = false; + ctx.all_cpus = false; } - if (!do_all) { - if (index == -1 || cpu == -1) { + if (!ctx.all_cpus) { + if (ctx.idx == -1 || ctx.cpu == -1) { p_err("cpu and index must be specified together"); goto err_close_map; } - - nfds = 1; } else { - nfds = min(get_possible_cpus(), map_info.max_entries); - cpu = 0; - index = 0; + ctx.cpu = 0; + ctx.idx = 0; } - rings = calloc(nfds, sizeof(rings[0])); - if (!rings) + opts.attr = &perf_attr; + opts.event_cb = print_bpf_output; + opts.ctx = &ctx; + opts.cpu_cnt = ctx.all_cpus ? 0 : 1; + opts.cpus = &ctx.cpu; + opts.map_keys = &ctx.idx; + + pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &opts); + err = libbpf_get_error(pb); + if (err) { + p_err("failed to create perf buffer: %s (%d)", + strerror(err), err); goto err_close_map; - - pfds = calloc(nfds, sizeof(pfds[0])); - if (!pfds) - goto err_free_rings; - - for (i = 0; i < nfds; i++) { - rings[i].cpu = cpu + i; - rings[i].key = index + i; - - rings[i].fd = bpf_perf_event_open(map_fd, rings[i].key, - rings[i].cpu); - if (rings[i].fd < 0) - goto err_close_fds_prev; - - rings[i].mem = perf_event_mmap(rings[i].fd); - if (!rings[i].mem) - goto err_close_fds_current; - - pfds[i].fd = rings[i].fd; - pfds[i].events = POLLIN; } signal(SIGINT, int_exit); @@ -277,34 +214,24 @@ int do_event_pipe(int argc, char **argv) jsonw_start_array(json_wtr); while (!stop) { - poll(pfds, nfds, 200); - for (i = 0; i < nfds; i++) - perf_event_read(&rings[i], &tmp_buf, &tmp_buf_sz); + err = perf_buffer__poll(pb, 200); + if (err < 0 && err != -EINTR) { + p_err("perf buffer polling failed: %s (%d)", + strerror(err), err); + goto err_close_pb; + } } - free(tmp_buf); if (json_output) jsonw_end_array(json_wtr); - for (i = 0; i < nfds; i++) { - perf_event_unmap(rings[i].mem); - close(rings[i].fd); - } - free(pfds); - free(rings); + perf_buffer__free(pb); close(map_fd); return 0; -err_close_fds_prev: - while (i--) { - perf_event_unmap(rings[i].mem); -err_close_fds_current: - close(rings[i].fd); - } - free(pfds); -err_free_rings: - free(rings); +err_close_pb: + perf_buffer__free(pb); err_close_map: close(map_fd); return -1; -- cgit v1.2.3-59-g8ed1b From cd07a95f9b4df63ac130b4ee0cf87beca12412ca Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 6 Jul 2019 11:06:28 -0700 Subject: libbpf: add perf_buffer_ prefix to README perf_buffer "object" is part of libbpf API now, add it to the list of libbpf function prefixes. Suggested-by: Daniel Borkman Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/lib/bpf/README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/bpf/README.rst b/tools/lib/bpf/README.rst index cef7b77eab69..8928f7787f2d 100644 --- a/tools/lib/bpf/README.rst +++ b/tools/lib/bpf/README.rst @@ -9,7 +9,8 @@ described here. It's recommended to follow these conventions whenever a new function or type is added to keep libbpf API clean and consistent. All types and functions provided by libbpf API should have one of the -following prefixes: ``bpf_``, ``btf_``, ``libbpf_``, ``xsk_``. +following prefixes: ``bpf_``, ``btf_``, ``libbpf_``, ``xsk_``, +``perf_buffer_``. System call wrappers -------------------- -- cgit v1.2.3-59-g8ed1b From 4cfacbe6df972db4081dd76feb7871fce996118d Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 1 Jul 2019 10:38:40 -0700 Subject: bpf: sync bpf.h to tools/ Sync user_ip6 & msg_src_ip6 comments. Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann --- tools/include/uapi/linux/bpf.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index cecf42c871d4..e692bb21846f 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -3244,7 +3244,7 @@ struct bpf_sock_addr { __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order. */ - __u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write. + __u32 user_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write. * Stored in network byte order. */ __u32 user_port; /* Allows 4-byte read and write. @@ -3253,10 +3253,10 @@ struct bpf_sock_addr { __u32 family; /* Allows 4-byte read, but no write */ __u32 type; /* Allows 4-byte read, but no write */ __u32 protocol; /* Allows 4-byte read, but no write */ - __u32 msg_src_ip4; /* Allows 1,2,4-byte read an 4-byte write. + __u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order. */ - __u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write. + __u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write. * Stored in network byte order. */ __bpf_md_ptr(struct bpf_sock *, sk); -- cgit v1.2.3-59-g8ed1b From 76d950773cd2a365f492ce973429ae629d58131c Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 1 Jul 2019 10:38:41 -0700 Subject: selftests/bpf: add verifier tests for wide stores Make sure that wide stores are allowed at proper (aligned) addresses. Note that user_ip6 is naturally aligned on 8-byte boundary, so correct addresses are user_ip6[0] and user_ip6[2]. msg_src_ip6 is, however, aligned on a 4-byte bondary, so only msg_src_ip6[1] can be wide-stored. Cc: Andrii Nakryiko Cc: Yonghong Song Acked-by: Andrii Nakryiko Signed-off-by: Stanislav Fomichev Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/test_verifier.c | 17 +++++++++-- tools/testing/selftests/bpf/verifier/wide_store.c | 36 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 tools/testing/selftests/bpf/verifier/wide_store.c (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index c5514daf8865..b0773291012a 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -105,6 +105,7 @@ struct bpf_test { __u64 data64[TEST_DATA_LEN / 8]; }; } retvals[MAX_TEST_RUNS]; + enum bpf_attach_type expected_attach_type; }; /* Note we want this to be 64 bit aligned so that the end of our array is @@ -850,6 +851,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv, int fd_prog, expected_ret, alignment_prevented_execution; int prog_len, prog_type = test->prog_type; struct bpf_insn *prog = test->insns; + struct bpf_load_program_attr attr; int run_errs, run_successes; int map_fds[MAX_NR_MAPS]; const char *expected_err; @@ -881,8 +883,17 @@ static void do_test_single(struct bpf_test *test, bool unpriv, pflags |= BPF_F_STRICT_ALIGNMENT; if (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS) pflags |= BPF_F_ANY_ALIGNMENT; - fd_prog = bpf_verify_program(prog_type, prog, prog_len, pflags, - "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 4); + + memset(&attr, 0, sizeof(attr)); + attr.prog_type = prog_type; + attr.expected_attach_type = test->expected_attach_type; + attr.insns = prog; + attr.insns_cnt = prog_len; + attr.license = "GPL"; + attr.log_level = 4; + attr.prog_flags = pflags; + + fd_prog = bpf_load_program_xattr(&attr, bpf_vlog, sizeof(bpf_vlog)); if (fd_prog < 0 && !bpf_probe_prog_type(prog_type, 0)) { printf("SKIP (unsupported program type %d)\n", prog_type); skips++; @@ -912,7 +923,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv, printf("FAIL\nUnexpected success to load!\n"); goto fail_log; } - if (!strstr(bpf_vlog, expected_err)) { + if (!expected_err || !strstr(bpf_vlog, expected_err)) { printf("FAIL\nUnexpected error message!\n\tEXP: %s\n\tRES: %s\n", expected_err, bpf_vlog); goto fail_log; diff --git a/tools/testing/selftests/bpf/verifier/wide_store.c b/tools/testing/selftests/bpf/verifier/wide_store.c new file mode 100644 index 000000000000..8fe99602ded4 --- /dev/null +++ b/tools/testing/selftests/bpf/verifier/wide_store.c @@ -0,0 +1,36 @@ +#define BPF_SOCK_ADDR(field, off, res, err) \ +{ \ + "wide store to bpf_sock_addr." #field "[" #off "]", \ + .insns = { \ + BPF_MOV64_IMM(BPF_REG_0, 1), \ + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, \ + offsetof(struct bpf_sock_addr, field[off])), \ + BPF_EXIT_INSN(), \ + }, \ + .result = res, \ + .prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR, \ + .expected_attach_type = BPF_CGROUP_UDP6_SENDMSG, \ + .errstr = err, \ +} + +/* user_ip6[0] is u64 aligned */ +BPF_SOCK_ADDR(user_ip6, 0, ACCEPT, + NULL), +BPF_SOCK_ADDR(user_ip6, 1, REJECT, + "invalid bpf_context access off=12 size=8"), +BPF_SOCK_ADDR(user_ip6, 2, ACCEPT, + NULL), +BPF_SOCK_ADDR(user_ip6, 3, REJECT, + "invalid bpf_context access off=20 size=8"), + +/* msg_src_ip6[0] is _not_ u64 aligned */ +BPF_SOCK_ADDR(msg_src_ip6, 0, REJECT, + "invalid bpf_context access off=44 size=8"), +BPF_SOCK_ADDR(msg_src_ip6, 1, ACCEPT, + NULL), +BPF_SOCK_ADDR(msg_src_ip6, 2, REJECT, + "invalid bpf_context access off=52 size=8"), +BPF_SOCK_ADDR(msg_src_ip6, 3, REJECT, + "invalid bpf_context access off=56 size=8"), + +#undef BPF_SOCK_ADDR -- cgit v1.2.3-59-g8ed1b From 06ec0e2c490afd2f870d89c59200540fd9acde95 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 5 Jul 2019 21:44:20 -0700 Subject: selftests/bpf: fix test_attach_probe map definition ef99b02b23ef ("libbpf: capture value in BTF type info for BTF-defined map defs") changed BTF-defined maps syntax, while independently merged 1e8611bbdfc9 ("selftests/bpf: add kprobe/uprobe selftests") added new test using outdated syntax of maps. This patch fixes this test after corresponding patch sets were merged. Fixes: ef99b02b23ef ("libbpf: capture value in BTF type info for BTF-defined map defs") Fixes: 1e8611bbdfc9 ("selftests/bpf: add kprobe/uprobe selftests") Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/progs/test_attach_probe.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/progs/test_attach_probe.c b/tools/testing/selftests/bpf/progs/test_attach_probe.c index 7a7c5cd728c8..63a8dfef893b 100644 --- a/tools/testing/selftests/bpf/progs/test_attach_probe.c +++ b/tools/testing/selftests/bpf/progs/test_attach_probe.c @@ -6,14 +6,11 @@ #include "bpf_helpers.h" struct { - int type; - int max_entries; - int *key; - int *value; -} results_map SEC(".maps") = { - .type = BPF_MAP_TYPE_ARRAY, - .max_entries = 4, -}; + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 4); + __type(key, int); + __type(value, int); +} results_map SEC(".maps"); SEC("kprobe/sys_nanosleep") int handle_sys_nanosleep_entry(struct pt_regs *ctx) -- cgit v1.2.3-59-g8ed1b From 8fc9f8bedf1bdaea48382ae2e3dd558e2b939cee Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 8 Jul 2019 14:05:46 +0100 Subject: tools: bpftool: add completion for bpftool prog "loadall" Bash completion for proposing the "loadall" subcommand is missing. Let's add it to the completion script. Add a specific case to propose "load" and "loadall" for completing: $ bpftool prog load ^ cursor is here Otherwise, completion considers that $command is in load|loadall and starts making related completions (file or directory names, as the number of words on the command line is below 6), when the only suggested keywords should be "load" and "loadall" until one has been picked and a space entered after that to move to the next word. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Signed-off-by: Daniel Borkmann --- tools/bpf/bpftool/bash-completion/bpftool | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 965a8658cca3..c8f42e1fcbc9 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -342,6 +342,13 @@ _bpftool() load|loadall) local obj + # Propose "load/loadall" to complete "bpftool prog load", + # or bash tries to complete "load" as a filename below. + if [[ ${#words[@]} -eq 3 ]]; then + COMPREPLY=( $( compgen -W "load loadall" -- "$cur" ) ) + return 0 + fi + if [[ ${#words[@]} -lt 6 ]]; then _filedir return 0 @@ -435,7 +442,7 @@ _bpftool() *) [[ $prev == $object ]] && \ COMPREPLY=( $( compgen -W 'dump help pin attach detach \ - load show list tracelog run' -- "$cur" ) ) + load loadall show list tracelog run' -- "$cur" ) ) ;; esac ;; -- cgit v1.2.3-59-g8ed1b From bc2d8afecbec33bd4549ce1ef904383fde21d385 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 3 Jul 2019 13:50:34 +0200 Subject: selftests/bpf: fix test_reuseport_array on s390 Fix endianness issue: passing a pointer to 64-bit fd as a 32-bit key does not work on big-endian architectures. So cast fd to 32-bits when necessary. Signed-off-by: Ilya Leoshkevich Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/test_maps.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index a3fbc571280a..5443b9bd75ed 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -1418,7 +1418,7 @@ static void test_map_wronly(void) assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM); } -static void prepare_reuseport_grp(int type, int map_fd, +static void prepare_reuseport_grp(int type, int map_fd, size_t map_elem_size, __s64 *fds64, __u64 *sk_cookies, unsigned int n) { @@ -1428,6 +1428,8 @@ static void prepare_reuseport_grp(int type, int map_fd, const int optval = 1; unsigned int i; u64 sk_cookie; + void *value; + __s32 fd32; __s64 fd64; int err; @@ -1449,8 +1451,14 @@ static void prepare_reuseport_grp(int type, int map_fd, "err:%d errno:%d\n", err, errno); /* reuseport_array does not allow unbound sk */ - err = bpf_map_update_elem(map_fd, &index0, &fd64, - BPF_ANY); + if (map_elem_size == sizeof(__u64)) + value = &fd64; + else { + assert(map_elem_size == sizeof(__u32)); + fd32 = (__s32)fd64; + value = &fd32; + } + err = bpf_map_update_elem(map_fd, &index0, value, BPF_ANY); CHECK(err != -1 || errno != EINVAL, "reuseport array update unbound sk", "sock_type:%d err:%d errno:%d\n", @@ -1478,7 +1486,7 @@ static void prepare_reuseport_grp(int type, int map_fd, * reuseport_array does not allow * non-listening tcp sk. */ - err = bpf_map_update_elem(map_fd, &index0, &fd64, + err = bpf_map_update_elem(map_fd, &index0, value, BPF_ANY); CHECK(err != -1 || errno != EINVAL, "reuseport array update non-listening sk", @@ -1541,7 +1549,7 @@ static void test_reuseport_array(void) for (t = 0; t < ARRAY_SIZE(types); t++) { type = types[t]; - prepare_reuseport_grp(type, map_fd, grpa_fds64, + prepare_reuseport_grp(type, map_fd, sizeof(__u64), grpa_fds64, grpa_cookies, ARRAY_SIZE(grpa_fds64)); /* Test BPF_* update flags */ @@ -1649,7 +1657,8 @@ static void test_reuseport_array(void) sizeof(__u32), sizeof(__u32), array_size, 0); CHECK(map_fd == -1, "reuseport array create", "map_fd:%d, errno:%d\n", map_fd, errno); - prepare_reuseport_grp(SOCK_STREAM, map_fd, &fd64, &sk_cookie, 1); + prepare_reuseport_grp(SOCK_STREAM, map_fd, sizeof(__u32), &fd64, + &sk_cookie, 1); fd = fd64; err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST); CHECK(err == -1, "reuseport array update 32 bit fd", -- cgit v1.2.3-59-g8ed1b