// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include #include #include #include "ksft.h" #define UMEM_SZ (1U << 16) #define NUM_DESC (UMEM_SZ / 2048) static void print_usage(const char *bin) { fprintf(stderr, "Usage: %s ifindex queue_id [-z]\n\n" "where:\n\t-z: force zerocopy mode", bin); } /* this is a simple helper program that creates an XDP socket and does the * minimum necessary to get bind() to succeed. * * this test program is not intended to actually process packets, but could be * extended in the future if that is actually needed. * * it is used by queues.py to ensure the xsk netlinux attribute is set * correctly. */ int main(int argc, char **argv) { struct xdp_umem_reg umem_reg = { 0 }; struct sockaddr_xdp sxdp = { 0 }; int num_desc = NUM_DESC; void *umem_area; int retry = 0; int ifindex; int sock_fd; int queue; if (argc != 3 && argc != 4) { print_usage(argv[0]); return 1; } sock_fd = socket(AF_XDP, SOCK_RAW, 0); if (sock_fd < 0) { perror("socket creation failed"); /* if the kernel doesn't support AF_XDP, let the test program * know with -1. All other error paths return 1. */ if (errno == EAFNOSUPPORT) return -1; return 1; } /* "Probing mode", just checking if AF_XDP sockets are supported */ if (!strcmp(argv[1], "-") && !strcmp(argv[2], "-")) { printf("AF_XDP support detected\n"); close(sock_fd); return 0; } ifindex = atoi(argv[1]); queue = atoi(argv[2]); umem_area = mmap(NULL, UMEM_SZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (umem_area == MAP_FAILED) { perror("mmap failed"); return 1; } umem_reg.addr = (uintptr_t)umem_area; umem_reg.len = UMEM_SZ; umem_reg.chunk_size = 2048; umem_reg.headroom = 0; setsockopt(sock_fd, SOL_XDP, XDP_UMEM_REG, &umem_reg, sizeof(umem_reg)); setsockopt(sock_fd, SOL_XDP, XDP_UMEM_FILL_RING, &num_desc, sizeof(num_desc)); setsockopt(sock_fd, SOL_XDP, XDP_UMEM_COMPLETION_RING, &num_desc, sizeof(num_desc)); setsockopt(sock_fd, SOL_XDP, XDP_RX_RING, &num_desc, sizeof(num_desc)); sxdp.sxdp_family = AF_XDP; sxdp.sxdp_ifindex = ifindex; sxdp.sxdp_queue_id = queue; sxdp.sxdp_flags = 0; if (argc > 3) { if (!strcmp(argv[3], "-z")) { sxdp.sxdp_flags = XDP_ZEROCOPY; } else { print_usage(argv[0]); return 1; } } while (1) { if (bind(sock_fd, (struct sockaddr *)&sxdp, sizeof(sxdp)) == 0) break; if (errno == EBUSY && retry < 3) { retry++; sleep(1); continue; } else { perror("bind failed"); munmap(umem_area, UMEM_SZ); close(sock_fd); return 1; } } ksft_ready(); ksft_wait(); /* parent program will write a byte to stdin when its ready for this * helper to exit */ close(sock_fd); return 0; }