Merge tag 'mips_fixes_4.16_1' of git://git.kernel.org/pub/scm/linux/kernel/git/jhogan...
[linux-2.6-block.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 "libbpf.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("(internal 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 };
121 #define MAX_CPUS 64
122
123 /* Userspace structs for collection of stats from maps */
124 struct record {
125         __u64 timestamp;
126         struct datarec total;
127         struct datarec *cpu;
128 };
129 struct u64rec {
130         __u64 processed;
131 };
132 struct record_u64 {
133         /* record for _kern side __u64 values */
134         __u64 timestamp;
135         struct u64rec total;
136         struct u64rec *cpu;
137 };
138
139 struct stats_record {
140         struct record_u64 xdp_redirect[REDIR_RES_MAX];
141         struct record_u64 xdp_exception[XDP_ACTION_MAX];
142         struct record xdp_cpumap_kthread;
143         struct record xdp_cpumap_enqueue[MAX_CPUS];
144 };
145
146 static bool map_collect_record(int fd, __u32 key, struct record *rec)
147 {
148         /* For percpu maps, userspace gets a value per possible CPU */
149         unsigned int nr_cpus = bpf_num_possible_cpus();
150         struct datarec values[nr_cpus];
151         __u64 sum_processed = 0;
152         __u64 sum_dropped = 0;
153         __u64 sum_info = 0;
154         int i;
155
156         if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
157                 fprintf(stderr,
158                         "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
159                 return false;
160         }
161         /* Get time as close as possible to reading map contents */
162         rec->timestamp = gettime();
163
164         /* Record and sum values from each CPU */
165         for (i = 0; i < nr_cpus; i++) {
166                 rec->cpu[i].processed = values[i].processed;
167                 sum_processed        += values[i].processed;
168                 rec->cpu[i].dropped = values[i].dropped;
169                 sum_dropped        += values[i].dropped;
170                 rec->cpu[i].info = values[i].info;
171                 sum_info        += values[i].info;
172         }
173         rec->total.processed = sum_processed;
174         rec->total.dropped   = sum_dropped;
175         rec->total.info      = sum_info;
176         return true;
177 }
178
179 static bool map_collect_record_u64(int fd, __u32 key, struct record_u64 *rec)
180 {
181         /* For percpu maps, userspace gets a value per possible CPU */
182         unsigned int nr_cpus = bpf_num_possible_cpus();
183         struct u64rec values[nr_cpus];
184         __u64 sum_total = 0;
185         int i;
186
187         if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
188                 fprintf(stderr,
189                         "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
190                 return false;
191         }
192         /* Get time as close as possible to reading map contents */
193         rec->timestamp = gettime();
194
195         /* Record and sum values from each CPU */
196         for (i = 0; i < nr_cpus; i++) {
197                 rec->cpu[i].processed = values[i].processed;
198                 sum_total            += values[i].processed;
199         }
200         rec->total.processed = sum_total;
201         return true;
202 }
203
204 static double calc_period(struct record *r, struct record *p)
205 {
206         double period_ = 0;
207         __u64 period = 0;
208
209         period = r->timestamp - p->timestamp;
210         if (period > 0)
211                 period_ = ((double) period / NANOSEC_PER_SEC);
212
213         return period_;
214 }
215
216 static double calc_period_u64(struct record_u64 *r, struct record_u64 *p)
217 {
218         double period_ = 0;
219         __u64 period = 0;
220
221         period = r->timestamp - p->timestamp;
222         if (period > 0)
223                 period_ = ((double) period / NANOSEC_PER_SEC);
224
225         return period_;
226 }
227
228 static double calc_pps(struct datarec *r, struct datarec *p, double period)
229 {
230         __u64 packets = 0;
231         double pps = 0;
232
233         if (period > 0) {
234                 packets = r->processed - p->processed;
235                 pps = packets / period;
236         }
237         return pps;
238 }
239
240 static double calc_pps_u64(struct u64rec *r, struct u64rec *p, double period)
241 {
242         __u64 packets = 0;
243         double pps = 0;
244
245         if (period > 0) {
246                 packets = r->processed - p->processed;
247                 pps = packets / period;
248         }
249         return pps;
250 }
251
252 static double calc_drop(struct datarec *r, struct datarec *p, double period)
253 {
254         __u64 packets = 0;
255         double pps = 0;
256
257         if (period > 0) {
258                 packets = r->dropped - p->dropped;
259                 pps = packets / period;
260         }
261         return pps;
262 }
263
264 static double calc_info(struct datarec *r, struct datarec *p, double period)
265 {
266         __u64 packets = 0;
267         double pps = 0;
268
269         if (period > 0) {
270                 packets = r->info - p->info;
271                 pps = packets / period;
272         }
273         return pps;
274 }
275
276 static void stats_print(struct stats_record *stats_rec,
277                         struct stats_record *stats_prev,
278                         bool err_only)
279 {
280         unsigned int nr_cpus = bpf_num_possible_cpus();
281         int rec_i = 0, i, to_cpu;
282         double t = 0, pps = 0;
283
284         /* Header */
285         printf("%-15s %-7s %-12s %-12s %-9s\n",
286                "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
287
288         /* tracepoint: xdp:xdp_redirect_* */
289         if (err_only)
290                 rec_i = REDIR_ERROR;
291
292         for (; rec_i < REDIR_RES_MAX; rec_i++) {
293                 struct record_u64 *rec, *prev;
294                 char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
295                 char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
296
297                 rec  =  &stats_rec->xdp_redirect[rec_i];
298                 prev = &stats_prev->xdp_redirect[rec_i];
299                 t = calc_period_u64(rec, prev);
300
301                 for (i = 0; i < nr_cpus; i++) {
302                         struct u64rec *r = &rec->cpu[i];
303                         struct u64rec *p = &prev->cpu[i];
304
305                         pps = calc_pps_u64(r, p, t);
306                         if (pps > 0)
307                                 printf(fmt1, "XDP_REDIRECT", i,
308                                        rec_i ? 0.0: pps, rec_i ? pps : 0.0,
309                                        err2str(rec_i));
310                 }
311                 pps = calc_pps_u64(&rec->total, &prev->total, t);
312                 printf(fmt2, "XDP_REDIRECT", "total",
313                        rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i));
314         }
315
316         /* tracepoint: xdp:xdp_exception */
317         for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
318                 struct record_u64 *rec, *prev;
319                 char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
320                 char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
321
322                 rec  =  &stats_rec->xdp_exception[rec_i];
323                 prev = &stats_prev->xdp_exception[rec_i];
324                 t = calc_period_u64(rec, prev);
325
326                 for (i = 0; i < nr_cpus; i++) {
327                         struct u64rec *r = &rec->cpu[i];
328                         struct u64rec *p = &prev->cpu[i];
329
330                         pps = calc_pps_u64(r, p, t);
331                         if (pps > 0)
332                                 printf(fmt1, "Exception", i,
333                                        0.0, pps, err2str(rec_i));
334                 }
335                 pps = calc_pps_u64(&rec->total, &prev->total, t);
336                 if (pps > 0)
337                         printf(fmt2, "Exception", "total",
338                                0.0, pps, action2str(rec_i));
339         }
340
341         /* cpumap enqueue stats */
342         for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) {
343                 char *fmt1 = "%-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
344                 char *fmt2 = "%-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
345                 struct record *rec, *prev;
346                 char *info_str = "";
347                 double drop, info;
348
349                 rec  =  &stats_rec->xdp_cpumap_enqueue[to_cpu];
350                 prev = &stats_prev->xdp_cpumap_enqueue[to_cpu];
351                 t = calc_period(rec, prev);
352                 for (i = 0; i < nr_cpus; i++) {
353                         struct datarec *r = &rec->cpu[i];
354                         struct datarec *p = &prev->cpu[i];
355
356                         pps  = calc_pps(r, p, t);
357                         drop = calc_drop(r, p, t);
358                         info = calc_info(r, p, t);
359                         if (info > 0) {
360                                 info_str = "bulk-average";
361                                 info = pps / info; /* calc average bulk size */
362                         }
363                         if (pps > 0)
364                                 printf(fmt1, "cpumap-enqueue",
365                                        i, to_cpu, pps, drop, info, info_str);
366                 }
367                 pps = calc_pps(&rec->total, &prev->total, t);
368                 if (pps > 0) {
369                         drop = calc_drop(&rec->total, &prev->total, t);
370                         info = calc_info(&rec->total, &prev->total, t);
371                         if (info > 0) {
372                                 info_str = "bulk-average";
373                                 info = pps / info; /* calc average bulk size */
374                         }
375                         printf(fmt2, "cpumap-enqueue",
376                                "sum", to_cpu, pps, drop, info, info_str);
377                 }
378         }
379
380         /* cpumap kthread stats */
381         {
382                 char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s\n";
383                 char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s\n";
384                 struct record *rec, *prev;
385                 double drop, info;
386                 char *i_str = "";
387
388                 rec  =  &stats_rec->xdp_cpumap_kthread;
389                 prev = &stats_prev->xdp_cpumap_kthread;
390                 t = calc_period(rec, prev);
391                 for (i = 0; i < nr_cpus; i++) {
392                         struct datarec *r = &rec->cpu[i];
393                         struct datarec *p = &prev->cpu[i];
394
395                         pps  = calc_pps(r, p, t);
396                         drop = calc_drop(r, p, t);
397                         info = calc_info(r, p, t);
398                         if (info > 0)
399                                 i_str = "sched";
400                         if (pps > 0)
401                                 printf(fmt1, "cpumap-kthread",
402                                        i, pps, drop, info, i_str);
403                 }
404                 pps = calc_pps(&rec->total, &prev->total, t);
405                 drop = calc_drop(&rec->total, &prev->total, t);
406                 info = calc_info(&rec->total, &prev->total, t);
407                 if (info > 0)
408                         i_str = "sched-sum";
409                 printf(fmt2, "cpumap-kthread", "total", pps, drop, info, i_str);
410         }
411
412         printf("\n");
413 }
414
415 static bool stats_collect(struct stats_record *rec)
416 {
417         int fd;
418         int i;
419
420         /* TODO: Detect if someone unloaded the perf event_fd's, as
421          * this can happen by someone running perf-record -e
422          */
423
424         fd = map_data[0].fd; /* map0: redirect_err_cnt */
425         for (i = 0; i < REDIR_RES_MAX; i++)
426                 map_collect_record_u64(fd, i, &rec->xdp_redirect[i]);
427
428         fd = map_data[1].fd; /* map1: exception_cnt */
429         for (i = 0; i < XDP_ACTION_MAX; i++) {
430                 map_collect_record_u64(fd, i, &rec->xdp_exception[i]);
431         }
432
433         fd = map_data[2].fd; /* map2: cpumap_enqueue_cnt */
434         for (i = 0; i < MAX_CPUS; i++)
435                 map_collect_record(fd, i, &rec->xdp_cpumap_enqueue[i]);
436
437         fd = map_data[3].fd; /* map3: cpumap_kthread_cnt */
438         map_collect_record(fd, 0, &rec->xdp_cpumap_kthread);
439
440         return true;
441 }
442
443 static void *alloc_rec_per_cpu(int record_size)
444 {
445         unsigned int nr_cpus = bpf_num_possible_cpus();
446         void *array;
447         size_t size;
448
449         size = record_size * nr_cpus;
450         array = malloc(size);
451         memset(array, 0, size);
452         if (!array) {
453                 fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
454                 exit(EXIT_FAIL_MEM);
455         }
456         return array;
457 }
458
459 static struct stats_record *alloc_stats_record(void)
460 {
461         struct stats_record *rec;
462         int rec_sz;
463         int i;
464
465         /* Alloc main stats_record structure */
466         rec = malloc(sizeof(*rec));
467         memset(rec, 0, sizeof(*rec));
468         if (!rec) {
469                 fprintf(stderr, "Mem alloc error\n");
470                 exit(EXIT_FAIL_MEM);
471         }
472
473         /* Alloc stats stored per CPU for each record */
474         rec_sz = sizeof(struct u64rec);
475         for (i = 0; i < REDIR_RES_MAX; i++)
476                 rec->xdp_redirect[i].cpu = alloc_rec_per_cpu(rec_sz);
477
478         for (i = 0; i < XDP_ACTION_MAX; i++)
479                 rec->xdp_exception[i].cpu = alloc_rec_per_cpu(rec_sz);
480
481         rec_sz = sizeof(struct datarec);
482         rec->xdp_cpumap_kthread.cpu = alloc_rec_per_cpu(rec_sz);
483
484         for (i = 0; i < MAX_CPUS; i++)
485                 rec->xdp_cpumap_enqueue[i].cpu = alloc_rec_per_cpu(rec_sz);
486
487         return rec;
488 }
489
490 static void free_stats_record(struct stats_record *r)
491 {
492         int i;
493
494         for (i = 0; i < REDIR_RES_MAX; i++)
495                 free(r->xdp_redirect[i].cpu);
496
497         for (i = 0; i < XDP_ACTION_MAX; i++)
498                 free(r->xdp_exception[i].cpu);
499
500         free(r->xdp_cpumap_kthread.cpu);
501
502         for (i = 0; i < MAX_CPUS; i++)
503                 free(r->xdp_cpumap_enqueue[i].cpu);
504
505         free(r);
506 }
507
508 /* Pointer swap trick */
509 static inline void swap(struct stats_record **a, struct stats_record **b)
510 {
511         struct stats_record *tmp;
512
513         tmp = *a;
514         *a = *b;
515         *b = tmp;
516 }
517
518 static void stats_poll(int interval, bool err_only)
519 {
520         struct stats_record *rec, *prev;
521
522         rec  = alloc_stats_record();
523         prev = alloc_stats_record();
524         stats_collect(rec);
525
526         if (err_only)
527                 printf("\n%s\n", __doc_err_only__);
528
529         /* Trick to pretty printf with thousands separators use %' */
530         setlocale(LC_NUMERIC, "en_US");
531
532         /* Header */
533         if (verbose)
534                 printf("\n%s", __doc__);
535
536         /* TODO Need more advanced stats on error types */
537         if (verbose) {
538                 printf(" - Stats map0: %s\n", map_data[0].name);
539                 printf(" - Stats map1: %s\n", map_data[1].name);
540                 printf("\n");
541         }
542         fflush(stdout);
543
544         while (1) {
545                 swap(&prev, &rec);
546                 stats_collect(rec);
547                 stats_print(rec, prev, err_only);
548                 fflush(stdout);
549                 sleep(interval);
550         }
551
552         free_stats_record(rec);
553         free_stats_record(prev);
554 }
555
556 static void print_bpf_prog_info(void)
557 {
558         int i;
559
560         /* Prog info */
561         printf("Loaded BPF prog have %d bpf program(s)\n", prog_cnt);
562         for (i = 0; i < prog_cnt; i++) {
563                 printf(" - prog_fd[%d] = fd(%d)\n", i, prog_fd[i]);
564         }
565
566         /* Maps info */
567         printf("Loaded BPF prog have %d map(s)\n", map_data_count);
568         for (i = 0; i < map_data_count; i++) {
569                 char *name = map_data[i].name;
570                 int fd     = map_data[i].fd;
571
572                 printf(" - map_data[%d] = fd(%d) name:%s\n", i, fd, name);
573         }
574
575         /* Event info */
576         printf("Searching for (max:%d) event file descriptor(s)\n", prog_cnt);
577         for (i = 0; i < prog_cnt; i++) {
578                 if (event_fd[i] != -1)
579                         printf(" - event_fd[%d] = fd(%d)\n", i, event_fd[i]);
580         }
581 }
582
583 int main(int argc, char **argv)
584 {
585         struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
586         int longindex = 0, opt;
587         int ret = EXIT_SUCCESS;
588         char bpf_obj_file[256];
589
590         /* Default settings: */
591         bool errors_only = true;
592         int interval = 2;
593
594         snprintf(bpf_obj_file, sizeof(bpf_obj_file), "%s_kern.o", argv[0]);
595
596         /* Parse commands line args */
597         while ((opt = getopt_long(argc, argv, "h",
598                                   long_options, &longindex)) != -1) {
599                 switch (opt) {
600                 case 'D':
601                         debug = true;
602                         break;
603                 case 'S':
604                         errors_only = false;
605                         break;
606                 case 's':
607                         interval = atoi(optarg);
608                         break;
609                 case 'h':
610                 default:
611                         usage(argv);
612                         return EXIT_FAILURE;
613                 }
614         }
615
616         if (setrlimit(RLIMIT_MEMLOCK, &r)) {
617                 perror("setrlimit(RLIMIT_MEMLOCK)");
618                 return EXIT_FAILURE;
619         }
620
621         if (load_bpf_file(bpf_obj_file)) {
622                 printf("ERROR - bpf_log_buf: %s", bpf_log_buf);
623                 return EXIT_FAILURE;
624         }
625         if (!prog_fd[0]) {
626                 printf("ERROR - load_bpf_file: %s\n", strerror(errno));
627                 return EXIT_FAILURE;
628         }
629
630         if (debug) {
631                 print_bpf_prog_info();
632         }
633
634         /* Unload/stop tracepoint event by closing fd's */
635         if (errors_only) {
636                 /* The prog_fd[i] and event_fd[i] depend on the
637                  * order the functions was defined in _kern.c
638                  */
639                 close(event_fd[2]); /* tracepoint/xdp/xdp_redirect */
640                 close(prog_fd[2]);  /* func: trace_xdp_redirect */
641                 close(event_fd[3]); /* tracepoint/xdp/xdp_redirect_map */
642                 close(prog_fd[3]);  /* func: trace_xdp_redirect_map */
643         }
644
645         stats_poll(interval, errors_only);
646
647         return ret;
648 }