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