18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc. 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_cistatic const char *__doc__ = " XDP RX-queue info extract example\n\n" 58c2ecf20Sopenharmony_ci "Monitor how many packets per sec (pps) are received\n" 68c2ecf20Sopenharmony_ci "per NIC RX queue index and which CPU processed the packet\n" 78c2ecf20Sopenharmony_ci ; 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <errno.h> 108c2ecf20Sopenharmony_ci#include <signal.h> 118c2ecf20Sopenharmony_ci#include <stdio.h> 128c2ecf20Sopenharmony_ci#include <stdlib.h> 138c2ecf20Sopenharmony_ci#include <stdbool.h> 148c2ecf20Sopenharmony_ci#include <string.h> 158c2ecf20Sopenharmony_ci#include <unistd.h> 168c2ecf20Sopenharmony_ci#include <locale.h> 178c2ecf20Sopenharmony_ci#include <sys/resource.h> 188c2ecf20Sopenharmony_ci#include <getopt.h> 198c2ecf20Sopenharmony_ci#include <net/if.h> 208c2ecf20Sopenharmony_ci#include <time.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <arpa/inet.h> 238c2ecf20Sopenharmony_ci#include <linux/if_link.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <bpf/bpf.h> 268c2ecf20Sopenharmony_ci#include <bpf/libbpf.h> 278c2ecf20Sopenharmony_ci#include "bpf_util.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int ifindex = -1; 308c2ecf20Sopenharmony_cistatic char ifname_buf[IF_NAMESIZE]; 318c2ecf20Sopenharmony_cistatic char *ifname; 328c2ecf20Sopenharmony_cistatic __u32 prog_id; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct bpf_map *stats_global_map; 378c2ecf20Sopenharmony_cistatic struct bpf_map *rx_queue_index_map; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Exit return codes */ 408c2ecf20Sopenharmony_ci#define EXIT_OK 0 418c2ecf20Sopenharmony_ci#define EXIT_FAIL 1 428c2ecf20Sopenharmony_ci#define EXIT_FAIL_OPTION 2 438c2ecf20Sopenharmony_ci#define EXIT_FAIL_XDP 3 448c2ecf20Sopenharmony_ci#define EXIT_FAIL_BPF 4 458c2ecf20Sopenharmony_ci#define EXIT_FAIL_MEM 5 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const struct option long_options[] = { 488c2ecf20Sopenharmony_ci {"help", no_argument, NULL, 'h' }, 498c2ecf20Sopenharmony_ci {"dev", required_argument, NULL, 'd' }, 508c2ecf20Sopenharmony_ci {"skb-mode", no_argument, NULL, 'S' }, 518c2ecf20Sopenharmony_ci {"sec", required_argument, NULL, 's' }, 528c2ecf20Sopenharmony_ci {"no-separators", no_argument, NULL, 'z' }, 538c2ecf20Sopenharmony_ci {"action", required_argument, NULL, 'a' }, 548c2ecf20Sopenharmony_ci {"readmem", no_argument, NULL, 'r' }, 558c2ecf20Sopenharmony_ci {"swapmac", no_argument, NULL, 'm' }, 568c2ecf20Sopenharmony_ci {"force", no_argument, NULL, 'F' }, 578c2ecf20Sopenharmony_ci {0, 0, NULL, 0 } 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void int_exit(int sig) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci __u32 curr_prog_id = 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (ifindex > -1) { 658c2ecf20Sopenharmony_ci if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { 668c2ecf20Sopenharmony_ci printf("bpf_get_link_xdp_id failed\n"); 678c2ecf20Sopenharmony_ci exit(EXIT_FAIL); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci if (prog_id == curr_prog_id) { 708c2ecf20Sopenharmony_ci fprintf(stderr, 718c2ecf20Sopenharmony_ci "Interrupted: Removing XDP program on ifindex:%d device:%s\n", 728c2ecf20Sopenharmony_ci ifindex, ifname); 738c2ecf20Sopenharmony_ci bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); 748c2ecf20Sopenharmony_ci } else if (!curr_prog_id) { 758c2ecf20Sopenharmony_ci printf("couldn't find a prog id on a given iface\n"); 768c2ecf20Sopenharmony_ci } else { 778c2ecf20Sopenharmony_ci printf("program on interface changed, not removing\n"); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci exit(EXIT_OK); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct config { 848c2ecf20Sopenharmony_ci __u32 action; 858c2ecf20Sopenharmony_ci int ifindex; 868c2ecf20Sopenharmony_ci __u32 options; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_cienum cfg_options_flags { 898c2ecf20Sopenharmony_ci NO_TOUCH = 0x0U, 908c2ecf20Sopenharmony_ci READ_MEM = 0x1U, 918c2ecf20Sopenharmony_ci SWAP_MAC = 0x2U, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci#define XDP_ACTION_MAX (XDP_TX + 1) 948c2ecf20Sopenharmony_ci#define XDP_ACTION_MAX_STRLEN 11 958c2ecf20Sopenharmony_cistatic const char *xdp_action_names[XDP_ACTION_MAX] = { 968c2ecf20Sopenharmony_ci [XDP_ABORTED] = "XDP_ABORTED", 978c2ecf20Sopenharmony_ci [XDP_DROP] = "XDP_DROP", 988c2ecf20Sopenharmony_ci [XDP_PASS] = "XDP_PASS", 998c2ecf20Sopenharmony_ci [XDP_TX] = "XDP_TX", 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const char *action2str(int action) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci if (action < XDP_ACTION_MAX) 1058c2ecf20Sopenharmony_ci return xdp_action_names[action]; 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int parse_xdp_action(char *action_str) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci size_t maxlen; 1128c2ecf20Sopenharmony_ci __u64 action = -1; 1138c2ecf20Sopenharmony_ci int i; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0; i < XDP_ACTION_MAX; i++) { 1168c2ecf20Sopenharmony_ci maxlen = XDP_ACTION_MAX_STRLEN; 1178c2ecf20Sopenharmony_ci if (strncmp(xdp_action_names[i], action_str, maxlen) == 0) { 1188c2ecf20Sopenharmony_ci action = i; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci return action; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void list_xdp_actions(void) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int i; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci printf("Available XDP --action <options>\n"); 1308c2ecf20Sopenharmony_ci for (i = 0; i < XDP_ACTION_MAX; i++) 1318c2ecf20Sopenharmony_ci printf("\t%s\n", xdp_action_names[i]); 1328c2ecf20Sopenharmony_ci printf("\n"); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic char* options2str(enum cfg_options_flags flag) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci if (flag == NO_TOUCH) 1388c2ecf20Sopenharmony_ci return "no_touch"; 1398c2ecf20Sopenharmony_ci if (flag & SWAP_MAC) 1408c2ecf20Sopenharmony_ci return "swapmac"; 1418c2ecf20Sopenharmony_ci if (flag & READ_MEM) 1428c2ecf20Sopenharmony_ci return "read"; 1438c2ecf20Sopenharmony_ci fprintf(stderr, "ERR: Unknown config option flags"); 1448c2ecf20Sopenharmony_ci exit(EXIT_FAIL); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void usage(char *argv[]) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci int i; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci printf("\nDOCUMENTATION:\n%s\n", __doc__); 1528c2ecf20Sopenharmony_ci printf(" Usage: %s (options-see-below)\n", argv[0]); 1538c2ecf20Sopenharmony_ci printf(" Listing options:\n"); 1548c2ecf20Sopenharmony_ci for (i = 0; long_options[i].name != 0; i++) { 1558c2ecf20Sopenharmony_ci printf(" --%-12s", long_options[i].name); 1568c2ecf20Sopenharmony_ci if (long_options[i].flag != NULL) 1578c2ecf20Sopenharmony_ci printf(" flag (internal value:%d)", 1588c2ecf20Sopenharmony_ci *long_options[i].flag); 1598c2ecf20Sopenharmony_ci else 1608c2ecf20Sopenharmony_ci printf(" short-option: -%c", 1618c2ecf20Sopenharmony_ci long_options[i].val); 1628c2ecf20Sopenharmony_ci printf("\n"); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci printf("\n"); 1658c2ecf20Sopenharmony_ci list_xdp_actions(); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define NANOSEC_PER_SEC 1000000000 /* 10^9 */ 1698c2ecf20Sopenharmony_cistatic __u64 gettime(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct timespec t; 1728c2ecf20Sopenharmony_ci int res; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci res = clock_gettime(CLOCK_MONOTONIC, &t); 1758c2ecf20Sopenharmony_ci if (res < 0) { 1768c2ecf20Sopenharmony_ci fprintf(stderr, "Error with gettimeofday! (%i)\n", res); 1778c2ecf20Sopenharmony_ci exit(EXIT_FAIL); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* Common stats data record shared with _kern.c */ 1838c2ecf20Sopenharmony_cistruct datarec { 1848c2ecf20Sopenharmony_ci __u64 processed; 1858c2ecf20Sopenharmony_ci __u64 issue; 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_cistruct record { 1888c2ecf20Sopenharmony_ci __u64 timestamp; 1898c2ecf20Sopenharmony_ci struct datarec total; 1908c2ecf20Sopenharmony_ci struct datarec *cpu; 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_cistruct stats_record { 1938c2ecf20Sopenharmony_ci struct record stats; 1948c2ecf20Sopenharmony_ci struct record *rxq; 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic struct datarec *alloc_record_per_cpu(void) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci unsigned int nr_cpus = bpf_num_possible_cpus(); 2008c2ecf20Sopenharmony_ci struct datarec *array; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci array = calloc(nr_cpus, sizeof(struct datarec)); 2038c2ecf20Sopenharmony_ci if (!array) { 2048c2ecf20Sopenharmony_ci fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus); 2058c2ecf20Sopenharmony_ci exit(EXIT_FAIL_MEM); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci return array; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic struct record *alloc_record_per_rxq(void) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; 2138c2ecf20Sopenharmony_ci struct record *array; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci array = calloc(nr_rxqs, sizeof(struct record)); 2168c2ecf20Sopenharmony_ci if (!array) { 2178c2ecf20Sopenharmony_ci fprintf(stderr, "Mem alloc error (nr_rxqs:%u)\n", nr_rxqs); 2188c2ecf20Sopenharmony_ci exit(EXIT_FAIL_MEM); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci return array; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic struct stats_record *alloc_stats_record(void) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; 2268c2ecf20Sopenharmony_ci struct stats_record *rec; 2278c2ecf20Sopenharmony_ci int i; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci rec = calloc(1, sizeof(struct stats_record)); 2308c2ecf20Sopenharmony_ci if (!rec) { 2318c2ecf20Sopenharmony_ci fprintf(stderr, "Mem alloc error\n"); 2328c2ecf20Sopenharmony_ci exit(EXIT_FAIL_MEM); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci rec->rxq = alloc_record_per_rxq(); 2358c2ecf20Sopenharmony_ci for (i = 0; i < nr_rxqs; i++) 2368c2ecf20Sopenharmony_ci rec->rxq[i].cpu = alloc_record_per_cpu(); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rec->stats.cpu = alloc_record_per_cpu(); 2398c2ecf20Sopenharmony_ci return rec; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic void free_stats_record(struct stats_record *r) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; 2458c2ecf20Sopenharmony_ci int i; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci for (i = 0; i < nr_rxqs; i++) 2488c2ecf20Sopenharmony_ci free(r->rxq[i].cpu); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci free(r->rxq); 2518c2ecf20Sopenharmony_ci free(r->stats.cpu); 2528c2ecf20Sopenharmony_ci free(r); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic bool map_collect_percpu(int fd, __u32 key, struct record *rec) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci /* For percpu maps, userspace gets a value per possible CPU */ 2588c2ecf20Sopenharmony_ci unsigned int nr_cpus = bpf_num_possible_cpus(); 2598c2ecf20Sopenharmony_ci struct datarec values[nr_cpus]; 2608c2ecf20Sopenharmony_ci __u64 sum_processed = 0; 2618c2ecf20Sopenharmony_ci __u64 sum_issue = 0; 2628c2ecf20Sopenharmony_ci int i; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if ((bpf_map_lookup_elem(fd, &key, values)) != 0) { 2658c2ecf20Sopenharmony_ci fprintf(stderr, 2668c2ecf20Sopenharmony_ci "ERR: bpf_map_lookup_elem failed key:0x%X\n", key); 2678c2ecf20Sopenharmony_ci return false; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci /* Get time as close as possible to reading map contents */ 2708c2ecf20Sopenharmony_ci rec->timestamp = gettime(); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* Record and sum values from each CPU */ 2738c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpus; i++) { 2748c2ecf20Sopenharmony_ci rec->cpu[i].processed = values[i].processed; 2758c2ecf20Sopenharmony_ci sum_processed += values[i].processed; 2768c2ecf20Sopenharmony_ci rec->cpu[i].issue = values[i].issue; 2778c2ecf20Sopenharmony_ci sum_issue += values[i].issue; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci rec->total.processed = sum_processed; 2808c2ecf20Sopenharmony_ci rec->total.issue = sum_issue; 2818c2ecf20Sopenharmony_ci return true; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void stats_collect(struct stats_record *rec) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int fd, i, max_rxqs; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci fd = bpf_map__fd(stats_global_map); 2898c2ecf20Sopenharmony_ci map_collect_percpu(fd, 0, &rec->stats); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci fd = bpf_map__fd(rx_queue_index_map); 2928c2ecf20Sopenharmony_ci max_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; 2938c2ecf20Sopenharmony_ci for (i = 0; i < max_rxqs; i++) 2948c2ecf20Sopenharmony_ci map_collect_percpu(fd, i, &rec->rxq[i]); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic double calc_period(struct record *r, struct record *p) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci double period_ = 0; 3008c2ecf20Sopenharmony_ci __u64 period = 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci period = r->timestamp - p->timestamp; 3038c2ecf20Sopenharmony_ci if (period > 0) 3048c2ecf20Sopenharmony_ci period_ = ((double) period / NANOSEC_PER_SEC); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return period_; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic __u64 calc_pps(struct datarec *r, struct datarec *p, double period_) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci __u64 packets = 0; 3128c2ecf20Sopenharmony_ci __u64 pps = 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (period_ > 0) { 3158c2ecf20Sopenharmony_ci packets = r->processed - p->processed; 3168c2ecf20Sopenharmony_ci pps = packets / period_; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci return pps; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic __u64 calc_errs_pps(struct datarec *r, 3228c2ecf20Sopenharmony_ci struct datarec *p, double period_) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci __u64 packets = 0; 3258c2ecf20Sopenharmony_ci __u64 pps = 0; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (period_ > 0) { 3288c2ecf20Sopenharmony_ci packets = r->issue - p->issue; 3298c2ecf20Sopenharmony_ci pps = packets / period_; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci return pps; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void stats_print(struct stats_record *stats_rec, 3358c2ecf20Sopenharmony_ci struct stats_record *stats_prev, 3368c2ecf20Sopenharmony_ci int action, __u32 cfg_opt) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; 3398c2ecf20Sopenharmony_ci unsigned int nr_cpus = bpf_num_possible_cpus(); 3408c2ecf20Sopenharmony_ci double pps = 0, err = 0; 3418c2ecf20Sopenharmony_ci struct record *rec, *prev; 3428c2ecf20Sopenharmony_ci double t; 3438c2ecf20Sopenharmony_ci int rxq; 3448c2ecf20Sopenharmony_ci int i; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Header */ 3478c2ecf20Sopenharmony_ci printf("\nRunning XDP on dev:%s (ifindex:%d) action:%s options:%s\n", 3488c2ecf20Sopenharmony_ci ifname, ifindex, action2str(action), options2str(cfg_opt)); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* stats_global_map */ 3518c2ecf20Sopenharmony_ci { 3528c2ecf20Sopenharmony_ci char *fmt_rx = "%-15s %-7d %'-11.0f %'-10.0f %s\n"; 3538c2ecf20Sopenharmony_ci char *fm2_rx = "%-15s %-7s %'-11.0f\n"; 3548c2ecf20Sopenharmony_ci char *errstr = ""; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci printf("%-15s %-7s %-11s %-11s\n", 3578c2ecf20Sopenharmony_ci "XDP stats", "CPU", "pps", "issue-pps"); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci rec = &stats_rec->stats; 3608c2ecf20Sopenharmony_ci prev = &stats_prev->stats; 3618c2ecf20Sopenharmony_ci t = calc_period(rec, prev); 3628c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpus; i++) { 3638c2ecf20Sopenharmony_ci struct datarec *r = &rec->cpu[i]; 3648c2ecf20Sopenharmony_ci struct datarec *p = &prev->cpu[i]; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci pps = calc_pps (r, p, t); 3678c2ecf20Sopenharmony_ci err = calc_errs_pps(r, p, t); 3688c2ecf20Sopenharmony_ci if (err > 0) 3698c2ecf20Sopenharmony_ci errstr = "invalid-ifindex"; 3708c2ecf20Sopenharmony_ci if (pps > 0) 3718c2ecf20Sopenharmony_ci printf(fmt_rx, "XDP-RX CPU", 3728c2ecf20Sopenharmony_ci i, pps, err, errstr); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci pps = calc_pps (&rec->total, &prev->total, t); 3758c2ecf20Sopenharmony_ci err = calc_errs_pps(&rec->total, &prev->total, t); 3768c2ecf20Sopenharmony_ci printf(fm2_rx, "XDP-RX CPU", "total", pps, err); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* rx_queue_index_map */ 3808c2ecf20Sopenharmony_ci printf("\n%-15s %-7s %-11s %-11s\n", 3818c2ecf20Sopenharmony_ci "RXQ stats", "RXQ:CPU", "pps", "issue-pps"); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci for (rxq = 0; rxq < nr_rxqs; rxq++) { 3848c2ecf20Sopenharmony_ci char *fmt_rx = "%-15s %3d:%-3d %'-11.0f %'-10.0f %s\n"; 3858c2ecf20Sopenharmony_ci char *fm2_rx = "%-15s %3d:%-3s %'-11.0f\n"; 3868c2ecf20Sopenharmony_ci char *errstr = ""; 3878c2ecf20Sopenharmony_ci int rxq_ = rxq; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Last RXQ in map catch overflows */ 3908c2ecf20Sopenharmony_ci if (rxq_ == nr_rxqs - 1) 3918c2ecf20Sopenharmony_ci rxq_ = -1; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci rec = &stats_rec->rxq[rxq]; 3948c2ecf20Sopenharmony_ci prev = &stats_prev->rxq[rxq]; 3958c2ecf20Sopenharmony_ci t = calc_period(rec, prev); 3968c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpus; i++) { 3978c2ecf20Sopenharmony_ci struct datarec *r = &rec->cpu[i]; 3988c2ecf20Sopenharmony_ci struct datarec *p = &prev->cpu[i]; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci pps = calc_pps (r, p, t); 4018c2ecf20Sopenharmony_ci err = calc_errs_pps(r, p, t); 4028c2ecf20Sopenharmony_ci if (err > 0) { 4038c2ecf20Sopenharmony_ci if (rxq_ == -1) 4048c2ecf20Sopenharmony_ci errstr = "map-overflow-RXQ"; 4058c2ecf20Sopenharmony_ci else 4068c2ecf20Sopenharmony_ci errstr = "err"; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci if (pps > 0) 4098c2ecf20Sopenharmony_ci printf(fmt_rx, "rx_queue_index", 4108c2ecf20Sopenharmony_ci rxq_, i, pps, err, errstr); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci pps = calc_pps (&rec->total, &prev->total, t); 4138c2ecf20Sopenharmony_ci err = calc_errs_pps(&rec->total, &prev->total, t); 4148c2ecf20Sopenharmony_ci if (pps || err) 4158c2ecf20Sopenharmony_ci printf(fm2_rx, "rx_queue_index", rxq_, "sum", pps, err); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/* Pointer swap trick */ 4218c2ecf20Sopenharmony_cistatic inline void swap(struct stats_record **a, struct stats_record **b) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct stats_record *tmp; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci tmp = *a; 4268c2ecf20Sopenharmony_ci *a = *b; 4278c2ecf20Sopenharmony_ci *b = tmp; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void stats_poll(int interval, int action, __u32 cfg_opt) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct stats_record *record, *prev; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci record = alloc_stats_record(); 4358c2ecf20Sopenharmony_ci prev = alloc_stats_record(); 4368c2ecf20Sopenharmony_ci stats_collect(record); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci while (1) { 4398c2ecf20Sopenharmony_ci swap(&prev, &record); 4408c2ecf20Sopenharmony_ci stats_collect(record); 4418c2ecf20Sopenharmony_ci stats_print(record, prev, action, cfg_opt); 4428c2ecf20Sopenharmony_ci sleep(interval); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci free_stats_record(record); 4468c2ecf20Sopenharmony_ci free_stats_record(prev); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ciint main(int argc, char **argv) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci __u32 cfg_options= NO_TOUCH ; /* Default: Don't touch packet memory */ 4538c2ecf20Sopenharmony_ci struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 4548c2ecf20Sopenharmony_ci struct bpf_prog_load_attr prog_load_attr = { 4558c2ecf20Sopenharmony_ci .prog_type = BPF_PROG_TYPE_XDP, 4568c2ecf20Sopenharmony_ci }; 4578c2ecf20Sopenharmony_ci struct bpf_prog_info info = {}; 4588c2ecf20Sopenharmony_ci __u32 info_len = sizeof(info); 4598c2ecf20Sopenharmony_ci int prog_fd, map_fd, opt, err; 4608c2ecf20Sopenharmony_ci bool use_separators = true; 4618c2ecf20Sopenharmony_ci struct config cfg = { 0 }; 4628c2ecf20Sopenharmony_ci struct bpf_object *obj; 4638c2ecf20Sopenharmony_ci struct bpf_map *map; 4648c2ecf20Sopenharmony_ci char filename[256]; 4658c2ecf20Sopenharmony_ci int longindex = 0; 4668c2ecf20Sopenharmony_ci int interval = 2; 4678c2ecf20Sopenharmony_ci __u32 key = 0; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci char action_str_buf[XDP_ACTION_MAX_STRLEN + 1 /* for \0 */] = { 0 }; 4718c2ecf20Sopenharmony_ci int action = XDP_PASS; /* Default action */ 4728c2ecf20Sopenharmony_ci char *action_str = NULL; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 4758c2ecf20Sopenharmony_ci prog_load_attr.file = filename; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (setrlimit(RLIMIT_MEMLOCK, &r)) { 4788c2ecf20Sopenharmony_ci perror("setrlimit(RLIMIT_MEMLOCK)"); 4798c2ecf20Sopenharmony_ci return 1; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) 4838c2ecf20Sopenharmony_ci return EXIT_FAIL; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci map = bpf_object__find_map_by_name(obj, "config_map"); 4868c2ecf20Sopenharmony_ci stats_global_map = bpf_object__find_map_by_name(obj, "stats_global_map"); 4878c2ecf20Sopenharmony_ci rx_queue_index_map = bpf_object__find_map_by_name(obj, "rx_queue_index_map"); 4888c2ecf20Sopenharmony_ci if (!map || !stats_global_map || !rx_queue_index_map) { 4898c2ecf20Sopenharmony_ci printf("finding a map in obj file failed\n"); 4908c2ecf20Sopenharmony_ci return EXIT_FAIL; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci map_fd = bpf_map__fd(map); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (!prog_fd) { 4958c2ecf20Sopenharmony_ci fprintf(stderr, "ERR: bpf_prog_load_xattr: %s\n", strerror(errno)); 4968c2ecf20Sopenharmony_ci return EXIT_FAIL; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* Parse commands line args */ 5008c2ecf20Sopenharmony_ci while ((opt = getopt_long(argc, argv, "FhSrmzd:s:a:", 5018c2ecf20Sopenharmony_ci long_options, &longindex)) != -1) { 5028c2ecf20Sopenharmony_ci switch (opt) { 5038c2ecf20Sopenharmony_ci case 'd': 5048c2ecf20Sopenharmony_ci if (strlen(optarg) >= IF_NAMESIZE) { 5058c2ecf20Sopenharmony_ci fprintf(stderr, "ERR: --dev name too long\n"); 5068c2ecf20Sopenharmony_ci goto error; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci ifname = (char *)&ifname_buf; 5098c2ecf20Sopenharmony_ci strncpy(ifname, optarg, IF_NAMESIZE); 5108c2ecf20Sopenharmony_ci ifindex = if_nametoindex(ifname); 5118c2ecf20Sopenharmony_ci if (ifindex == 0) { 5128c2ecf20Sopenharmony_ci fprintf(stderr, 5138c2ecf20Sopenharmony_ci "ERR: --dev name unknown err(%d):%s\n", 5148c2ecf20Sopenharmony_ci errno, strerror(errno)); 5158c2ecf20Sopenharmony_ci goto error; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci case 's': 5198c2ecf20Sopenharmony_ci interval = atoi(optarg); 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case 'S': 5228c2ecf20Sopenharmony_ci xdp_flags |= XDP_FLAGS_SKB_MODE; 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci case 'z': 5258c2ecf20Sopenharmony_ci use_separators = false; 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci case 'a': 5288c2ecf20Sopenharmony_ci action_str = (char *)&action_str_buf; 5298c2ecf20Sopenharmony_ci strncpy(action_str, optarg, XDP_ACTION_MAX_STRLEN); 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci case 'r': 5328c2ecf20Sopenharmony_ci cfg_options |= READ_MEM; 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci case 'm': 5358c2ecf20Sopenharmony_ci cfg_options |= SWAP_MAC; 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci case 'F': 5388c2ecf20Sopenharmony_ci xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci case 'h': 5418c2ecf20Sopenharmony_ci error: 5428c2ecf20Sopenharmony_ci default: 5438c2ecf20Sopenharmony_ci usage(argv); 5448c2ecf20Sopenharmony_ci return EXIT_FAIL_OPTION; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) 5498c2ecf20Sopenharmony_ci xdp_flags |= XDP_FLAGS_DRV_MODE; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Required option */ 5528c2ecf20Sopenharmony_ci if (ifindex == -1) { 5538c2ecf20Sopenharmony_ci fprintf(stderr, "ERR: required option --dev missing\n"); 5548c2ecf20Sopenharmony_ci usage(argv); 5558c2ecf20Sopenharmony_ci return EXIT_FAIL_OPTION; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci cfg.ifindex = ifindex; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Parse action string */ 5608c2ecf20Sopenharmony_ci if (action_str) { 5618c2ecf20Sopenharmony_ci action = parse_xdp_action(action_str); 5628c2ecf20Sopenharmony_ci if (action < 0) { 5638c2ecf20Sopenharmony_ci fprintf(stderr, "ERR: Invalid XDP --action: %s\n", 5648c2ecf20Sopenharmony_ci action_str); 5658c2ecf20Sopenharmony_ci list_xdp_actions(); 5668c2ecf20Sopenharmony_ci return EXIT_FAIL_OPTION; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci cfg.action = action; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* XDP_TX requires changing MAC-addrs, else HW may drop */ 5728c2ecf20Sopenharmony_ci if (action == XDP_TX) 5738c2ecf20Sopenharmony_ci cfg_options |= SWAP_MAC; 5748c2ecf20Sopenharmony_ci cfg.options = cfg_options; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* Trick to pretty printf with thousands separators use %' */ 5778c2ecf20Sopenharmony_ci if (use_separators) 5788c2ecf20Sopenharmony_ci setlocale(LC_NUMERIC, "en_US"); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* User-side setup ifindex in config_map */ 5818c2ecf20Sopenharmony_ci err = bpf_map_update_elem(map_fd, &key, &cfg, 0); 5828c2ecf20Sopenharmony_ci if (err) { 5838c2ecf20Sopenharmony_ci fprintf(stderr, "Store config failed (err:%d)\n", err); 5848c2ecf20Sopenharmony_ci exit(EXIT_FAIL_BPF); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Remove XDP program when program is interrupted or killed */ 5888c2ecf20Sopenharmony_ci signal(SIGINT, int_exit); 5898c2ecf20Sopenharmony_ci signal(SIGTERM, int_exit); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { 5928c2ecf20Sopenharmony_ci fprintf(stderr, "link set xdp fd failed\n"); 5938c2ecf20Sopenharmony_ci return EXIT_FAIL_XDP; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); 5978c2ecf20Sopenharmony_ci if (err) { 5988c2ecf20Sopenharmony_ci printf("can't get prog info - %s\n", strerror(errno)); 5998c2ecf20Sopenharmony_ci return err; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci prog_id = info.id; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci stats_poll(interval, action, cfg_options); 6048c2ecf20Sopenharmony_ci return EXIT_OK; 6058c2ecf20Sopenharmony_ci} 606