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

Configure Feed

Select the types of activity you want to include in your feed.

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