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

samples, bpf: Refactor perf_event user program with libbpf bpf_link

The bpf_program__attach of libbpf(using bpf_link) is much more intuitive
than the previous method using ioctl.

bpf_program__attach_perf_event manages the enable of perf_event and
attach of BPF programs to it, so there's no neeed to do this
directly with ioctl.

In addition, bpf_link provides consistency in the use of API because it
allows disable (detach, destroy) for multiple events to be treated as
one bpf_link__destroy. Also, bpf_link__destroy manages the close() of
perf_event fd.

This commit refactors samples that attach the bpf program to perf_event
by using libbbpf instead of ioctl. Also the bpf_load in the samples were
removed and migrated to use libbbpf API.

Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/20200321100424.1593964-3-danieltimlee@gmail.com

authored by

Daniel T. Lee and committed by
Daniel Borkmann
aa5e2af6 24a6034a

+160 -83
+2 -2
samples/bpf/Makefile
··· 88 88 xdp_router_ipv4-objs := xdp_router_ipv4_user.o 89 89 test_current_task_under_cgroup-objs := bpf_load.o $(CGROUP_HELPERS) \ 90 90 test_current_task_under_cgroup_user.o 91 - trace_event-objs := bpf_load.o trace_event_user.o $(TRACE_HELPERS) 92 - sampleip-objs := bpf_load.o sampleip_user.o $(TRACE_HELPERS) 91 + trace_event-objs := trace_event_user.o $(TRACE_HELPERS) 92 + sampleip-objs := sampleip_user.o $(TRACE_HELPERS) 93 93 tc_l2_redirect-objs := bpf_load.o tc_l2_redirect_user.o 94 94 lwt_len_hist-objs := bpf_load.o lwt_len_hist_user.o 95 95 xdp_tx_iptunnel-objs := xdp_tx_iptunnel_user.o
+64 -34
samples/bpf/sampleip_user.c
··· 10 10 #include <errno.h> 11 11 #include <signal.h> 12 12 #include <string.h> 13 - #include <assert.h> 14 13 #include <linux/perf_event.h> 15 14 #include <linux/ptrace.h> 16 15 #include <linux/bpf.h> 17 - #include <sys/ioctl.h> 16 + #include <bpf/bpf.h> 18 17 #include <bpf/libbpf.h> 19 - #include "bpf_load.h" 20 18 #include "perf-sys.h" 21 19 #include "trace_helpers.h" 20 + 21 + #define __must_check 22 + #include <linux/err.h> 22 23 23 24 #define DEFAULT_FREQ 99 24 25 #define DEFAULT_SECS 5 25 26 #define MAX_IPS 8192 26 27 #define PAGE_OFFSET 0xffff880000000000 27 28 29 + static int map_fd; 28 30 static int nr_cpus; 29 31 30 32 static void usage(void) ··· 36 34 printf(" duration # sampling duration (seconds), default 5\n"); 37 35 } 38 36 39 - static int sampling_start(int *pmu_fd, int freq) 37 + static int sampling_start(int freq, struct bpf_program *prog, 38 + struct bpf_link *links[]) 40 39 { 41 - int i; 40 + int i, pmu_fd; 42 41 43 42 struct perf_event_attr pe_sample_attr = { 44 43 .type = PERF_TYPE_SOFTWARE, ··· 50 47 }; 51 48 52 49 for (i = 0; i < nr_cpus; i++) { 53 - pmu_fd[i] = sys_perf_event_open(&pe_sample_attr, -1 /* pid */, i, 50 + pmu_fd = sys_perf_event_open(&pe_sample_attr, -1 /* pid */, i, 54 51 -1 /* group_fd */, 0 /* flags */); 55 - if (pmu_fd[i] < 0) { 52 + if (pmu_fd < 0) { 56 53 fprintf(stderr, "ERROR: Initializing perf sampling\n"); 57 54 return 1; 58 55 } 59 - assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, 60 - prog_fd[0]) == 0); 61 - assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE, 0) == 0); 56 + links[i] = bpf_program__attach_perf_event(prog, pmu_fd); 57 + if (IS_ERR(links[i])) { 58 + fprintf(stderr, "ERROR: Attach perf event\n"); 59 + links[i] = NULL; 60 + close(pmu_fd); 61 + return 1; 62 + } 62 63 } 63 64 64 65 return 0; 65 66 } 66 67 67 - static void sampling_end(int *pmu_fd) 68 + static void sampling_end(struct bpf_link *links[]) 68 69 { 69 70 int i; 70 71 71 72 for (i = 0; i < nr_cpus; i++) 72 - close(pmu_fd[i]); 73 + bpf_link__destroy(links[i]); 73 74 } 74 75 75 76 struct ipcount { ··· 135 128 static void int_exit(int sig) 136 129 { 137 130 printf("\n"); 138 - print_ip_map(map_fd[0]); 131 + print_ip_map(map_fd); 139 132 exit(0); 140 133 } 141 134 142 135 int main(int argc, char **argv) 143 136 { 137 + int opt, freq = DEFAULT_FREQ, secs = DEFAULT_SECS, error = 1; 138 + struct bpf_object *obj = NULL; 139 + struct bpf_program *prog; 140 + struct bpf_link **links; 144 141 char filename[256]; 145 - int *pmu_fd, opt, freq = DEFAULT_FREQ, secs = DEFAULT_SECS; 146 142 147 143 /* process arguments */ 148 144 while ((opt = getopt(argc, argv, "F:h")) != -1) { ··· 173 163 } 174 164 175 165 /* create perf FDs for each CPU */ 176 - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 177 - pmu_fd = malloc(nr_cpus * sizeof(int)); 178 - if (pmu_fd == NULL) { 179 - fprintf(stderr, "ERROR: malloc of pmu_fd\n"); 180 - return 1; 166 + nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); 167 + links = calloc(nr_cpus, sizeof(struct bpf_link *)); 168 + if (!links) { 169 + fprintf(stderr, "ERROR: malloc of links\n"); 170 + goto cleanup; 171 + } 172 + 173 + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 174 + obj = bpf_object__open_file(filename, NULL); 175 + if (IS_ERR(obj)) { 176 + fprintf(stderr, "ERROR: opening BPF object file failed\n"); 177 + obj = NULL; 178 + goto cleanup; 179 + } 180 + 181 + prog = bpf_object__find_program_by_name(obj, "do_sample"); 182 + if (!prog) { 183 + fprintf(stderr, "ERROR: finding a prog in obj file failed\n"); 184 + goto cleanup; 181 185 } 182 186 183 187 /* load BPF program */ 184 - snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 185 - if (load_bpf_file(filename)) { 186 - fprintf(stderr, "ERROR: loading BPF program (errno %d):\n", 187 - errno); 188 - if (strcmp(bpf_log_buf, "") == 0) 189 - fprintf(stderr, "Try: ulimit -l unlimited\n"); 190 - else 191 - fprintf(stderr, "%s", bpf_log_buf); 192 - return 1; 188 + if (bpf_object__load(obj)) { 189 + fprintf(stderr, "ERROR: loading BPF object file failed\n"); 190 + goto cleanup; 193 191 } 192 + 193 + map_fd = bpf_object__find_map_fd_by_name(obj, "ip_map"); 194 + if (map_fd < 0) { 195 + fprintf(stderr, "ERROR: finding a map in obj file failed\n"); 196 + goto cleanup; 197 + } 198 + 194 199 signal(SIGINT, int_exit); 195 200 signal(SIGTERM, int_exit); 196 201 197 202 /* do sampling */ 198 203 printf("Sampling at %d Hertz for %d seconds. Ctrl-C also ends.\n", 199 204 freq, secs); 200 - if (sampling_start(pmu_fd, freq) != 0) 201 - return 1; 205 + if (sampling_start(freq, prog, links) != 0) 206 + goto cleanup; 207 + 202 208 sleep(secs); 203 - sampling_end(pmu_fd); 204 - free(pmu_fd); 209 + error = 0; 205 210 211 + cleanup: 212 + sampling_end(links); 206 213 /* output sample counts */ 207 - print_ip_map(map_fd[0]); 214 + if (!error) 215 + print_ip_map(map_fd); 208 216 209 - return 0; 217 + free(links); 218 + bpf_object__close(obj); 219 + return error; 210 220 }
+94 -47
samples/bpf/trace_event_user.c
··· 6 6 #include <stdlib.h> 7 7 #include <stdbool.h> 8 8 #include <string.h> 9 - #include <fcntl.h> 10 - #include <poll.h> 11 - #include <sys/ioctl.h> 12 9 #include <linux/perf_event.h> 13 10 #include <linux/bpf.h> 14 11 #include <signal.h> 15 - #include <assert.h> 16 12 #include <errno.h> 17 13 #include <sys/resource.h> 14 + #include <bpf/bpf.h> 18 15 #include <bpf/libbpf.h> 19 - #include "bpf_load.h" 20 16 #include "perf-sys.h" 21 17 #include "trace_helpers.h" 22 18 19 + #define __must_check 20 + #include <linux/err.h> 21 + 23 22 #define SAMPLE_FREQ 50 24 23 24 + static int pid; 25 + /* counts, stackmap */ 26 + static int map_fd[2]; 27 + struct bpf_program *prog; 25 28 static bool sys_read_seen, sys_write_seen; 26 29 27 30 static void print_ksym(__u64 addr) ··· 94 91 } 95 92 } 96 93 97 - static void int_exit(int sig) 94 + static void err_exit(int err) 98 95 { 99 - kill(0, SIGKILL); 100 - exit(0); 96 + kill(pid, SIGKILL); 97 + exit(err); 101 98 } 102 99 103 100 static void print_stacks(void) ··· 105 102 struct key_t key = {}, next_key; 106 103 __u64 value; 107 104 __u32 stackid = 0, next_id; 108 - int fd = map_fd[0], stack_map = map_fd[1]; 105 + int error = 1, fd = map_fd[0], stack_map = map_fd[1]; 109 106 110 107 sys_read_seen = sys_write_seen = false; 111 108 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { ··· 117 114 printf("\n"); 118 115 if (!sys_read_seen || !sys_write_seen) { 119 116 printf("BUG kernel stack doesn't contain sys_read() and sys_write()\n"); 120 - int_exit(0); 117 + err_exit(error); 121 118 } 122 119 123 120 /* clear stack map */ ··· 139 136 140 137 static void test_perf_event_all_cpu(struct perf_event_attr *attr) 141 138 { 142 - int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 143 - int *pmu_fd = malloc(nr_cpus * sizeof(int)); 144 - int i, error = 0; 139 + int nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); 140 + struct bpf_link **links = calloc(nr_cpus, sizeof(struct bpf_link *)); 141 + int i, pmu_fd, error = 1; 142 + 143 + if (!links) { 144 + printf("malloc of links failed\n"); 145 + goto err; 146 + } 145 147 146 148 /* system wide perf event, no need to inherit */ 147 149 attr->inherit = 0; 148 150 149 151 /* open perf_event on all cpus */ 150 152 for (i = 0; i < nr_cpus; i++) { 151 - pmu_fd[i] = sys_perf_event_open(attr, -1, i, -1, 0); 152 - if (pmu_fd[i] < 0) { 153 + pmu_fd = sys_perf_event_open(attr, -1, i, -1, 0); 154 + if (pmu_fd < 0) { 153 155 printf("sys_perf_event_open failed\n"); 154 - error = 1; 155 156 goto all_cpu_err; 156 157 } 157 - assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); 158 - assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE) == 0); 158 + links[i] = bpf_program__attach_perf_event(prog, pmu_fd); 159 + if (IS_ERR(links[i])) { 160 + printf("bpf_program__attach_perf_event failed\n"); 161 + links[i] = NULL; 162 + close(pmu_fd); 163 + goto all_cpu_err; 164 + } 159 165 } 160 166 161 - if (generate_load() < 0) { 162 - error = 1; 167 + if (generate_load() < 0) 163 168 goto all_cpu_err; 164 - } 169 + 165 170 print_stacks(); 171 + error = 0; 166 172 all_cpu_err: 167 - for (i--; i >= 0; i--) { 168 - ioctl(pmu_fd[i], PERF_EVENT_IOC_DISABLE); 169 - close(pmu_fd[i]); 170 - } 171 - free(pmu_fd); 173 + for (i--; i >= 0; i--) 174 + bpf_link__destroy(links[i]); 175 + err: 176 + free(links); 172 177 if (error) 173 - int_exit(0); 178 + err_exit(error); 174 179 } 175 180 176 181 static void test_perf_event_task(struct perf_event_attr *attr) 177 182 { 178 - int pmu_fd, error = 0; 183 + struct bpf_link *link = NULL; 184 + int pmu_fd, error = 1; 179 185 180 186 /* per task perf event, enable inherit so the "dd ..." command can be traced properly. 181 187 * Enabling inherit will cause bpf_perf_prog_read_time helper failure. ··· 195 183 pmu_fd = sys_perf_event_open(attr, 0, -1, -1, 0); 196 184 if (pmu_fd < 0) { 197 185 printf("sys_perf_event_open failed\n"); 198 - int_exit(0); 199 - } 200 - assert(ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); 201 - assert(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE) == 0); 202 - 203 - if (generate_load() < 0) { 204 - error = 1; 205 186 goto err; 206 187 } 188 + link = bpf_program__attach_perf_event(prog, pmu_fd); 189 + if (IS_ERR(link)) { 190 + printf("bpf_program__attach_perf_event failed\n"); 191 + link = NULL; 192 + close(pmu_fd); 193 + goto err; 194 + } 195 + 196 + if (generate_load() < 0) 197 + goto err; 198 + 207 199 print_stacks(); 200 + error = 0; 208 201 err: 209 - ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); 210 - close(pmu_fd); 202 + bpf_link__destroy(link); 211 203 if (error) 212 - int_exit(0); 204 + err_exit(error); 213 205 } 214 206 215 207 static void test_bpf_perf_event(void) ··· 298 282 int main(int argc, char **argv) 299 283 { 300 284 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 285 + struct bpf_object *obj = NULL; 301 286 char filename[256]; 287 + int error = 1; 302 288 303 289 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 304 290 setrlimit(RLIMIT_MEMLOCK, &r); 305 291 306 - signal(SIGINT, int_exit); 307 - signal(SIGTERM, int_exit); 292 + signal(SIGINT, err_exit); 293 + signal(SIGTERM, err_exit); 308 294 309 295 if (load_kallsyms()) { 310 296 printf("failed to process /proc/kallsyms\n"); 311 - return 1; 297 + goto cleanup; 312 298 } 313 299 314 - if (load_bpf_file(filename)) { 315 - printf("%s", bpf_log_buf); 316 - return 2; 300 + obj = bpf_object__open_file(filename, NULL); 301 + if (IS_ERR(obj)) { 302 + printf("opening BPF object file failed\n"); 303 + obj = NULL; 304 + goto cleanup; 317 305 } 318 306 319 - if (fork() == 0) { 307 + prog = bpf_object__find_program_by_name(obj, "bpf_prog1"); 308 + if (!prog) { 309 + printf("finding a prog in obj file failed\n"); 310 + goto cleanup; 311 + } 312 + 313 + /* load BPF program */ 314 + if (bpf_object__load(obj)) { 315 + printf("loading BPF object file failed\n"); 316 + goto cleanup; 317 + } 318 + 319 + map_fd[0] = bpf_object__find_map_fd_by_name(obj, "counts"); 320 + map_fd[1] = bpf_object__find_map_fd_by_name(obj, "stackmap"); 321 + if (map_fd[0] < 0 || map_fd[1] < 0) { 322 + printf("finding a counts/stackmap map in obj file failed\n"); 323 + goto cleanup; 324 + } 325 + 326 + pid = fork(); 327 + if (pid == 0) { 320 328 read_trace_pipe(); 321 329 return 0; 330 + } else if (pid == -1) { 331 + printf("couldn't spawn process\n"); 332 + goto cleanup; 322 333 } 334 + 323 335 test_bpf_perf_event(); 324 - int_exit(0); 325 - return 0; 336 + error = 0; 337 + 338 + cleanup: 339 + bpf_object__close(obj); 340 + err_exit(error); 326 341 }