xref: /linux/tools/testing/selftests/net/lib/xdp_helper.c (revision 1b98f357dadd6ea613a435fbaef1a5dd7b35fd21)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <sys/mman.h>
8 #include <sys/socket.h>
9 #include <linux/if_xdp.h>
10 #include <linux/if_link.h>
11 #include <net/if.h>
12 #include <inttypes.h>
13 
14 #include "ksft.h"
15 
16 #define UMEM_SZ (1U << 16)
17 #define NUM_DESC (UMEM_SZ / 2048)
18 
19 
20 static void print_usage(const char *bin)
21 {
22 	fprintf(stderr, "Usage: %s ifindex queue_id [-z]\n\n"
23 		"where:\n\t-z: force zerocopy mode", bin);
24 }
25 
26 /* this is a simple helper program that creates an XDP socket and does the
27  * minimum necessary to get bind() to succeed.
28  *
29  * this test program is not intended to actually process packets, but could be
30  * extended in the future if that is actually needed.
31  *
32  * it is used by queues.py to ensure the xsk netlinux attribute is set
33  * correctly.
34  */
35 int main(int argc, char **argv)
36 {
37 	struct xdp_umem_reg umem_reg = { 0 };
38 	struct sockaddr_xdp sxdp = { 0 };
39 	int num_desc = NUM_DESC;
40 	void *umem_area;
41 	int retry = 0;
42 	int ifindex;
43 	int sock_fd;
44 	int queue;
45 
46 	if (argc != 3 && argc != 4) {
47 		print_usage(argv[0]);
48 		return 1;
49 	}
50 
51 	sock_fd = socket(AF_XDP, SOCK_RAW, 0);
52 	if (sock_fd < 0) {
53 		perror("socket creation failed");
54 		/* if the kernel doesn't support AF_XDP, let the test program
55 		 * know with -1. All other error paths return 1.
56 		 */
57 		if (errno == EAFNOSUPPORT)
58 			return -1;
59 		return 1;
60 	}
61 
62 	/* "Probing mode", just checking if AF_XDP sockets are supported */
63 	if (!strcmp(argv[1], "-") && !strcmp(argv[2], "-")) {
64 		printf("AF_XDP support detected\n");
65 		close(sock_fd);
66 		return 0;
67 	}
68 
69 	ifindex = atoi(argv[1]);
70 	queue = atoi(argv[2]);
71 
72 	umem_area = mmap(NULL, UMEM_SZ, PROT_READ | PROT_WRITE, MAP_PRIVATE |
73 			MAP_ANONYMOUS, -1, 0);
74 	if (umem_area == MAP_FAILED) {
75 		perror("mmap failed");
76 		return 1;
77 	}
78 
79 	umem_reg.addr = (uintptr_t)umem_area;
80 	umem_reg.len = UMEM_SZ;
81 	umem_reg.chunk_size = 2048;
82 	umem_reg.headroom = 0;
83 
84 	setsockopt(sock_fd, SOL_XDP, XDP_UMEM_REG, &umem_reg,
85 		   sizeof(umem_reg));
86 	setsockopt(sock_fd, SOL_XDP, XDP_UMEM_FILL_RING, &num_desc,
87 		   sizeof(num_desc));
88 	setsockopt(sock_fd, SOL_XDP, XDP_UMEM_COMPLETION_RING, &num_desc,
89 		   sizeof(num_desc));
90 	setsockopt(sock_fd, SOL_XDP, XDP_RX_RING, &num_desc, sizeof(num_desc));
91 
92 	sxdp.sxdp_family = AF_XDP;
93 	sxdp.sxdp_ifindex = ifindex;
94 	sxdp.sxdp_queue_id = queue;
95 	sxdp.sxdp_flags = 0;
96 
97 	if (argc > 3) {
98 		if (!strcmp(argv[3], "-z")) {
99 			sxdp.sxdp_flags = XDP_ZEROCOPY;
100 		} else {
101 			print_usage(argv[0]);
102 			return 1;
103 		}
104 	}
105 
106 	while (1) {
107 		if (bind(sock_fd, (struct sockaddr *)&sxdp, sizeof(sxdp)) == 0)
108 			break;
109 
110 		if (errno == EBUSY && retry < 3) {
111 			retry++;
112 			sleep(1);
113 			continue;
114 		} else {
115 			perror("bind failed");
116 			munmap(umem_area, UMEM_SZ);
117 			close(sock_fd);
118 			return 1;
119 		}
120 	}
121 
122 	ksft_ready();
123 	ksft_wait();
124 
125 	/* parent program will write a byte to stdin when its ready for this
126 	 * helper to exit
127 	 */
128 
129 	close(sock_fd);
130 	return 0;
131 }
132