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