Merge tag 'drm-fixes-2018-07-06' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-microblaze.git] / samples / bpf / xdp_rxq_info_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__ = " XDP RX-queue info extract example\n\n"
5         "Monitor how many packets per sec (pps) are received\n"
6         "per NIC RX queue index and which CPU processed the packet\n"
7         ;
8
9 #include <errno.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdbool.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <locale.h>
17 #include <sys/resource.h>
18 #include <getopt.h>
19 #include <net/if.h>
20 #include <time.h>
21
22 #include <arpa/inet.h>
23 #include <linux/if_link.h>
24
25 #include "bpf/bpf.h"
26 #include "bpf/libbpf.h"
27 #include "bpf_util.h"
28
29 static int ifindex = -1;
30 static char ifname_buf[IF_NAMESIZE];
31 static char *ifname;
32
33 static __u32 xdp_flags;
34
35 static struct bpf_map *stats_global_map;
36 static struct bpf_map *rx_queue_index_map;
37
38 /* Exit return codes */
39 #define EXIT_OK         0
40 #define EXIT_FAIL               1
41 #define EXIT_FAIL_OPTION        2
42 #define EXIT_FAIL_XDP           3
43 #define EXIT_FAIL_BPF           4
44 #define EXIT_FAIL_MEM           5
45
46 static const struct option long_options[] = {
47         {"help",        no_argument,            NULL, 'h' },
48         {"dev",         required_argument,      NULL, 'd' },
49         {"skb-mode",    no_argument,            NULL, 'S' },
50         {"sec",         required_argument,      NULL, 's' },
51         {"no-separators", no_argument,          NULL, 'z' },
52         {"action",      required_argument,      NULL, 'a' },
53         {0, 0, NULL,  0 }
54 };
55
56 static void int_exit(int sig)
57 {
58         fprintf(stderr,
59                 "Interrupted: Removing XDP program on ifindex:%d device:%s\n",
60                 ifindex, ifname);
61         if (ifindex > -1)
62                 bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
63         exit(EXIT_OK);
64 }
65
66 struct config {
67         __u32 action;
68         int ifindex;
69 };
70 #define XDP_ACTION_MAX (XDP_TX + 1)
71 #define XDP_ACTION_MAX_STRLEN 11
72 static const char *xdp_action_names[XDP_ACTION_MAX] = {
73         [XDP_ABORTED]   = "XDP_ABORTED",
74         [XDP_DROP]      = "XDP_DROP",
75         [XDP_PASS]      = "XDP_PASS",
76         [XDP_TX]        = "XDP_TX",
77 };
78
79 static const char *action2str(int action)
80 {
81         if (action < XDP_ACTION_MAX)
82                 return xdp_action_names[action];
83         return NULL;
84 }
85
86 static int parse_xdp_action(char *action_str)
87 {
88         size_t maxlen;
89         __u64 action = -1;
90         int i;
91
92         for (i = 0; i < XDP_ACTION_MAX; i++) {
93                 maxlen = XDP_ACTION_MAX_STRLEN;
94                 if (strncmp(xdp_action_names[i], action_str, maxlen) == 0) {
95                         action = i;
96                         break;
97                 }
98         }
99         return action;
100 }
101
102 static void list_xdp_actions(void)
103 {
104         int i;
105
106         printf("Available XDP --action <options>\n");
107         for (i = 0; i < XDP_ACTION_MAX; i++)
108                 printf("\t%s\n", xdp_action_names[i]);
109         printf("\n");
110 }
111
112 static void usage(char *argv[])
113 {
114         int i;
115
116         printf("\nDOCUMENTATION:\n%s\n", __doc__);
117         printf(" Usage: %s (options-see-below)\n", argv[0]);
118         printf(" Listing options:\n");
119         for (i = 0; long_options[i].name != 0; i++) {
120                 printf(" --%-12s", long_options[i].name);
121                 if (long_options[i].flag != NULL)
122                         printf(" flag (internal value:%d)",
123                                 *long_options[i].flag);
124                 else
125                         printf(" short-option: -%c",
126                                 long_options[i].val);
127                 printf("\n");
128         }
129         printf("\n");
130         list_xdp_actions();
131 }
132
133 #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
134 static __u64 gettime(void)
135 {
136         struct timespec t;
137         int res;
138
139         res = clock_gettime(CLOCK_MONOTONIC, &t);
140         if (res < 0) {
141                 fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
142                 exit(EXIT_FAIL);
143         }
144         return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
145 }
146
147 /* Common stats data record shared with _kern.c */
148 struct datarec {
149         __u64 processed;
150         __u64 issue;
151 };
152 struct record {
153         __u64 timestamp;
154         struct datarec total;
155         struct datarec *cpu;
156 };
157 struct stats_record {
158         struct record stats;
159         struct record *rxq;
160 };
161
162 static struct datarec *alloc_record_per_cpu(void)
163 {
164         unsigned int nr_cpus = bpf_num_possible_cpus();
165         struct datarec *array;
166         size_t size;
167
168         size = sizeof(struct datarec) * nr_cpus;
169         array = malloc(size);
170         memset(array, 0, size);
171         if (!array) {
172                 fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
173                 exit(EXIT_FAIL_MEM);
174         }
175         return array;
176 }
177
178 static struct record *alloc_record_per_rxq(void)
179 {
180         unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
181         struct record *array;
182         size_t size;
183
184         size = sizeof(struct record) * nr_rxqs;
185         array = malloc(size);
186         memset(array, 0, size);
187         if (!array) {
188                 fprintf(stderr, "Mem alloc error (nr_rxqs:%u)\n", nr_rxqs);
189                 exit(EXIT_FAIL_MEM);
190         }
191         return array;
192 }
193
194 static struct stats_record *alloc_stats_record(void)
195 {
196         unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
197         struct stats_record *rec;
198         int i;
199
200         rec = malloc(sizeof(*rec));
201         memset(rec, 0, sizeof(*rec));
202         if (!rec) {
203                 fprintf(stderr, "Mem alloc error\n");
204                 exit(EXIT_FAIL_MEM);
205         }
206         rec->rxq = alloc_record_per_rxq();
207         for (i = 0; i < nr_rxqs; i++)
208                 rec->rxq[i].cpu = alloc_record_per_cpu();
209
210         rec->stats.cpu = alloc_record_per_cpu();
211         return rec;
212 }
213
214 static void free_stats_record(struct stats_record *r)
215 {
216         unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
217         int i;
218
219         for (i = 0; i < nr_rxqs; i++)
220                 free(r->rxq[i].cpu);
221
222         free(r->rxq);
223         free(r->stats.cpu);
224         free(r);
225 }
226
227 static bool map_collect_percpu(int fd, __u32 key, struct record *rec)
228 {
229         /* For percpu maps, userspace gets a value per possible CPU */
230         unsigned int nr_cpus = bpf_num_possible_cpus();
231         struct datarec values[nr_cpus];
232         __u64 sum_processed = 0;
233         __u64 sum_issue = 0;
234         int i;
235
236         if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
237                 fprintf(stderr,
238                         "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
239                 return false;
240         }
241         /* Get time as close as possible to reading map contents */
242         rec->timestamp = gettime();
243
244         /* Record and sum values from each CPU */
245         for (i = 0; i < nr_cpus; i++) {
246                 rec->cpu[i].processed = values[i].processed;
247                 sum_processed        += values[i].processed;
248                 rec->cpu[i].issue = values[i].issue;
249                 sum_issue        += values[i].issue;
250         }
251         rec->total.processed = sum_processed;
252         rec->total.issue     = sum_issue;
253         return true;
254 }
255
256 static void stats_collect(struct stats_record *rec)
257 {
258         int fd, i, max_rxqs;
259
260         fd = bpf_map__fd(stats_global_map);
261         map_collect_percpu(fd, 0, &rec->stats);
262
263         fd = bpf_map__fd(rx_queue_index_map);
264         max_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
265         for (i = 0; i < max_rxqs; i++)
266                 map_collect_percpu(fd, i, &rec->rxq[i]);
267 }
268
269 static double calc_period(struct record *r, struct record *p)
270 {
271         double period_ = 0;
272         __u64 period = 0;
273
274         period = r->timestamp - p->timestamp;
275         if (period > 0)
276                 period_ = ((double) period / NANOSEC_PER_SEC);
277
278         return period_;
279 }
280
281 static __u64 calc_pps(struct datarec *r, struct datarec *p, double period_)
282 {
283         __u64 packets = 0;
284         __u64 pps = 0;
285
286         if (period_ > 0) {
287                 packets = r->processed - p->processed;
288                 pps = packets / period_;
289         }
290         return pps;
291 }
292
293 static __u64 calc_errs_pps(struct datarec *r,
294                             struct datarec *p, double period_)
295 {
296         __u64 packets = 0;
297         __u64 pps = 0;
298
299         if (period_ > 0) {
300                 packets = r->issue - p->issue;
301                 pps = packets / period_;
302         }
303         return pps;
304 }
305
306 static void stats_print(struct stats_record *stats_rec,
307                         struct stats_record *stats_prev,
308                         int action)
309 {
310         unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
311         unsigned int nr_cpus = bpf_num_possible_cpus();
312         double pps = 0, err = 0;
313         struct record *rec, *prev;
314         double t;
315         int rxq;
316         int i;
317
318         /* Header */
319         printf("\nRunning XDP on dev:%s (ifindex:%d) action:%s\n",
320                ifname, ifindex, action2str(action));
321
322         /* stats_global_map */
323         {
324                 char *fmt_rx = "%-15s %-7d %'-11.0f %'-10.0f %s\n";
325                 char *fm2_rx = "%-15s %-7s %'-11.0f\n";
326                 char *errstr = "";
327
328                 printf("%-15s %-7s %-11s %-11s\n",
329                        "XDP stats", "CPU", "pps", "issue-pps");
330
331                 rec  =  &stats_rec->stats;
332                 prev = &stats_prev->stats;
333                 t = calc_period(rec, prev);
334                 for (i = 0; i < nr_cpus; i++) {
335                         struct datarec *r = &rec->cpu[i];
336                         struct datarec *p = &prev->cpu[i];
337
338                         pps = calc_pps     (r, p, t);
339                         err = calc_errs_pps(r, p, t);
340                         if (err > 0)
341                                 errstr = "invalid-ifindex";
342                         if (pps > 0)
343                                 printf(fmt_rx, "XDP-RX CPU",
344                                         i, pps, err, errstr);
345                 }
346                 pps  = calc_pps     (&rec->total, &prev->total, t);
347                 err  = calc_errs_pps(&rec->total, &prev->total, t);
348                 printf(fm2_rx, "XDP-RX CPU", "total", pps, err);
349         }
350
351         /* rx_queue_index_map */
352         printf("\n%-15s %-7s %-11s %-11s\n",
353                "RXQ stats", "RXQ:CPU", "pps", "issue-pps");
354
355         for (rxq = 0; rxq < nr_rxqs; rxq++) {
356                 char *fmt_rx = "%-15s %3d:%-3d %'-11.0f %'-10.0f %s\n";
357                 char *fm2_rx = "%-15s %3d:%-3s %'-11.0f\n";
358                 char *errstr = "";
359                 int rxq_ = rxq;
360
361                 /* Last RXQ in map catch overflows */
362                 if (rxq_ == nr_rxqs - 1)
363                         rxq_ = -1;
364
365                 rec  =  &stats_rec->rxq[rxq];
366                 prev = &stats_prev->rxq[rxq];
367                 t = calc_period(rec, prev);
368                 for (i = 0; i < nr_cpus; i++) {
369                         struct datarec *r = &rec->cpu[i];
370                         struct datarec *p = &prev->cpu[i];
371
372                         pps = calc_pps     (r, p, t);
373                         err = calc_errs_pps(r, p, t);
374                         if (err > 0) {
375                                 if (rxq_ == -1)
376                                         errstr = "map-overflow-RXQ";
377                                 else
378                                         errstr = "err";
379                         }
380                         if (pps > 0)
381                                 printf(fmt_rx, "rx_queue_index",
382                                        rxq_, i, pps, err, errstr);
383                 }
384                 pps  = calc_pps     (&rec->total, &prev->total, t);
385                 err  = calc_errs_pps(&rec->total, &prev->total, t);
386                 if (pps || err)
387                         printf(fm2_rx, "rx_queue_index", rxq_, "sum", pps, err);
388         }
389 }
390
391
392 /* Pointer swap trick */
393 static inline void swap(struct stats_record **a, struct stats_record **b)
394 {
395         struct stats_record *tmp;
396
397         tmp = *a;
398         *a = *b;
399         *b = tmp;
400 }
401
402 static void stats_poll(int interval, int action)
403 {
404         struct stats_record *record, *prev;
405
406         record = alloc_stats_record();
407         prev   = alloc_stats_record();
408         stats_collect(record);
409
410         while (1) {
411                 swap(&prev, &record);
412                 stats_collect(record);
413                 stats_print(record, prev, action);
414                 sleep(interval);
415         }
416
417         free_stats_record(record);
418         free_stats_record(prev);
419 }
420
421
422 int main(int argc, char **argv)
423 {
424         struct rlimit r = {10 * 1024 * 1024, RLIM_INFINITY};
425         struct bpf_prog_load_attr prog_load_attr = {
426                 .prog_type      = BPF_PROG_TYPE_XDP,
427         };
428         int prog_fd, map_fd, opt, err;
429         bool use_separators = true;
430         struct config cfg = { 0 };
431         struct bpf_object *obj;
432         struct bpf_map *map;
433         char filename[256];
434         int longindex = 0;
435         int interval = 2;
436         __u32 key = 0;
437
438         char action_str_buf[XDP_ACTION_MAX_STRLEN + 1 /* for \0 */] = { 0 };
439         int action = XDP_PASS; /* Default action */
440         char *action_str = NULL;
441
442         snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
443         prog_load_attr.file = filename;
444
445         if (setrlimit(RLIMIT_MEMLOCK, &r)) {
446                 perror("setrlimit(RLIMIT_MEMLOCK)");
447                 return 1;
448         }
449
450         if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
451                 return EXIT_FAIL;
452
453         map = bpf_map__next(NULL, obj);
454         stats_global_map = bpf_map__next(map, obj);
455         rx_queue_index_map = bpf_map__next(stats_global_map, obj);
456         if (!map || !stats_global_map || !rx_queue_index_map) {
457                 printf("finding a map in obj file failed\n");
458                 return EXIT_FAIL;
459         }
460         map_fd = bpf_map__fd(map);
461
462         if (!prog_fd) {
463                 fprintf(stderr, "ERR: load_bpf_file: %s\n", strerror(errno));
464                 return EXIT_FAIL;
465         }
466
467         /* Parse commands line args */
468         while ((opt = getopt_long(argc, argv, "hSd:",
469                                   long_options, &longindex)) != -1) {
470                 switch (opt) {
471                 case 'd':
472                         if (strlen(optarg) >= IF_NAMESIZE) {
473                                 fprintf(stderr, "ERR: --dev name too long\n");
474                                 goto error;
475                         }
476                         ifname = (char *)&ifname_buf;
477                         strncpy(ifname, optarg, IF_NAMESIZE);
478                         ifindex = if_nametoindex(ifname);
479                         if (ifindex == 0) {
480                                 fprintf(stderr,
481                                         "ERR: --dev name unknown err(%d):%s\n",
482                                         errno, strerror(errno));
483                                 goto error;
484                         }
485                         break;
486                 case 's':
487                         interval = atoi(optarg);
488                         break;
489                 case 'S':
490                         xdp_flags |= XDP_FLAGS_SKB_MODE;
491                         break;
492                 case 'z':
493                         use_separators = false;
494                         break;
495                 case 'a':
496                         action_str = (char *)&action_str_buf;
497                         strncpy(action_str, optarg, XDP_ACTION_MAX_STRLEN);
498                         break;
499                 case 'h':
500                 error:
501                 default:
502                         usage(argv);
503                         return EXIT_FAIL_OPTION;
504                 }
505         }
506         /* Required option */
507         if (ifindex == -1) {
508                 fprintf(stderr, "ERR: required option --dev missing\n");
509                 usage(argv);
510                 return EXIT_FAIL_OPTION;
511         }
512         cfg.ifindex = ifindex;
513
514         /* Parse action string */
515         if (action_str) {
516                 action = parse_xdp_action(action_str);
517                 if (action < 0) {
518                         fprintf(stderr, "ERR: Invalid XDP --action: %s\n",
519                                 action_str);
520                         list_xdp_actions();
521                         return EXIT_FAIL_OPTION;
522                 }
523         }
524         cfg.action = action;
525
526         /* Trick to pretty printf with thousands separators use %' */
527         if (use_separators)
528                 setlocale(LC_NUMERIC, "en_US");
529
530         /* User-side setup ifindex in config_map */
531         err = bpf_map_update_elem(map_fd, &key, &cfg, 0);
532         if (err) {
533                 fprintf(stderr, "Store config failed (err:%d)\n", err);
534                 exit(EXIT_FAIL_BPF);
535         }
536
537         /* Remove XDP program when program is interrupted */
538         signal(SIGINT, int_exit);
539
540         if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
541                 fprintf(stderr, "link set xdp fd failed\n");
542                 return EXIT_FAIL_XDP;
543         }
544
545         stats_poll(interval, action);
546         return EXIT_OK;
547 }