xdp_rxq_info_user.c 14 KB

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