Merge tag 'net-pull-request' of https://github.com/jasowang/qemu into staging

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEEIV1G9IJGaJ7HfzVi7wSWWzmNYhEFAmh11cgACgkQ7wSWWzmN
# YhGZKAf+PZ3ZnOoHXd5z8hA5d9Xf+U/01YyPN+Q0NPLWVXhYZBeNhhYEnZwGeSwS
# n0YFTLiYIrcaSrt74QtBvUVCX7KoILRnzgoLquUnFBlI0BrR5pFKB70gHmLU3Dxw
# xOdxtIm/chfiicE39ziTfO28Cv0N1k9NCHsuMsydbhQL8kc/aRaMofizO8MjPLbr
# J8hf8N7jivh8fzH3F5vyglaNl2ijSkPm+XDQYAb04laGfdsIlYkmB7lB/17def2a
# S9gur484x5w+Yb2LNdyq/3IPzDqzlNbRGVcfTZS8FIc65R+5idIN+7lKHCffURrr
# W8zWFy1wA54hJoTxAq0nsf1TSvc9UA==
# =DiBC
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 15 Jul 2025 00:15:04 EDT
# gpg:                using RSA key 215D46F48246689EC77F3562EF04965B398D6211
# gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>" [full]
# Primary key fingerprint: 215D 46F4 8246 689E C77F  3562 EF04 965B 398D 6211

* tag 'net-pull-request' of https://github.com/jasowang/qemu:
  net/af-xdp: Support pinned map path for AF_XDP sockets
  net/af-xdp: Fix up cleanup path upon failure in queue creation
  net/af-xdp: Remove XDP program cleanup logic

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi
2025-07-16 07:05:36 -04:00
3 changed files with 119 additions and 30 deletions

View File

@@ -49,9 +49,12 @@ typedef struct AFXDPState {
char *buffer;
struct xsk_umem *umem;
uint32_t n_queues;
uint32_t xdp_flags;
bool inhibit;
char *map_path;
int map_fd;
uint32_t map_start_index;
} AFXDPState;
#define AF_XDP_BATCH_SIZE 64
@@ -261,6 +264,7 @@ static void af_xdp_send(void *opaque)
static void af_xdp_cleanup(NetClientState *nc)
{
AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc);
int idx;
qemu_purge_queued_packets(nc);
@@ -275,13 +279,17 @@ static void af_xdp_cleanup(NetClientState *nc)
qemu_vfree(s->buffer);
s->buffer = NULL;
/* Remove the program if it's the last open queue. */
if (!s->inhibit && nc->queue_index == s->n_queues - 1 && s->xdp_flags
&& bpf_xdp_detach(s->ifindex, s->xdp_flags, NULL) != 0) {
fprintf(stderr,
"af-xdp: unable to remove XDP program from '%s', ifindex: %d\n",
s->ifname, s->ifindex);
if (s->map_fd >= 0) {
idx = nc->queue_index + s->map_start_index;
if (bpf_map_delete_elem(s->map_fd, &idx)) {
fprintf(stderr, "af-xdp: unable to remove AF_XDP socket from map"
" %s\n", s->map_path);
}
close(s->map_fd);
s->map_fd = -1;
}
g_free(s->map_path);
s->map_path = NULL;
}
static int af_xdp_umem_create(AFXDPState *s, int sock_fd, Error **errp)
@@ -345,7 +353,6 @@ static int af_xdp_socket_create(AFXDPState *s,
};
int queue_id, error = 0;
s->inhibit = opts->has_inhibit && opts->inhibit;
if (s->inhibit) {
cfg.libxdp_flags |= XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD;
}
@@ -396,6 +403,35 @@ static int af_xdp_socket_create(AFXDPState *s,
return 0;
}
static int af_xdp_update_xsk_map(AFXDPState *s, Error **errp)
{
int xsk_fd, idx, error = 0;
if (!s->map_path) {
return 0;
}
s->map_fd = bpf_obj_get(s->map_path);
if (s->map_fd < 0) {
error = errno;
} else {
xsk_fd = xsk_socket__fd(s->xsk);
idx = s->nc.queue_index + s->map_start_index;
if (bpf_map_update_elem(s->map_fd, &idx, &xsk_fd, 0)) {
error = errno;
}
}
if (error) {
error_setg_errno(errp, error,
"failed to insert AF_XDP socket into map %s",
s->map_path);
return -1;
}
return 0;
}
/* NetClientInfo methods. */
static NetClientInfo net_af_xdp_info = {
.type = NET_CLIENT_DRIVER_AF_XDP,
@@ -444,12 +480,14 @@ int net_init_af_xdp(const Netdev *netdev,
{
const NetdevAFXDPOptions *opts = &netdev->u.af_xdp;
NetClientState *nc, *nc0 = NULL;
int32_t map_start_index;
unsigned int ifindex;
uint32_t prog_id = 0;
g_autofree int *sock_fds = NULL;
int64_t i, queues;
Error *err = NULL;
AFXDPState *s;
bool inhibit;
ifindex = if_nametoindex(opts->ifname);
if (!ifindex) {
@@ -465,8 +503,28 @@ int net_init_af_xdp(const Netdev *netdev,
return -1;
}
if ((opts->has_inhibit && opts->inhibit) != !!opts->sock_fds) {
error_setg(errp, "'inhibit=on' requires 'sock-fds' and vice versa");
inhibit = opts->has_inhibit && opts->inhibit;
if (inhibit && !opts->sock_fds && !opts->map_path) {
error_setg(errp, "'inhibit=on' requires 'sock-fds' or 'map-path'");
return -1;
}
if (!inhibit && (opts->sock_fds || opts->map_path)) {
error_setg(errp, "'sock-fds' and 'map-path' require 'inhibit=on'");
return -1;
}
if (opts->sock_fds && opts->map_path) {
error_setg(errp, "'sock-fds' and 'map-path' are mutually exclusive");
return -1;
}
if (!opts->map_path && opts->has_map_start_index) {
error_setg(errp, "'map-start-index' requires 'map-path'");
return -1;
}
map_start_index = opts->has_map_start_index ? opts->map_start_index : 0;
if (map_start_index < 0) {
error_setg(errp, "'map-start-index' cannot be negative (%d)",
map_start_index);
return -1;
}
@@ -490,21 +548,23 @@ int net_init_af_xdp(const Netdev *netdev,
pstrcpy(s->ifname, sizeof(s->ifname), opts->ifname);
s->ifindex = ifindex;
s->n_queues = queues;
s->inhibit = inhibit;
if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, errp)
|| af_xdp_socket_create(s, opts, errp)) {
/* Make sure the XDP program will be removed. */
s->n_queues = i;
error_propagate(errp, err);
s->map_path = g_strdup(opts->map_path);
s->map_start_index = map_start_index;
s->map_fd = -1;
if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, &err) ||
af_xdp_socket_create(s, opts, &err) ||
af_xdp_update_xsk_map(s, &err)) {
goto err;
}
}
if (nc0) {
if (nc0 && !inhibit) {
s = DO_UPCAST(AFXDPState, nc, nc0);
if (bpf_xdp_query_id(s->ifindex, s->xdp_flags, &prog_id) || !prog_id) {
error_setg_errno(errp, errno,
error_setg_errno(&err, errno,
"no XDP program loaded on '%s', ifindex: %d",
s->ifname, s->ifindex);
goto err;
@@ -518,6 +578,7 @@ int net_init_af_xdp(const Netdev *netdev,
err:
if (nc0) {
qemu_del_net_client(nc0);
error_propagate(errp, err);
}
return -1;

View File

@@ -567,25 +567,34 @@
# (default: 0).
#
# @inhibit: Don't load a default XDP program, use one already loaded
# to the interface (default: false). Requires @sock-fds.
# to the interface (default: false). Requires @sock-fds or @map-path.
#
# @sock-fds: A colon (:) separated list of file descriptors for
# already open but not bound AF_XDP sockets in the queue order.
# One fd per queue. These descriptors should already be added
# into XDP socket map for corresponding queues. Requires
# @inhibit.
# into XDP socket map for corresponding queues. @sock-fds and
# @map-path are mutually exclusive. Requires @inhibit.
#
# @map-path: The path to a pinned xsk map to push file descriptors
# for bound AF_XDP sockets into. @map-path and @sock-fds are
# mutually exclusive. Requires @inhibit. (Since 10.1)
#
# @map-start-index: Use @map-path to insert xsk sockets starting from
# this index number (default: 0). Requires @map-path. (Since 10.1)
#
# Since: 8.2
##
{ 'struct': 'NetdevAFXDPOptions',
'data': {
'ifname': 'str',
'*mode': 'AFXDPMode',
'*force-copy': 'bool',
'*queues': 'int',
'*start-queue': 'int',
'*inhibit': 'bool',
'*sock-fds': 'str' },
'ifname': 'str',
'*mode': 'AFXDPMode',
'*force-copy': 'bool',
'*queues': 'int',
'*start-queue': 'int',
'*inhibit': 'bool',
'*sock-fds': 'str',
'*map-path': 'str',
'*map-start-index': 'int32' },
'if': 'CONFIG_AF_XDP' }
##

View File

@@ -2934,6 +2934,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
#ifdef CONFIG_AF_XDP
"-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off]\n"
" [,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]\n"
" [,map-path=/path/to/socket/map][,map-start-index=i]\n"
" attach to the existing network interface 'name' with AF_XDP socket\n"
" use 'mode=MODE' to specify an XDP program attach mode\n"
" use 'force-copy=on|off' to force XDP copy mode even if device supports zero-copy (default: off)\n"
@@ -2941,6 +2942,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
" with inhibit=on,\n"
" use 'sock-fds' to provide file descriptors for already open AF_XDP sockets\n"
" added to a socket map in XDP program. One socket per queue.\n"
" use 'map-path' to provide the socket map location to populate AF_XDP sockets with,\n"
" and use 'map-start-index' to specify the starting index for the map (default: 0) (Since 10.1)\n"
" use 'queues=n' to specify how many queues of a multiqueue interface should be used\n"
" use 'start-queue=m' to specify the first queue that should be used\n"
#endif
@@ -3764,7 +3767,7 @@ SRST
# launch QEMU instance
|qemu_system| linux.img -nic vde,sock=/tmp/myswitch
``-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off][,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]``
``-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off][,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z][,map-path=/path/to/socket/map][,map-start-index=i]``
Configure AF_XDP backend to connect to a network interface 'name'
using AF_XDP socket. A specific program attach mode for a default
XDP program can be forced with 'mode', defaults to best-effort,
@@ -3804,7 +3807,8 @@ SRST
-netdev af-xdp,id=n1,ifname=eth0,queues=1,start-queue=1
XDP program can also be loaded externally. In this case 'inhibit' option
should be set to 'on' and 'sock-fds' provided with file descriptors for
should be set to 'on'. Either 'sock-fds' or 'map-path' can be used with
'inhibit' enabled. 'sock-fds' can be provided with file descriptors for
already open but not bound XDP sockets already added to a socket map for
corresponding queues. One socket per queue.
@@ -3813,6 +3817,21 @@ SRST
|qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\
-netdev af-xdp,id=n1,ifname=eth0,queues=3,inhibit=on,sock-fds=15:16:17
For the 'inhibit' option set to 'on' used together with 'map-path' it is
expected that the XDP program with the socket map is already loaded on
the networking device and the map pinned into BPF file system. The path
to the pinned map is then passed to QEMU which then creates the file
descriptors and inserts them into the existing socket map.
.. parsed-literal::
|qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\
-netdev af-xdp,id=n1,ifname=eth0,queues=2,inhibit=on,map-path=/sys/fs/bpf/xsks_map
Additionally, 'map-start-index' can be used to specify the start offset
for insertion into the socket map. The combination of 'map-path' and
'sock-fds' together is not supported.
``-netdev vhost-user,chardev=id[,vhostforce=on|off][,queues=n]``
Establish a vhost-user netdev, backed by a chardev id. The chardev
should be a unix domain socket backed one. The vhost-user uses a