]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - samples/bpf/xdp_redirect_cpu_user.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[mirror_ubuntu-jammy-kernel.git] / samples / bpf / xdp_redirect_cpu_user.c
CommitLineData
7f904d7e
TG
1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
fad3917e
JDB
3 */
4static const char *__doc__ =
e531a220
KKD
5"XDP CPU redirect tool, using BPF_MAP_TYPE_CPUMAP\n"
6"Usage: xdp_redirect_cpu -d <IFINDEX|IFNAME> -c 0 ... -c N\n"
7"Valid specification for CPUMAP BPF program:\n"
8" --mprog-name/-e pass (use built-in XDP_PASS program)\n"
9" --mprog-name/-e drop (use built-in XDP_DROP program)\n"
10" --redirect-device/-r <ifindex|ifname> (use built-in DEVMAP redirect program)\n"
11" Custom CPUMAP BPF program:\n"
12" --mprog-filename/-f <filename> --mprog-name/-e <program>\n"
13" Optionally, also pass --redirect-map/-m and --redirect-device/-r together\n"
14" to configure DEVMAP in BPF object <filename>\n";
fad3917e
JDB
15
16#include <errno.h>
17#include <signal.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <stdbool.h>
21#include <string.h>
22#include <unistd.h>
23#include <locale.h>
24#include <sys/resource.h>
6a098154 25#include <sys/sysinfo.h>
fad3917e
JDB
26#include <getopt.h>
27#include <net/if.h>
28#include <time.h>
f9e6bfdb 29#include <linux/limits.h>
fad3917e
JDB
30#include <arpa/inet.h>
31#include <linux/if_link.h>
2bf3e2ef 32#include <bpf/bpf.h>
7cf245a3 33#include <bpf/libbpf.h>
fad3917e 34#include "bpf_util.h"
e531a220
KKD
35#include "xdp_sample_user.h"
36#include "xdp_redirect_cpu.skel.h"
fad3917e 37
e531a220
KKD
38static int map_fd;
39static int avail_fd;
40static int count_fd;
151936bf 41
e531a220
KKD
42static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT |
43 SAMPLE_CPUMAP_ENQUEUE_CNT | SAMPLE_CPUMAP_KTHREAD_CNT |
44 SAMPLE_EXCEPTION_CNT;
fad3917e 45
e531a220 46DEFINE_SAMPLE_INIT(xdp_redirect_cpu);
fad3917e
JDB
47
48static const struct option long_options[] = {
e531a220
KKD
49 { "help", no_argument, NULL, 'h' },
50 { "dev", required_argument, NULL, 'd' },
51 { "skb-mode", no_argument, NULL, 'S' },
52 { "progname", required_argument, NULL, 'p' },
53 { "qsize", required_argument, NULL, 'q' },
54 { "cpu", required_argument, NULL, 'c' },
55 { "stress-mode", no_argument, NULL, 'x' },
56 { "force", no_argument, NULL, 'F' },
57 { "interval", required_argument, NULL, 'i' },
58 { "verbose", no_argument, NULL, 'v' },
59 { "stats", no_argument, NULL, 's' },
60 { "mprog-name", required_argument, NULL, 'e' },
61 { "mprog-filename", required_argument, NULL, 'f' },
62 { "redirect-device", required_argument, NULL, 'r' },
63 { "redirect-map", required_argument, NULL, 'm' },
64 {}
fad3917e
JDB
65};
66
bbaf6029
MF
67static void print_avail_progs(struct bpf_object *obj)
68{
69 struct bpf_program *pos;
70
e531a220 71 printf(" Programs to be used for -p/--progname:\n");
bbaf6029 72 bpf_object__for_each_program(pos, obj) {
e531a220
KKD
73 if (bpf_program__is_xdp(pos)) {
74 if (!strncmp(bpf_program__name(pos), "xdp_prognum",
75 sizeof("xdp_prognum") - 1))
76 printf(" %s\n", bpf_program__name(pos));
fad3917e 77 }
fad3917e 78 }
fad3917e
JDB
79}
80
e531a220
KKD
81static void usage(char *argv[], const struct option *long_options,
82 const char *doc, int mask, bool error, struct bpf_object *obj)
fad3917e 83{
e531a220
KKD
84 sample_usage(argv, long_options, doc, mask, error);
85 print_avail_progs(obj);
fad3917e
JDB
86}
87
ce4dade7 88static int create_cpu_entry(__u32 cpu, struct bpf_cpumap_val *value,
fad3917e
JDB
89 __u32 avail_idx, bool new)
90{
91 __u32 curr_cpus_count = 0;
92 __u32 key = 0;
93 int ret;
94
95 /* Add a CPU entry to cpumap, as this allocate a cpu entry in
96 * the kernel for the cpu.
97 */
e531a220
KKD
98 ret = bpf_map_update_elem(map_fd, &cpu, value, 0);
99 if (ret < 0) {
100 fprintf(stderr, "Create CPU entry failed: %s\n", strerror(errno));
101 return ret;
fad3917e
JDB
102 }
103
104 /* Inform bpf_prog's that a new CPU is available to select
105 * from via some control maps.
106 */
e531a220
KKD
107 ret = bpf_map_update_elem(avail_fd, &avail_idx, &cpu, 0);
108 if (ret < 0) {
109 fprintf(stderr, "Add to avail CPUs failed: %s\n", strerror(errno));
110 return ret;
fad3917e
JDB
111 }
112
113 /* When not replacing/updating existing entry, bump the count */
e531a220
KKD
114 ret = bpf_map_lookup_elem(count_fd, &key, &curr_cpus_count);
115 if (ret < 0) {
116 fprintf(stderr, "Failed reading curr cpus_count: %s\n",
117 strerror(errno));
118 return ret;
fad3917e
JDB
119 }
120 if (new) {
121 curr_cpus_count++;
e531a220 122 ret = bpf_map_update_elem(count_fd, &key,
bbaf6029 123 &curr_cpus_count, 0);
e531a220
KKD
124 if (ret < 0) {
125 fprintf(stderr, "Failed write curr cpus_count: %s\n",
126 strerror(errno));
127 return ret;
fad3917e
JDB
128 }
129 }
e531a220
KKD
130
131 printf("%s CPU: %u as idx: %u qsize: %d cpumap_prog_fd: %d (cpus_count: %u)\n",
132 new ? "Add new" : "Replace", cpu, avail_idx,
ce4dade7 133 value->qsize, value->bpf_prog.fd, curr_cpus_count);
fad3917e
JDB
134
135 return 0;
136}
137
138/* CPUs are zero-indexed. Thus, add a special sentinel default value
139 * in map cpus_available to mark CPU index'es not configured
140 */
e531a220 141static int mark_cpus_unavailable(void)
fad3917e 142{
e531a220 143 int ret, i, n_cpus = libbpf_num_possible_cpus();
48b2e71c 144 __u32 invalid_cpu = n_cpus;
fad3917e 145
6a098154 146 for (i = 0; i < n_cpus; i++) {
e531a220 147 ret = bpf_map_update_elem(avail_fd, &i,
bbaf6029 148 &invalid_cpu, 0);
e531a220
KKD
149 if (ret < 0) {
150 fprintf(stderr, "Failed marking CPU unavailable: %s\n",
151 strerror(errno));
152 return ret;
fad3917e
JDB
153 }
154 }
e531a220
KKD
155
156 return 0;
fad3917e
JDB
157}
158
159/* Stress cpumap management code by concurrently changing underlying cpumap */
e531a220 160static void stress_cpumap(void *ctx)
fad3917e 161{
e531a220
KKD
162 struct bpf_cpumap_val *value = ctx;
163
fad3917e
JDB
164 /* Changing qsize will cause kernel to free and alloc a new
165 * bpf_cpu_map_entry, with an associated/complicated tear-down
166 * procedure.
167 */
ce4dade7
LB
168 value->qsize = 1024;
169 create_cpu_entry(1, value, 0, false);
170 value->qsize = 8;
171 create_cpu_entry(1, value, 0, false);
172 value->qsize = 16000;
173 create_cpu_entry(1, value, 0, false);
fad3917e
JDB
174}
175
e531a220
KKD
176static int set_cpumap_prog(struct xdp_redirect_cpu *skel,
177 const char *redir_interface, const char *redir_map,
178 const char *mprog_filename, const char *mprog_name)
f9e6bfdb 179{
e531a220
KKD
180 if (mprog_filename) {
181 struct bpf_program *prog;
182 struct bpf_object *obj;
183 int ret;
184
185 if (!mprog_name) {
186 fprintf(stderr, "BPF program not specified for file %s\n",
187 mprog_filename);
188 goto end;
189 }
190 if ((redir_interface && !redir_map) || (!redir_interface && redir_map)) {
191 fprintf(stderr, "--redirect-%s specified but --redirect-%s not specified\n",
192 redir_interface ? "device" : "map", redir_interface ? "map" : "device");
193 goto end;
151936bf 194 }
bbaf6029 195
e531a220
KKD
196 /* Custom BPF program */
197 obj = bpf_object__open_file(mprog_filename, NULL);
198 if (!obj) {
199 ret = -errno;
200 fprintf(stderr, "Failed to bpf_prog_load_xattr: %s\n",
201 strerror(errno));
202 return ret;
203 }
ce4dade7 204
e531a220
KKD
205 ret = bpf_object__load(obj);
206 if (ret < 0) {
207 ret = -errno;
208 fprintf(stderr, "Failed to bpf_object__load: %s\n",
209 strerror(errno));
210 return ret;
211 }
ce4dade7 212
e531a220
KKD
213 if (redir_map) {
214 int err, redir_map_fd, ifindex_out, key = 0;
ce4dade7 215
e531a220
KKD
216 redir_map_fd = bpf_object__find_map_fd_by_name(obj, redir_map);
217 if (redir_map_fd < 0) {
218 fprintf(stderr, "Failed to bpf_object__find_map_fd_by_name: %s\n",
219 strerror(errno));
220 return redir_map_fd;
221 }
ce4dade7 222
e531a220
KKD
223 ifindex_out = if_nametoindex(redir_interface);
224 if (!ifindex_out)
225 ifindex_out = strtoul(redir_interface, NULL, 0);
226 if (!ifindex_out) {
227 fprintf(stderr, "Bad interface name or index\n");
228 return -EINVAL;
229 }
ce4dade7 230
e531a220
KKD
231 err = bpf_map_update_elem(redir_map_fd, &key, &ifindex_out, 0);
232 if (err < 0)
233 return err;
234 }
ce4dade7 235
e531a220
KKD
236 prog = bpf_object__find_program_by_name(obj, mprog_name);
237 if (!prog) {
238 ret = -errno;
239 fprintf(stderr, "Failed to bpf_object__find_program_by_name: %s\n",
240 strerror(errno));
241 return ret;
242 }
ce4dade7 243
e531a220
KKD
244 return bpf_program__fd(prog);
245 } else {
246 if (mprog_name) {
247 if (redir_interface || redir_map) {
248 fprintf(stderr, "Need to specify --mprog-filename/-f\n");
249 goto end;
250 }
251 if (!strcmp(mprog_name, "pass") || !strcmp(mprog_name, "drop")) {
252 /* Use built-in pass/drop programs */
253 return *mprog_name == 'p' ? bpf_program__fd(skel->progs.xdp_redirect_cpu_pass)
254 : bpf_program__fd(skel->progs.xdp_redirect_cpu_drop);
255 } else {
256 fprintf(stderr, "Unknown name \"%s\" for built-in BPF program\n",
257 mprog_name);
258 goto end;
259 }
260 } else {
261 if (redir_map) {
262 fprintf(stderr, "Need to specify --mprog-filename, --mprog-name and"
263 " --redirect-device with --redirect-map\n");
264 goto end;
265 }
266 if (redir_interface) {
267 /* Use built-in devmap redirect */
268 struct bpf_devmap_val val = {};
269 int ifindex_out, err;
270 __u32 key = 0;
271
272 if (!redir_interface)
273 return 0;
274
275 ifindex_out = if_nametoindex(redir_interface);
276 if (!ifindex_out)
277 ifindex_out = strtoul(redir_interface, NULL, 0);
278 if (!ifindex_out) {
279 fprintf(stderr, "Bad interface name or index\n");
280 return -EINVAL;
281 }
282
283 if (get_mac_addr(ifindex_out, skel->bss->tx_mac_addr) < 0) {
284 printf("Get interface %d mac failed\n", ifindex_out);
285 return -EINVAL;
286 }
287
288 val.ifindex = ifindex_out;
289 val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_redirect_egress_prog);
290 err = bpf_map_update_elem(bpf_map__fd(skel->maps.tx_port), &key, &val, 0);
291 if (err < 0)
292 return -errno;
293
294 return bpf_program__fd(skel->progs.xdp_redirect_cpu_devmap);
295 }
296 }
ce4dade7
LB
297 }
298
e531a220
KKD
299 /* Disabled */
300 return 0;
301end:
302 fprintf(stderr, "Invalid options for CPUMAP BPF program\n");
303 return -EINVAL;
ce4dade7
LB
304}
305
fad3917e
JDB
306int main(int argc, char **argv)
307{
e531a220
KKD
308 const char *redir_interface = NULL, *redir_map = NULL;
309 const char *mprog_filename = NULL, *mprog_name = NULL;
310 struct xdp_redirect_cpu *skel;
311 struct bpf_map_info info = {};
312 char ifname_buf[IF_NAMESIZE];
ce4dade7 313 struct bpf_cpumap_val value;
e531a220
KKD
314 __u32 infosz = sizeof(info);
315 int ret = EXIT_FAIL_OPTION;
316 unsigned long interval = 2;
fad3917e 317 bool stress_mode = false;
bbaf6029 318 struct bpf_program *prog;
e531a220
KKD
319 const char *prog_name;
320 bool generic = false;
321 bool force = false;
fad3917e 322 int added_cpus = 0;
e531a220 323 bool error = true;
fad3917e 324 int longindex = 0;
fad3917e 325 int add_cpu = -1;
e531a220
KKD
326 int ifindex = -1;
327 int *cpu, i, opt;
328 char *ifname;
fad3917e 329 __u32 qsize;
e531a220 330 int n_cpus;
fad3917e 331
e531a220 332 n_cpus = libbpf_num_possible_cpus();
6a098154 333
eff94154
JDB
334 /* Notice: Choosing the queue size is very important when CPU is
335 * configured with power-saving states.
336 *
337 * If deepest state take 133 usec to wakeup from (133/10^6). When link
338 * speed is 10Gbit/s ((10*10^9/8) in bytes/sec). How many bytes can
339 * arrive with in 133 usec at this speed: (10*10^9/8)*(133/10^6) =
340 * 166250 bytes. With MTU size packets this is 110 packets, and with
341 * minimum Ethernet (MAC-preamble + intergap) 84 bytes is 1979 packets.
342 *
343 * Setting default cpumap queue to 2048 as worst-case (small packet)
344 * should be +64 packet due kthread wakeup call (due to xdp_do_flush)
345 * worst-case is 2043 packets.
346 *
347 * Sysadm can configured system to avoid deep-sleep via:
348 * tuned-adm profile network-latency
fad3917e 349 */
eff94154 350 qsize = 2048;
fad3917e 351
e531a220
KKD
352 skel = xdp_redirect_cpu__open();
353 if (!skel) {
354 fprintf(stderr, "Failed to xdp_redirect_cpu__open: %s\n",
bbaf6029 355 strerror(errno));
e531a220
KKD
356 ret = EXIT_FAIL_BPF;
357 goto end;
358 }
359
360 ret = sample_init_pre_load(skel);
361 if (ret < 0) {
362 fprintf(stderr, "Failed to sample_init_pre_load: %s\n", strerror(-ret));
363 ret = EXIT_FAIL_BPF;
364 goto end_destroy;
bbaf6029 365 }
151936bf 366
e531a220
KKD
367 if (bpf_map__set_max_entries(skel->maps.cpu_map, n_cpus) < 0) {
368 fprintf(stderr, "Failed to set max entries for cpu_map map: %s",
369 strerror(errno));
370 ret = EXIT_FAIL_BPF;
371 goto end_destroy;
151936bf
DL
372 }
373
e531a220
KKD
374 if (bpf_map__set_max_entries(skel->maps.cpus_available, n_cpus) < 0) {
375 fprintf(stderr, "Failed to set max entries for cpus_available map: %s",
376 strerror(errno));
377 ret = EXIT_FAIL_BPF;
378 goto end_destroy;
fad3917e 379 }
fad3917e 380
e531a220 381 cpu = calloc(n_cpus, sizeof(int));
a4e76f1b 382 if (!cpu) {
e531a220
KKD
383 fprintf(stderr, "Failed to allocate cpu array\n");
384 goto end_destroy;
a4e76f1b 385 }
a4e76f1b 386
e531a220
KKD
387 prog = skel->progs.xdp_prognum5_lb_hash_ip_pairs;
388 while ((opt = getopt_long(argc, argv, "d:si:Sxp:f:e:r:m:c:q:Fvh",
fad3917e
JDB
389 long_options, &longindex)) != -1) {
390 switch (opt) {
391 case 'd':
392 if (strlen(optarg) >= IF_NAMESIZE) {
e531a220
KKD
393 fprintf(stderr, "-d/--dev name too long\n");
394 goto end_cpu;
fad3917e
JDB
395 }
396 ifname = (char *)&ifname_buf;
e531a220 397 safe_strncpy(ifname, optarg, sizeof(ifname));
fad3917e 398 ifindex = if_nametoindex(ifname);
e531a220
KKD
399 if (!ifindex)
400 ifindex = strtoul(optarg, NULL, 0);
401 if (!ifindex) {
402 fprintf(stderr, "Bad interface index or name (%d): %s\n",
fad3917e 403 errno, strerror(errno));
e531a220
KKD
404 usage(argv, long_options, __doc__, mask, true, skel->obj);
405 goto end_cpu;
fad3917e
JDB
406 }
407 break;
408 case 's':
e531a220
KKD
409 mask |= SAMPLE_REDIRECT_MAP_CNT;
410 break;
411 case 'i':
412 interval = strtoul(optarg, NULL, 0);
fad3917e
JDB
413 break;
414 case 'S':
e531a220 415 generic = true;
fad3917e 416 break;
fad3917e
JDB
417 case 'x':
418 stress_mode = true;
419 break;
fad3917e
JDB
420 case 'p':
421 /* Selecting eBPF prog to load */
bbaf6029 422 prog_name = optarg;
e531a220
KKD
423 prog = bpf_object__find_program_by_name(skel->obj,
424 prog_name);
425 if (!prog) {
426 fprintf(stderr,
427 "Failed to find program %s specified by"
428 " option -p/--progname\n",
429 prog_name);
430 print_avail_progs(skel->obj);
431 goto end_cpu;
432 }
ce4dade7
LB
433 break;
434 case 'f':
435 mprog_filename = optarg;
436 break;
437 case 'e':
438 mprog_name = optarg;
439 break;
440 case 'r':
441 redir_interface = optarg;
e531a220 442 mask |= SAMPLE_DEVMAP_XMIT_CNT_MULTI;
ce4dade7
LB
443 break;
444 case 'm':
445 redir_map = optarg;
446 break;
fad3917e
JDB
447 case 'c':
448 /* Add multiple CPUs */
449 add_cpu = strtoul(optarg, NULL, 0);
6a098154 450 if (add_cpu >= n_cpus) {
fad3917e 451 fprintf(stderr,
48b2e71c 452 "--cpu nr too large for cpumap err (%d):%s\n",
fad3917e 453 errno, strerror(errno));
48b2e71c 454 usage(argv, long_options, __doc__, mask, true, skel->obj);
e531a220 455 goto end_cpu;
fad3917e 456 }
a4e76f1b 457 cpu[added_cpus++] = add_cpu;
fad3917e
JDB
458 break;
459 case 'q':
e531a220 460 qsize = strtoul(optarg, NULL, 0);
fad3917e 461 break;
743e568c 462 case 'F':
e531a220
KKD
463 force = true;
464 break;
465 case 'v':
466 sample_switch_mode();
743e568c 467 break;
fad3917e 468 case 'h':
e531a220 469 error = false;
fad3917e 470 default:
e531a220
KKD
471 usage(argv, long_options, __doc__, mask, error, skel->obj);
472 goto end_cpu;
fad3917e
JDB
473 }
474 }
d50ecc46 475
e531a220 476 ret = EXIT_FAIL_OPTION;
fad3917e 477 if (ifindex == -1) {
e531a220
KKD
478 fprintf(stderr, "Required option --dev missing\n");
479 usage(argv, long_options, __doc__, mask, true, skel->obj);
480 goto end_cpu;
fad3917e 481 }
e531a220 482
fad3917e 483 if (add_cpu == -1) {
e531a220
KKD
484 fprintf(stderr, "Required option --cpu missing\n"
485 "Specify multiple --cpu option to add more\n");
486 usage(argv, long_options, __doc__, mask, true, skel->obj);
487 goto end_cpu;
fad3917e
JDB
488 }
489
e531a220
KKD
490 skel->rodata->from_match[0] = ifindex;
491 if (redir_interface)
492 skel->rodata->to_match[0] = if_nametoindex(redir_interface);
493
494 ret = xdp_redirect_cpu__load(skel);
495 if (ret < 0) {
496 fprintf(stderr, "Failed to xdp_redirect_cpu__load: %s\n",
497 strerror(errno));
498 goto end_cpu;
ce4dade7 499 }
ce4dade7 500
e531a220
KKD
501 ret = bpf_obj_get_info_by_fd(bpf_map__fd(skel->maps.cpu_map), &info, &infosz);
502 if (ret < 0) {
503 fprintf(stderr, "Failed bpf_obj_get_info_by_fd for cpumap: %s\n",
504 strerror(errno));
505 goto end_cpu;
506 }
a4e76f1b 507
e531a220 508 skel->bss->cpumap_map_id = info.id;
fad3917e 509
e531a220
KKD
510 map_fd = bpf_map__fd(skel->maps.cpu_map);
511 avail_fd = bpf_map__fd(skel->maps.cpus_available);
512 count_fd = bpf_map__fd(skel->maps.cpus_count);
513
514 ret = mark_cpus_unavailable();
515 if (ret < 0) {
516 fprintf(stderr, "Unable to mark CPUs as unavailable\n");
517 goto end_cpu;
bbaf6029
MF
518 }
519
e531a220
KKD
520 ret = sample_init(skel, mask);
521 if (ret < 0) {
522 fprintf(stderr, "Failed to initialize sample: %s\n", strerror(-ret));
523 ret = EXIT_FAIL;
524 goto end_cpu;
bbaf6029
MF
525 }
526
e531a220
KKD
527 value.bpf_prog.fd = set_cpumap_prog(skel, redir_interface, redir_map,
528 mprog_filename, mprog_name);
529 if (value.bpf_prog.fd < 0) {
530 fprintf(stderr, "Failed to set CPUMAP BPF program: %s\n",
531 strerror(-value.bpf_prog.fd));
532 usage(argv, long_options, __doc__, mask, true, skel->obj);
533 ret = EXIT_FAIL_BPF;
534 goto end_cpu;
fad3917e 535 }
e531a220 536 value.qsize = qsize;
fad3917e 537
e531a220
KKD
538 for (i = 0; i < added_cpus; i++) {
539 if (create_cpu_entry(cpu[i], &value, i, true) < 0) {
540 fprintf(stderr, "Cannot proceed, exiting\n");
541 usage(argv, long_options, __doc__, mask, true, skel->obj);
542 goto end_cpu;
543 }
3b7a8ec2 544 }
3b7a8ec2 545
e531a220
KKD
546 ret = EXIT_FAIL_XDP;
547 if (sample_install_xdp(prog, ifindex, generic, force) < 0)
548 goto end_cpu;
151936bf 549
e531a220
KKD
550 ret = sample_run(interval, stress_mode ? stress_cpumap : NULL, &value);
551 if (ret < 0) {
552 fprintf(stderr, "Failed during sample run: %s\n", strerror(-ret));
553 ret = EXIT_FAIL;
554 goto end_cpu;
555 }
556 ret = EXIT_OK;
557end_cpu:
a4e76f1b 558 free(cpu);
e531a220
KKD
559end_destroy:
560 xdp_redirect_cpu__destroy(skel);
561end:
562 sample_exit(ret);
fad3917e 563}