// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "perf-sys.h" #include "trace_helpers.h" #define MAX_CPUS 128 static int pmu_fds[MAX_CPUS], if_idx; static struct perf_event_mmap_page *headers[MAX_CPUS]; static char *if_name; static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; static __u32 prog_id; static int do_attach(int idx, int fd, const char *name) { struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); int err; err = bpf_set_link_xdp_fd(idx, fd, xdp_flags); if (err < 0) { printf("ERROR: failed to attach program to %s\n", name); return err; } err = bpf_obj_get_info_by_fd(fd, &info, &info_len); if (err) { printf("can't get prog info - %s\n", strerror(errno)); return err; } prog_id = info.id; return err; } static int do_detach(int idx, const char *name) { __u32 curr_prog_id = 0; int err = 0; err = bpf_get_link_xdp_id(idx, &curr_prog_id, 0); if (err) { printf("bpf_get_link_xdp_id failed\n"); return err; } if (prog_id == curr_prog_id) { err = bpf_set_link_xdp_fd(idx, -1, 0); if (err < 0) printf("ERROR: failed to detach prog from %s\n", name); } else if (!curr_prog_id) { printf("couldn't find a prog id on a %s\n", name); } else { printf("program on interface changed, not removing\n"); } return err; } #define SAMPLE_SIZE 64 static int print_bpf_output(void *data, int size) { struct { __u16 cookie; __u16 pkt_len; __u8 pkt_data[SAMPLE_SIZE]; } __packed *e = data; int i; if (e->cookie != 0xdead) { printf("BUG cookie %x sized %d\n", e->cookie, size); return LIBBPF_PERF_EVENT_ERROR; } printf("Pkt len: %-5d bytes. Ethernet hdr: ", e->pkt_len); for (i = 0; i < 14 && i < e->pkt_len; i++) printf("%02x ", e->pkt_data[i]); printf("\n"); return LIBBPF_PERF_EVENT_CONT; } static void test_bpf_perf_event(int map_fd, int num) { struct perf_event_attr attr = { .sample_type = PERF_SAMPLE_RAW, .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_BPF_OUTPUT, .wakeup_events = 1, /* get an fd notification for every event */ }; int i; for (i = 0; i < num; i++) { int key = i; pmu_fds[i] = sys_perf_event_open(&attr, -1/*pid*/, i/*cpu*/, -1/*group_fd*/, 0); assert(pmu_fds[i] >= 0); assert(bpf_map_update_elem(map_fd, &key, &pmu_fds[i], BPF_ANY) == 0); ioctl(pmu_fds[i], PERF_EVENT_IOC_ENABLE, 0); } } static void sig_handler(int signo) { do_detach(if_idx, if_name); exit(0); } static void usage(const char *prog) { fprintf(stderr, "%s: %s [OPTS] \n\n" "OPTS:\n" " -F force loading prog\n", __func__, prog); } int main(int argc, char **argv) { struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; struct bpf_prog_load_attr prog_load_attr = { .prog_type = BPF_PROG_TYPE_XDP, }; const char *optstr = "F"; int prog_fd, map_fd, opt; struct bpf_object *obj; struct bpf_map *map; char filename[256]; int ret, err, i; int numcpus; while ((opt = getopt(argc, argv, optstr)) != -1) { switch (opt) { case 'F': xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; break; default: usage(basename(argv[0])); return 1; } } if (optind == argc) { usage(basename(argv[0])); return 1; } if (setrlimit(RLIMIT_MEMLOCK, &r)) { perror("setrlimit(RLIMIT_MEMLOCK)"); return 1; } numcpus = get_nprocs(); if (numcpus > MAX_CPUS) numcpus = MAX_CPUS; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); prog_load_attr.file = filename; if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) return 1; if (!prog_fd) { printf("load_bpf_file: %s\n", strerror(errno)); return 1; } map = bpf_map__next(NULL, obj); if (!map) { printf("finding a map in obj file failed\n"); return 1; } map_fd = bpf_map__fd(map); if_idx = if_nametoindex(argv[optind]); if (!if_idx) if_idx = strtoul(argv[optind], NULL, 0); if (!if_idx) { fprintf(stderr, "Invalid ifname\n"); return 1; } if_name = argv[optind]; err = do_attach(if_idx, prog_fd, if_name); if (err) return err; if (signal(SIGINT, sig_handler) || signal(SIGHUP, sig_handler) || signal(SIGTERM, sig_handler)) { perror("signal"); return 1; } test_bpf_perf_event(map_fd, numcpus); for (i = 0; i < numcpus; i++) if (perf_event_mmap_header(pmu_fds[i], &headers[i]) < 0) return 1; ret = perf_event_poller_multi(pmu_fds, headers, numcpus, print_bpf_output); kill(0, SIGINT); return ret; }