Hi folks I am stuck with proper cleanup of AF_XDP socket. Not sure if I am doing something wrong or is it a bug. I create xdp socket, then I remove it and then I am trying to create it again in the same process but getting -EBUSY from bind syscall of xsk_socket__create. My cleanup looks like this: xsk_socket__delete(); xsk_umem__delete(); bpf_set_link_xdp_fd(ifindex, -1, opt_xdp_flags); Attaching simple test case. It’s just reduced and slightly modified xdpsock_user.c form kernel samples. Tested on kernel v5.3 4d856f72c10ecb060868ed10ff1b1453943fc6c8 and related libbpf. Thanks a lot Julius
// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2017 - 2018 Intel Corporation. */ #include <asm/barrier.h> #include <errno.h> #include <getopt.h> #include <libgen.h> #include <linux/bpf.h> #include <linux/compiler.h> #include <linux/if_link.h> #include <linux/if_xdp.h> #include <linux/if_ether.h> #include <locale.h> #include <net/ethernet.h> #include <net/if.h> #include <poll.h> #include <pthread.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/resource.h> #include <sys/socket.h> #include <sys/types.h> #include <time.h> #include <unistd.h> #include "libbpf.h" #include "xsk.h" #include <bpf/bpf.h> #ifndef SOL_XDP #define SOL_XDP 283 #endif #ifndef AF_XDP #define AF_XDP 44 #endif #ifndef PF_XDP #define PF_XDP AF_XDP #endif #define NUM_FRAMES (4 * 1024) #define BATCH_SIZE 64 #define MAX_SOCKS 1 typedef __u64 u64; typedef __u32 u32; static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; static const char *opt_if = ""; static int opt_ifindex; static int opt_queue; static u32 opt_xdp_bind_flags; static int opt_xsk_frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE; static __u32 prog_id; struct xsk_umem_info { struct xsk_ring_prod fq; struct xsk_ring_cons cq; struct xsk_umem *umem; void *buffer; }; struct xsk_socket_info { struct xsk_ring_cons rx; struct xsk_ring_prod tx; struct xsk_umem_info *umem; struct xsk_socket *xsk; unsigned long rx_npkts; unsigned long tx_npkts; unsigned long prev_rx_npkts; unsigned long prev_tx_npkts; u32 outstanding_tx; }; struct xsk_socket_info *xsks[MAX_SOCKS]; static void remove_xdp_program(void) { __u32 curr_prog_id = 0; if (bpf_get_link_xdp_id(opt_ifindex, &curr_prog_id, opt_xdp_flags)) { printf("bpf_get_link_xdp_id failed\n"); exit(EXIT_FAILURE); } if (prog_id == curr_prog_id) bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags); else if (!curr_prog_id) printf("couldn't find a prog id on a given interface\n"); else printf("program on interface changed, not removing\n"); } static void __exit_with_error(int error, const char *file, const char *func, int line) { fprintf(stderr, "%s:%s:%i: errno: %d/\"%s\"\n", file, func, line, error, strerror(error)); remove_xdp_program(); exit(EXIT_FAILURE); } #define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \ __LINE__) static struct xsk_umem_info *xsk_configure_umem(void *buffer, u64 size) { struct xsk_umem_info *umem; struct xsk_umem_config cfg = { .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, .frame_size = opt_xsk_frame_size, .frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM, }; int ret; umem = calloc(1, sizeof(*umem)); if (!umem) exit_with_error(errno); ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq, &cfg); if (ret) exit_with_error(-ret); umem->buffer = buffer; return umem; } static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem) { struct xsk_socket_config cfg; struct xsk_socket_info *xsk; int ret; u32 idx; int i; xsk = calloc(1, sizeof(*xsk)); if (!xsk) exit_with_error(errno); xsk->umem = umem; cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS; cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; cfg.libbpf_flags = 0; cfg.xdp_flags = opt_xdp_flags; cfg.bind_flags = opt_xdp_bind_flags; ret = xsk_socket__create(&xsk->xsk, opt_if, opt_queue, umem->umem, &xsk->rx, &xsk->tx, &cfg); if (ret) exit_with_error(-ret); ret = bpf_get_link_xdp_id(opt_ifindex, &prog_id, opt_xdp_flags); if (ret) exit_with_error(-ret); ret = xsk_ring_prod__reserve(&xsk->umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx); if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS) exit_with_error(-ret); for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++) *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx++) = i * opt_xsk_frame_size; xsk_ring_prod__submit(&xsk->umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS); return xsk; } static struct option long_options[] = { {"interface", required_argument, 0, 'i'}, {0, 0, 0, 0} }; static void usage(const char *prog) { const char *str = " Usage: %s [OPTIONS]\n" " Options:\n" " -i, --interface=n Run on interface n\n" "\n"; fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE); exit(EXIT_FAILURE); } static void parse_command_line(int argc, char **argv) { int option_index, c; opterr = 0; for (;;) { c = getopt_long(argc, argv, "i:", long_options, &option_index); if (c == -1) break; switch (c) { case 'i': opt_if = optarg; break; default: usage(basename(argv[0])); } } opt_ifindex = if_nametoindex(opt_if); if (!opt_ifindex) { fprintf(stderr, "ERROR: interface \"%s\" does not exist\n", opt_if); usage(basename(argv[0])); } } int main(int argc, char **argv) { struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; struct xsk_umem_info *umem; void *bufs; int ret; parse_command_line(argc, argv); if (setrlimit(RLIMIT_MEMLOCK, &r)) { fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n", strerror(errno)); exit(EXIT_FAILURE); } ret = posix_memalign(&bufs, getpagesize(), /* PAGE_SIZE aligned */ NUM_FRAMES * opt_xsk_frame_size); if (ret) exit_with_error(ret); /* Create sockets... */ umem = xsk_configure_umem(bufs, NUM_FRAMES * opt_xsk_frame_size); xsks[0] = xsk_configure_socket(umem); /* Cleanup */ /// missing something ? struct xsk_umem *uumem = xsks[0]->umem->umem; xsk_socket__delete(xsks[0]->xsk); (void)xsk_umem__delete(uumem); remove_xdp_program(); free(bufs); /* Create sockets 2nd time ... */ umem = NULL; bufs = NULL; ret = posix_memalign(&bufs, getpagesize(), /* PAGE_SIZE aligned */ NUM_FRAMES * opt_xsk_frame_size); if (ret) exit_with_error(ret); umem = xsk_configure_umem(bufs, NUM_FRAMES * opt_xsk_frame_size); xsks[0] = xsk_configure_socket(umem); /// fails printf("Victory, socket created 2nd time\n"); return 0; }