Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[sfrench/cifs-2.6.git] / samples / bpf / xdp_monitor_user.c
1 /* SPDX-License-Identifier: GPL-2.0
2  * Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
3  */
4 static const char *__doc__=
5  "XDP monitor tool, based on tracepoints\n"
6 ;
7
8 static const char *__doc_err_only__=
9  " NOTICE: Only tracking XDP redirect errors\n"
10  "         Enable TX success stats via '--stats'\n"
11  "         (which comes with a per packet processing overhead)\n"
12 ;
13
14 #include <errno.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdbool.h>
18 #include <stdint.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <unistd.h>
22 #include <locale.h>
23
24 #include <sys/resource.h>
25 #include <getopt.h>
26 #include <net/if.h>
27 #include <time.h>
28
29 #include <bpf/bpf.h>
30 #include "bpf_load.h"
31 #include "bpf_util.h"
32
33 static int verbose = 1;
34 static bool debug = false;
35
36 static const struct option long_options[] = {
37         {"help",        no_argument,            NULL, 'h' },
38         {"debug",       no_argument,            NULL, 'D' },
39         {"stats",       no_argument,            NULL, 'S' },
40         {"sec",         required_argument,      NULL, 's' },
41         {0, 0, NULL,  0 }
42 };
43
44 /* C standard specifies two constants, EXIT_SUCCESS(0) and EXIT_FAILURE(1) */
45 #define EXIT_FAIL_MEM   5
46
47 static void usage(char *argv[])
48 {
49         int i;
50         printf("\nDOCUMENTATION:\n%s\n", __doc__);
51         printf("\n");
52         printf(" Usage: %s (options-see-below)\n",
53                argv[0]);
54         printf(" Listing options:\n");
55         for (i = 0; long_options[i].name != 0; i++) {
56                 printf(" --%-15s", long_options[i].name);
57                 if (long_options[i].flag != NULL)
58                         printf(" flag (internal value:%d)",
59                                *long_options[i].flag);
60                 else
61                         printf("short-option: -%c",
62                                long_options[i].val);
63                 printf("\n");
64         }
65         printf("\n");
66 }
67
68 #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
69 static __u64 gettime(void)
70 {
71         struct timespec t;
72         int res;
73
74         res = clock_gettime(CLOCK_MONOTONIC, &t);
75         if (res < 0) {
76                 fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
77                 exit(EXIT_FAILURE);
78         }
79         return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
80 }
81
82 enum {
83         REDIR_SUCCESS = 0,
84         REDIR_ERROR = 1,
85 };
86 #define REDIR_RES_MAX 2
87 static const char *redir_names[REDIR_RES_MAX] = {
88         [REDIR_SUCCESS] = "Success",
89         [REDIR_ERROR]   = "Error",
90 };
91 static const char *err2str(int err)
92 {
93         if (err < REDIR_RES_MAX)
94                 return redir_names[err];
95         return NULL;
96 }
97 /* enum xdp_action */
98 #define XDP_UNKNOWN     XDP_REDIRECT + 1
99 #define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
100 static const char *xdp_action_names[XDP_ACTION_MAX] = {
101         [XDP_ABORTED]   = "XDP_ABORTED",
102         [XDP_DROP]      = "XDP_DROP",
103         [XDP_PASS]      = "XDP_PASS",
104         [XDP_TX]        = "XDP_TX",
105         [XDP_REDIRECT]  = "XDP_REDIRECT",
106         [XDP_UNKNOWN]   = "XDP_UNKNOWN",
107 };
108 static const char *action2str(int action)
109 {
110         if (action < XDP_ACTION_MAX)
111                 return xdp_action_names[action];
112         return NULL;
113 }
114
115 /* Common stats data record shared with _kern.c */
116 struct datarec {
117         __u64 processed;
118         __u64 dropped;
119         __u64 info;
120         __u64 err;
121 };
122 #define MAX_CPUS 64
123
124 /* Userspace structs for collection of stats from maps */
125 struct record {
126         __u64 timestamp;
127         struct datarec total;
128         struct datarec *cpu;
129 };
130 struct u64rec {
131         __u64 processed;
132 };
133 struct record_u64 {
134         /* record for _kern side __u64 values */
135         __u64 timestamp;
136         struct u64rec total;
137         struct u64rec *cpu;
138 };
139
140 struct stats_record {
141         struct record_u64 xdp_redirect[REDIR_RES_MAX];
142         struct record_u64 xdp_exception[XDP_ACTION_MAX];
143         struct record xdp_cpumap_kthread;
144         struct record xdp_cpumap_enqueue[MAX_CPUS];
145         struct record xdp_devmap_xmit;
146 };
147
148 static bool map_collect_record(int fd, __u32 key, struct record *rec)
149 {
150         /* For percpu maps, userspace gets a value per possible CPU */
151         unsigned int nr_cpus = bpf_num_possible_cpus();
152         struct datarec values[nr_cpus];
153         __u64 sum_processed = 0;
154         __u64 sum_dropped = 0;
155         __u64 sum_info = 0;
156         __u64 sum_err = 0;
157         int i;
158
159         if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
160                 fprintf(stderr,
161                         "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
162                 return false;
163         }
164         /* Get time as close as possible to reading map contents */
165         rec->timestamp = gettime();
166
167         /* Record and sum values from each CPU */
168         for (i = 0; i < nr_cpus; i++) {
169                 rec->cpu[i].processed = values[i].processed;
170                 sum_processed        += values[i].processed;
171                 rec->cpu[i].dropped = values[i].dropped;
172                 sum_dropped        += values[i].dropped;
173                 rec->cpu[i].info = values[i].info;
174                 sum_info        += values[i].info;
175                 rec->cpu[i].err = values[i].err;
176                 sum_err        += values[i].err;
177         }
178         rec->total.processed = sum_processed;
179         rec->total.dropped   = sum_dropped;
180         rec->total.info      = sum_info;
181         rec->total.err       = sum_err;
182         return true;
183 }
184
185 static bool map_collect_record_u64(int fd, __u32 key, struct record_u64 *rec)
186 {
187         /* For percpu maps, userspace gets a value per possible CPU */
188         unsigned int nr_cpus = bpf_num_possible_cpus();
189         struct u64rec values[nr_cpus];
190         __u64 sum_total = 0;
191         int i;
192
193         if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
194                 fprintf(stderr,
195                         "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
196                 return false;
197         }
198         /* Get time as close as possible to reading map contents */
199         rec->timestamp = gettime();
200
201         /* Record and sum values from each CPU */
202         for (i = 0; i < nr_cpus; i++) {
203                 rec->cpu[i].processed = values[i].processed;
204                 sum_total            += values[i].processed;
205         }
206         rec->total.processed = sum_total;
207         return true;
208 }
209
210 static double calc_period(struct record *r, struct record *p)
211 {
212         double period_ = 0;
213         __u64 period = 0;
214
215         period = r->timestamp - p->timestamp;
216         if (period > 0)
217                 period_ = ((double) period / NANOSEC_PER_SEC);
218
219         return period_;
220 }
221
222 static double calc_period_u64(struct record_u64 *r, struct record_u64 *p)
223 {
224         double period_ = 0;
225         __u64 period = 0;
226
227         period = r->timestamp - p->timestamp;
228         if (period > 0)
229                 period_ = ((double) period / NANOSEC_PER_SEC);
230
231         return period_;
232 }
233
234 static double calc_pps(struct datarec *r, struct datarec *p, double period)
235 {
236         __u64 packets = 0;
237         double pps = 0;
238
239         if (period > 0) {
240                 packets = r->processed - p->processed;
241                 pps = packets / period;
242         }
243         return pps;
244 }
245
246 static double calc_pps_u64(struct u64rec *r, struct u64rec *p, double period)
247 {
248         __u64 packets = 0;
249         double pps = 0;
250
251         if (period > 0) {
252                 packets = r->processed - p->processed;
253                 pps = packets / period;
254         }
255         return pps;
256 }
257
258 static double calc_drop(struct datarec *r, struct datarec *p, double period)
259 {
260         __u64 packets = 0;
261         double pps = 0;
262
263         if (period > 0) {
264                 packets = r->dropped - p->dropped;
265                 pps = packets / period;
266         }
267         return pps;
268 }
269
270 static double calc_info(struct datarec *r, struct datarec *p, double period)
271 {
272         __u64 packets = 0;
273         double pps = 0;
274
275         if (period > 0) {
276                 packets = r->info - p->info;
277                 pps = packets / period;
278         }
279         return pps;
280 }
281
282 static double calc_err(struct datarec *r, struct datarec *p, double period)
283 {
284         __u64 packets = 0;
285         double pps = 0;
286
287         if (period > 0) {
288                 packets = r->err - p->err;
289                 pps = packets / period;
290         }
291         return pps;
292 }
293
294 static void stats_print(struct stats_record *stats_rec,
295                         struct stats_record *stats_prev,
296                         bool err_only)
297 {
298         unsigned int nr_cpus = bpf_num_possible_cpus();
299         int rec_i = 0, i, to_cpu;
300         double t = 0, pps = 0;
301
302         /* Header */
303         printf("%-15s %-7s %-12s %-12s %-9s\n",
304                "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
305
306         /* tracepoint: xdp:xdp_redirect_* */
307         if (err_only)
308                 rec_i = REDIR_ERROR;
309
310         for (; rec_i < REDIR_RES_MAX; rec_i++) {
311                 struct record_u64 *rec, *prev;
312                 char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
313                 char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
314
315                 rec  =  &stats_rec->xdp_redirect[rec_i];
316                 prev = &stats_prev->xdp_redirect[rec_i];
317                 t = calc_period_u64(rec, prev);
318
319                 for (i = 0; i < nr_cpus; i++) {
320                         struct u64rec *r = &rec->cpu[i];
321                         struct u64rec *p = &prev->cpu[i];
322
323                         pps = calc_pps_u64(r, p, t);
324                         if (pps > 0)
325                                 printf(fmt1, "XDP_REDIRECT", i,
326                                        rec_i ? 0.0: pps, rec_i ? pps : 0.0,
327                                        err2str(rec_i));
328                 }
329                 pps = calc_pps_u64(&rec->total, &prev->total, t);
330                 printf(fmt2, "XDP_REDIRECT", "total",
331                        rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i));
332         }
333
334         /* tracepoint: xdp:xdp_exception */
335         for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
336                 struct record_u64 *rec, *prev;
337                 char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
338                 char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
339
340                 rec  =  &stats_rec->xdp_exception[rec_i];
341                 prev = &stats_prev->xdp_exception[rec_i];
342                 t = calc_period_u64(rec, prev);
343
344                 for (i = 0; i < nr_cpus; i++) {
345                         struct u64rec *r = &rec->cpu[i];
346                         struct u64rec *p = &prev->cpu[i];
347
348                         pps = calc_pps_u64(r, p, t);
349                         if (pps > 0)
350                                 printf(fmt1, "Exception", i,
351                                        0.0, pps, action2str(rec_i));
352                 }
353                 pps = calc_pps_u64(&rec->total, &prev->total, t);
354                 if (pps > 0)
355                         printf(fmt2, "Exception", "total",
356                                0.0, pps, action2str(rec_i));
357         }
358
359         /* cpumap enqueue stats */
360         for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) {
361                 char *fmt1 = "%-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
362                 char *fmt2 = "%-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
363                 struct record *rec, *prev;
364                 char *info_str = "";
365                 double drop, info;
366
367                 rec  =  &stats_rec->xdp_cpumap_enqueue[to_cpu];
368                 prev = &stats_prev->xdp_cpumap_enqueue[to_cpu];
369                 t = calc_period(rec, prev);
370                 for (i = 0; i < nr_cpus; i++) {
371                         struct datarec *r = &rec->cpu[i];
372                         struct datarec *p = &prev->cpu[i];
373
374                         pps  = calc_pps(r, p, t);
375                         drop = calc_drop(r, p, t);
376                         info = calc_info(r, p, t);
377                         if (info > 0) {
378                                 info_str = "bulk-average";
379                                 info = pps / info; /* calc average bulk size */
380                         }
381                         if (pps > 0)
382                                 printf(fmt1, "cpumap-enqueue",
383                                        i, to_cpu, pps, drop, info, info_str);
384                 }
385                 pps = calc_pps(&rec->total, &prev->total, t);
386                 if (pps > 0) {
387                         drop = calc_drop(&rec->total, &prev->total, t);
388                         info = calc_info(&rec->total, &prev->total, t);
389                         if (info > 0) {
390                                 info_str = "bulk-average";
391                                 info = pps / info; /* calc average bulk size */
392                         }
393                         printf(fmt2, "cpumap-enqueue",
394                                "sum", to_cpu, pps, drop, info, info_str);
395                 }
396         }
397
398         /* cpumap kthread stats */
399         {
400                 char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s\n";
401                 char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s\n";
402                 struct record *rec, *prev;
403                 double drop, info;
404                 char *i_str = "";
405
406                 rec  =  &stats_rec->xdp_cpumap_kthread;
407                 prev = &stats_prev->xdp_cpumap_kthread;
408                 t = calc_period(rec, prev);
409                 for (i = 0; i < nr_cpus; i++) {
410                         struct datarec *r = &rec->cpu[i];
411                         struct datarec *p = &prev->cpu[i];
412
413                         pps  = calc_pps(r, p, t);
414                         drop = calc_drop(r, p, t);
415                         info = calc_info(r, p, t);
416                         if (info > 0)
417                                 i_str = "sched";
418                         if (pps > 0 || drop > 0)
419                                 printf(fmt1, "cpumap-kthread",
420                                        i, pps, drop, info, i_str);
421                 }
422                 pps = calc_pps(&rec->total, &prev->total, t);
423                 drop = calc_drop(&rec->total, &prev->total, t);
424                 info = calc_info(&rec->total, &prev->total, t);
425                 if (info > 0)
426                         i_str = "sched-sum";
427                 printf(fmt2, "cpumap-kthread", "total", pps, drop, info, i_str);
428         }
429
430         /* devmap ndo_xdp_xmit stats */
431         {
432                 char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.2f %s %s\n";
433                 char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.2f %s %s\n";
434                 struct record *rec, *prev;
435                 double drop, info, err;
436                 char *i_str = "";
437                 char *err_str = "";
438
439                 rec  =  &stats_rec->xdp_devmap_xmit;
440                 prev = &stats_prev->xdp_devmap_xmit;
441                 t = calc_period(rec, prev);
442                 for (i = 0; i < nr_cpus; i++) {
443                         struct datarec *r = &rec->cpu[i];
444                         struct datarec *p = &prev->cpu[i];
445
446                         pps  = calc_pps(r, p, t);
447                         drop = calc_drop(r, p, t);
448                         info = calc_info(r, p, t);
449                         err  = calc_err(r, p, t);
450                         if (info > 0) {
451                                 i_str = "bulk-average";
452                                 info = (pps+drop) / info; /* calc avg bulk */
453                         }
454                         if (err > 0)
455                                 err_str = "drv-err";
456                         if (pps > 0 || drop > 0)
457                                 printf(fmt1, "devmap-xmit",
458                                        i, pps, drop, info, i_str, err_str);
459                 }
460                 pps = calc_pps(&rec->total, &prev->total, t);
461                 drop = calc_drop(&rec->total, &prev->total, t);
462                 info = calc_info(&rec->total, &prev->total, t);
463                 err  = calc_err(&rec->total, &prev->total, t);
464                 if (info > 0) {
465                         i_str = "bulk-average";
466                         info = (pps+drop) / info; /* calc avg bulk */
467                 }
468                 if (err > 0)
469                         err_str = "drv-err";
470                 printf(fmt2, "devmap-xmit", "total", pps, drop,
471                        info, i_str, err_str);
472         }
473
474         printf("\n");
475 }
476
477 static bool stats_collect(struct stats_record *rec)
478 {
479         int fd;
480         int i;
481
482         /* TODO: Detect if someone unloaded the perf event_fd's, as
483          * this can happen by someone running perf-record -e
484          */
485
486         fd = map_data[0].fd; /* map0: redirect_err_cnt */
487         for (i = 0; i < REDIR_RES_MAX; i++)
488                 map_collect_record_u64(fd, i, &rec->xdp_redirect[i]);
489
490         fd = map_data[1].fd; /* map1: exception_cnt */
491         for (i = 0; i < XDP_ACTION_MAX; i++) {
492                 map_collect_record_u64(fd, i, &rec->xdp_exception[i]);
493         }
494
495         fd = map_data[2].fd; /* map2: cpumap_enqueue_cnt */
496         for (i = 0; i < MAX_CPUS; i++)
497                 map_collect_record(fd, i, &rec->xdp_cpumap_enqueue[i]);
498
499         fd = map_data[3].fd; /* map3: cpumap_kthread_cnt */
500         map_collect_record(fd, 0, &rec->xdp_cpumap_kthread);
501
502         fd = map_data[4].fd; /* map4: devmap_xmit_cnt */
503         map_collect_record(fd, 0, &rec->xdp_devmap_xmit);
504
505         return true;
506 }
507
508 static void *alloc_rec_per_cpu(int record_size)
509 {
510         unsigned int nr_cpus = bpf_num_possible_cpus();
511         void *array;
512         size_t size;
513
514         size = record_size * nr_cpus;
515         array = malloc(size);
516         memset(array, 0, size);
517         if (!array) {
518                 fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
519                 exit(EXIT_FAIL_MEM);
520         }
521         return array;
522 }
523
524 static struct stats_record *alloc_stats_record(void)
525 {
526         struct stats_record *rec;
527         int rec_sz;
528         int i;
529
530         /* Alloc main stats_record structure */
531         rec = malloc(sizeof(*rec));
532         memset(rec, 0, sizeof(*rec));
533         if (!rec) {
534                 fprintf(stderr, "Mem alloc error\n");
535                 exit(EXIT_FAIL_MEM);
536         }
537
538         /* Alloc stats stored per CPU for each record */
539         rec_sz = sizeof(struct u64rec);
540         for (i = 0; i < REDIR_RES_MAX; i++)
541                 rec->xdp_redirect[i].cpu = alloc_rec_per_cpu(rec_sz);
542
543         for (i = 0; i < XDP_ACTION_MAX; i++)
544                 rec->xdp_exception[i].cpu = alloc_rec_per_cpu(rec_sz);
545
546         rec_sz = sizeof(struct datarec);
547         rec->xdp_cpumap_kthread.cpu = alloc_rec_per_cpu(rec_sz);
548         rec->xdp_devmap_xmit.cpu    = alloc_rec_per_cpu(rec_sz);
549
550         for (i = 0; i < MAX_CPUS; i++)
551                 rec->xdp_cpumap_enqueue[i].cpu = alloc_rec_per_cpu(rec_sz);
552
553         return rec;
554 }
555
556 static void free_stats_record(struct stats_record *r)
557 {
558         int i;
559
560         for (i = 0; i < REDIR_RES_MAX; i++)
561                 free(r->xdp_redirect[i].cpu);
562
563         for (i = 0; i < XDP_ACTION_MAX; i++)
564                 free(r->xdp_exception[i].cpu);
565
566         free(r->xdp_cpumap_kthread.cpu);
567         free(r->xdp_devmap_xmit.cpu);
568
569         for (i = 0; i < MAX_CPUS; i++)
570                 free(r->xdp_cpumap_enqueue[i].cpu);
571
572         free(r);
573 }
574
575 /* Pointer swap trick */
576 static inline void swap(struct stats_record **a, struct stats_record **b)
577 {
578         struct stats_record *tmp;
579
580         tmp = *a;
581         *a = *b;
582         *b = tmp;
583 }
584
585 static void stats_poll(int interval, bool err_only)
586 {
587         struct stats_record *rec, *prev;
588
589         rec  = alloc_stats_record();
590         prev = alloc_stats_record();
591         stats_collect(rec);
592
593         if (err_only)
594                 printf("\n%s\n", __doc_err_only__);
595
596         /* Trick to pretty printf with thousands separators use %' */
597         setlocale(LC_NUMERIC, "en_US");
598
599         /* Header */
600         if (verbose)
601                 printf("\n%s", __doc__);
602
603         /* TODO Need more advanced stats on error types */
604         if (verbose) {
605                 printf(" - Stats map0: %s\n", map_data[0].name);
606                 printf(" - Stats map1: %s\n", map_data[1].name);
607                 printf("\n");
608         }
609         fflush(stdout);
610
611         while (1) {
612                 swap(&prev, &rec);
613                 stats_collect(rec);
614                 stats_print(rec, prev, err_only);
615                 fflush(stdout);
616                 sleep(interval);
617         }
618
619         free_stats_record(rec);
620         free_stats_record(prev);
621 }
622
623 static void print_bpf_prog_info(void)
624 {
625         int i;
626
627         /* Prog info */
628         printf("Loaded BPF prog have %d bpf program(s)\n", prog_cnt);
629         for (i = 0; i < prog_cnt; i++) {
630                 printf(" - prog_fd[%d] = fd(%d)\n", i, prog_fd[i]);
631         }
632
633         /* Maps info */
634         printf("Loaded BPF prog have %d map(s)\n", map_data_count);
635         for (i = 0; i < map_data_count; i++) {
636                 char *name = map_data[i].name;
637                 int fd     = map_data[i].fd;
638
639                 printf(" - map_data[%d] = fd(%d) name:%s\n", i, fd, name);
640         }
641
642         /* Event info */
643         printf("Searching for (max:%d) event file descriptor(s)\n", prog_cnt);
644         for (i = 0; i < prog_cnt; i++) {
645                 if (event_fd[i] != -1)
646                         printf(" - event_fd[%d] = fd(%d)\n", i, event_fd[i]);
647         }
648 }
649
650 int main(int argc, char **argv)
651 {
652         struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
653         int longindex = 0, opt;
654         int ret = EXIT_SUCCESS;
655         char bpf_obj_file[256];
656
657         /* Default settings: */
658         bool errors_only = true;
659         int interval = 2;
660
661         snprintf(bpf_obj_file, sizeof(bpf_obj_file), "%s_kern.o", argv[0]);
662
663         /* Parse commands line args */
664         while ((opt = getopt_long(argc, argv, "hDSs:",
665                                   long_options, &longindex)) != -1) {
666                 switch (opt) {
667                 case 'D':
668                         debug = true;
669                         break;
670                 case 'S':
671                         errors_only = false;
672                         break;
673                 case 's':
674                         interval = atoi(optarg);
675                         break;
676                 case 'h':
677                 default:
678                         usage(argv);
679                         return EXIT_FAILURE;
680                 }
681         }
682
683         if (setrlimit(RLIMIT_MEMLOCK, &r)) {
684                 perror("setrlimit(RLIMIT_MEMLOCK)");
685                 return EXIT_FAILURE;
686         }
687
688         if (load_bpf_file(bpf_obj_file)) {
689                 printf("ERROR - bpf_log_buf: %s", bpf_log_buf);
690                 return EXIT_FAILURE;
691         }
692         if (!prog_fd[0]) {
693                 printf("ERROR - load_bpf_file: %s\n", strerror(errno));
694                 return EXIT_FAILURE;
695         }
696
697         if (debug) {
698                 print_bpf_prog_info();
699         }
700
701         /* Unload/stop tracepoint event by closing fd's */
702         if (errors_only) {
703                 /* The prog_fd[i] and event_fd[i] depend on the
704                  * order the functions was defined in _kern.c
705                  */
706                 close(event_fd[2]); /* tracepoint/xdp/xdp_redirect */
707                 close(prog_fd[2]);  /* func: trace_xdp_redirect */
708                 close(event_fd[3]); /* tracepoint/xdp/xdp_redirect_map */
709                 close(prog_fd[3]);  /* func: trace_xdp_redirect_map */
710         }
711
712         stats_poll(interval, errors_only);
713
714         return ret;
715 }