uint32_t xdp_flags;
bool inhibit;
+
+ char *map_path;
+ int map_fd;
+ uint32_t map_start_index;
} AFXDPState;
#define AF_XDP_BATCH_SIZE 64
static void af_xdp_cleanup(NetClientState *nc)
{
AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc);
+ int idx;
qemu_purge_queued_packets(nc);
s->umem = NULL;
qemu_vfree(s->buffer);
s->buffer = NULL;
+
+ 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)
};
int queue_id, error = 0;
- s->inhibit = opts->has_inhibit && opts->inhibit;
if (s->inhibit) {
cfg.libxdp_flags |= XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD;
}
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,
{
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) {
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;
}
pstrcpy(s->ifname, sizeof(s->ifname), opts->ifname);
s->ifindex = ifindex;
+ s->inhibit = inhibit;
+
+ 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_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(&err, errno,
# (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' }
##
#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"
" 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
# 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,
-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.
|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