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
print_usage(const char * bin)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 */
main(int argc,char ** argv)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