Skip to contentSkip to conversation

virtio: introduce support for GSO over UDP tunnel

RFC PATCH Superseded Paolo Abeni 3 participants 37 messages Jul 11, 2025 – Jul 18, 2025

Subsystems (2)

v2 of 7 Superseded Latest is v7 →

lore
Share & tools
lore

Paolo Abeni

author maintainer
#0 lore raw

v2 cover virtio: introduce support for GSO over UDP tunnel

Some virtualized deployments use UDP tunnel pervasively and are impacted negatively by the lack of GSO support for such kind of traffic in the virtual NIC driver.

The virtio_net specification recently introduced support for GSO over UDP tunnel, and the kernel side of the implementation has been merged into the net-next tree; this series updates the virtio implementation to support such a feature.

Currently the qemu virtio support limits the feature space to 64 bits, while the virtio specification allows for a larger number of features. Specifically the GSO-over-UDP-tunnel-related virtio features use bits 65-69; the larger part of this series (patches 3-11) actually deals with extending the features space.

The extended features are carried by fixed size uint64_t arrays, bringing the current maximum features number to 128.

The patches use some syntactic sugar to try to minimize the otherwise very large code churn. Specifically the extended features are boundled in an union with 'legacy' features definition, allowing no changes in the virtio devices not needing the extended features set.

The actual offload implementation is in patches 12 and 13 and boils down to propagating the new offload to the tun devices and the vhost backend.

Finally patch 1 is a small pre-req refactor that ideally could enter the tree separately; it's presented here in the same series to help reviewers more easily getting the full picture and patch 2 is a needed linux headers update.

Tested with basic stream transfer with all the possible permutations of host kernel/qemu/guest kernel with/without GSO over UDP tunnel support, vs snapshots creation and restore and vs migration.

Sharing again as RFC as the kernel bits have not entered the Linus tree yet - but they should on next merge window.

Paolo Abeni (13):
net: bundle all offloads in a single struct
linux-headers: Update to Linux ~v6.16-rc5 net-next
virtio: introduce extended features type
virtio: serialize extended features state
virtio: add support for negotiating extended features
virtio-pci: implement support for extended features
vhost: add support for negotiating extended features
qmp: update virtio features map to support extended features
vhost-backend: implement extended features support
vhost-net: implement extended features support
virtio-net: implement extended features support
net: implement tunnel probing
net: implement UDP tunnel features offloading

 hw/net/e1000e_core.c                         |   5 +-
 hw/net/igb_core.c                            |   5 +-
 hw/net/vhost_net-stub.c                      |   8 +-
 hw/net/vhost_net.c                           |  50 +++--
 hw/net/virtio-net.c                          | 215 +++++++++++++------
 hw/net/vmxnet3.c                             |  13 +-
 hw/virtio/vhost-backend.c                    |  62 +++++-
 hw/virtio/vhost.c                            |  73 ++++++-
 hw/virtio/virtio-bus.c                       |  11 +-
 hw/virtio/virtio-hmp-cmds.c                  |   3 +-
 hw/virtio/virtio-pci.c                       | 101 ++++++++-
 hw/virtio/virtio-qmp.c                       |  89 +++++---
 hw/virtio/virtio-qmp.h                       |   3 +-
 hw/virtio/virtio.c                           | 111 ++++++++--
 include/hw/virtio/vhost-backend.h            |   6 +
 include/hw/virtio/vhost.h                    |  36 +++-
 include/hw/virtio/virtio-features.h          | 124 +++++++++++
 include/hw/virtio/virtio-net.h               |   2 +-
 include/hw/virtio/virtio-pci.h               |   6 +-
 include/hw/virtio/virtio.h                   |  11 +-
 include/net/net.h                            |  20 +-
 include/net/vhost_net.h                      |  33 ++-
 include/standard-headers/linux/ethtool.h     |   4 +-
 include/standard-headers/linux/vhost_types.h |   5 +
 include/standard-headers/linux/virtio_net.h  |  33 +++
 linux-headers/asm-x86/kvm.h                  |   8 +-
 linux-headers/linux/kvm.h                    |   4 +
 linux-headers/linux/vhost.h                  |   7 +
 net/net.c                                    |  17 +-
 net/netmap.c                                 |   3 +-
 net/tap-bsd.c                                |   8 +-
 net/tap-linux.c                              |  38 +++-
 net/tap-linux.h                              |   9 +
 net/tap-solaris.c                            |   9 +-
 net/tap-stub.c                               |   8 +-
 net/tap.c                                    |  19 +-
 net/tap_int.h                                |   5 +-
 qapi/virtio.json                             |   8 +-
 38 files changed, 945 insertions(+), 227 deletions(-)
 create mode 100644 include/hw/virtio/virtio-features.h

--
2.50.0

author maintainer
#1 lore raw

RFC PATCH v2 1/13 net: bundle all offloads in a single struct

The set_offload() argument list is already pretty long and we are going to introduce soon a bunch of additional offloads.

Replace the offload arguments with a single struct and update all the relevant call-sites.

No functional changes intended.

---
Note: I maintained the struct usage as opposed to uint64_t bitmask usage as suggested by Akihiko, because the latter feel a bit more invasive.

v1 -> v2:
- drop unneeded 'struct' keywords
- moved to series start

---
 hw/net/e1000e_core.c |  5 +++--
 hw/net/igb_core.c    |  5 +++--
 hw/net/virtio-net.c  | 19 +++++++++++--------
 hw/net/vmxnet3.c     | 13 +++++--------
 include/net/net.h    | 15 ++++++++++++---
 net/net.c            |  5 ++---
 net/netmap.c         |  3 +--
 net/tap-bsd.c        |  3 +--
 net/tap-linux.c      | 21 ++++++++++++---------
 net/tap-solaris.c    |  4 ++--
 net/tap-stub.c       |  3 +--
 net/tap.c            |  8 ++++----
 net/tap_int.h        |  4 ++--
 13 files changed, 59 insertions(+), 49 deletions(-)
hw/net/e1000e_core.c +3 -2
index 2413858790..27599a0dc2 100644
@@ -2827,8 +2827,9 @@ e1000e_update_rx_offloads(E1000ECore *core)
     trace_e1000e_rx_set_cso(cso_state);
 
     if (core->has_vnet) {
-        qemu_set_offload(qemu_get_queue(core->owner_nic)->peer,
-                         cso_state, 0, 0, 0, 0, 0, 0);
+        NetOffloads ol = {.csum = cso_state };
+
+        qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, &ol);
     }
 }
 
hw/net/igb_core.c +3 -2
index 39e3ce1c8f..45d8fd795b 100644
@@ -3058,8 +3058,9 @@ igb_update_rx_offloads(IGBCore *core)
     trace_e1000e_rx_set_cso(cso_state);
 
     if (core->has_vnet) {
-        qemu_set_offload(qemu_get_queue(core->owner_nic)->peer,
-                         cso_state, 0, 0, 0, 0, 0, 0);
+        NetOffloads ol = {.csum = cso_state };
+
+        qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, &ol);
     }
 }
 
hw/net/virtio-net.c +11 -8
index eb93607b8c..16df9e85c8 100644
@@ -842,14 +842,17 @@ static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
 
 static void virtio_net_apply_guest_offloads(VirtIONet *n)
 {
-    qemu_set_offload(qemu_get_queue(n->nic)->peer,
-            !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
-            !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
-            !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
-            !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
-            !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)),
-            !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)),
-            !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6)));
+    NetOffloads ol = {
+       .csum = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
+       .tso4 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
+       .tso6 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
+       .ecn  = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
+       .ufo  = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)),
+       .uso4 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)),
+       .uso6 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6)),
+    };
+
+    qemu_set_offload(qemu_get_queue(n->nic)->peer, &ol);
 }
 
 static uint64_t virtio_net_guest_offloads_by_features(uint64_t features)
hw/net/vmxnet3.c +5 -8
index 7c0ca56b7c..57e457e758 100644
@@ -1323,14 +1323,11 @@ static void vmxnet3_update_features(VMXNET3State *s)
               s->lro_supported, rxcso_supported,
               s->rx_vlan_stripping);
     if (s->peer_has_vhdr) {
-        qemu_set_offload(qemu_get_queue(s->nic)->peer,
-                         rxcso_supported,
-                         s->lro_supported,
-                         s->lro_supported,
-                         0,
-                         0,
-                         0,
-                         0);
+        NetOffloads ol = { .csum = rxcso_supported,
+                           .tso4 = s->lro_supported,
+                           .tso6 = s->lro_supported };
+
+        qemu_set_offload(qemu_get_queue(s->nic)->peer, &ol);
     }
 }
 
include/net/net.h +12 -3
index cdd5b109b0..5edea7671a 100644
@@ -35,6 +35,16 @@ typedef struct NICConf {
     int32_t bootindex;
 } NICConf;
 
+typedef struct NetOffloads {
+    bool csum;
+    bool tso4;
+    bool tso6;
+    bool ecn;
+    bool ufo;
+    bool uso4;
+    bool uso6;
+} NetOffloads;
+
 #define DEFINE_NIC_PROPERTIES(_state, _conf)                            \
     DEFINE_PROP_MACADDR("mac",   _state, _conf.macaddr),                \
     DEFINE_PROP_NETDEV("netdev", _state, _conf.peers)
@@ -57,7 +67,7 @@ typedef bool (HasUfo)(NetClientState *);
 typedef bool (HasUso)(NetClientState *);
 typedef bool (HasVnetHdr)(NetClientState *);
 typedef bool (HasVnetHdrLen)(NetClientState *, int);
-typedef void (SetOffload)(NetClientState *, int, int, int, int, int, int, int);
+typedef void (SetOffload)(NetClientState *, const NetOffloads *);
 typedef int (GetVnetHdrLen)(NetClientState *);
 typedef void (SetVnetHdrLen)(NetClientState *, int);
 typedef int (SetVnetLE)(NetClientState *, bool);
@@ -185,8 +195,7 @@ bool qemu_has_ufo(NetClientState *nc);
 bool qemu_has_uso(NetClientState *nc);
 bool qemu_has_vnet_hdr(NetClientState *nc);
 bool qemu_has_vnet_hdr_len(NetClientState *nc, int len);
-void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
-                      int ecn, int ufo, int uso4, int uso6);
+void qemu_set_offload(NetClientState *nc, const NetOffloads *ol);
 int qemu_get_vnet_hdr_len(NetClientState *nc);
 void qemu_set_vnet_hdr_len(NetClientState *nc, int len);
 int qemu_set_vnet_le(NetClientState *nc, bool is_le);
net/net.c +2 -3
index 39d6f28158..053db7c314 100644
@@ -540,14 +540,13 @@ bool qemu_has_vnet_hdr_len(NetClientState *nc, int len)
     return nc->info->has_vnet_hdr_len(nc, len);
 }
 
-void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
-                          int ecn, int ufo, int uso4, int uso6)
+void qemu_set_offload(NetClientState *nc, const NetOffloads *ol)
 {
     if (!nc || !nc->info->set_offload) {
         return;
     }
 
-    nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo, uso4, uso6);
+    nc->info->set_offload(nc, ol);
 }
 
 int qemu_get_vnet_hdr_len(NetClientState *nc)
net/netmap.c +1 -2
index 297510e190..6cd8f2bdc5 100644
@@ -366,8 +366,7 @@ static void netmap_set_vnet_hdr_len(NetClientState *nc, int len)
     }
 }
 
-static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
-                               int ecn, int ufo, int uso4, int uso6)
+static void netmap_set_offload(NetClientState *nc, const NetOffloads *ol)
 {
     NetmapState *s = DO_UPCAST(NetmapState, nc, nc);
 
net/tap-bsd.c +1 -2
index b4c84441ba..86b6edee94 100644
@@ -231,8 +231,7 @@ int tap_fd_set_vnet_be(int fd, int is_be)
     return -EINVAL;
 }
 
-void tap_fd_set_offload(int fd, int csum, int tso4,
-                        int tso6, int ecn, int ufo, int uso4, int uso6)
+void tap_fd_set_offload(int fd, const NetOffloads *ol)
 {
 }
 
net/tap-linux.c +12 -9
index 22ec2f45d2..a1c58f74f5 100644
@@ -239,8 +239,7 @@ int tap_fd_set_vnet_be(int fd, int is_be)
     abort();
 }
 
-void tap_fd_set_offload(int fd, int csum, int tso4,
-                        int tso6, int ecn, int ufo, int uso4, int uso6)
+void tap_fd_set_offload(int fd, const NetOffloads *ol)
 {
     unsigned int offload = 0;
 
@@ -249,20 +248,24 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
         return;
     }
 
-    if (csum) {
+    if (ol->csum) {
         offload |= TUN_F_CSUM;
-        if (tso4)
+        if (ol->tso4) {
             offload |= TUN_F_TSO4;
-        if (tso6)
+        }
+        if (ol->tso6) {
             offload |= TUN_F_TSO6;
-        if ((tso4 || tso6) && ecn)
+        }
+        if ((ol->tso4 || ol->tso6) && ol->ecn) {
             offload |= TUN_F_TSO_ECN;
-        if (ufo)
+        }
+        if (ol->ufo) {
             offload |= TUN_F_UFO;
-        if (uso4) {
+        }
+        if (ol->uso4) {
             offload |= TUN_F_USO4;
         }
-        if (uso6) {
+        if (ol->uso6) {
             offload |= TUN_F_USO6;
         }
     }
net/tap-solaris.c +2 -2
index 51b7830bef..833c066bee 100644
@@ -27,6 +27,7 @@
 #include "tap_int.h"
 #include "qemu/ctype.h"
 #include "qemu/cutils.h"
+#include "net/net.h"
 
 #include <sys/ethernet.h>
 #include <sys/sockio.h>
@@ -235,8 +236,7 @@ int tap_fd_set_vnet_be(int fd, int is_be)
     return -EINVAL;
 }
 
-void tap_fd_set_offload(int fd, int csum, int tso4,
-                        int tso6, int ecn, int ufo, int uso4, int uso6)
+void tap_fd_set_offload(int fd, const NetOffloads *ol)
 {
 }
 
net/tap-stub.c +1 -2
index 38673434cb..67d14ad4d5 100644
@@ -66,8 +66,7 @@ int tap_fd_set_vnet_be(int fd, int is_be)
     return -EINVAL;
 }
 
-void tap_fd_set_offload(int fd, int csum, int tso4,
-                        int tso6, int ecn, int ufo, int uso4, int uso6)
+void tap_fd_set_offload(int fd, const NetOffloads *ol)
 {
 }
 
net/tap.c +4 -4
index ae1c7e3983..13e19130ce 100644
@@ -262,15 +262,14 @@ static int tap_set_vnet_be(NetClientState *nc, bool is_be)
     return tap_fd_set_vnet_be(s->fd, is_be);
 }
 
-static void tap_set_offload(NetClientState *nc, int csum, int tso4,
-                     int tso6, int ecn, int ufo, int uso4, int uso6)
+static void tap_set_offload(NetClientState *nc, const NetOffloads *ol)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
     if (s->fd < 0) {
         return;
     }
 
-    tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo, uso4, uso6);
+    tap_fd_set_offload(s->fd, ol);
 }
 
 static void tap_exit_notify(Notifier *notifier, void *data)
@@ -355,6 +354,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
                                  int fd,
                                  int vnet_hdr)
 {
+    NetOffloads ol = {};
     NetClientState *nc;
     TAPState *s;
 
@@ -368,7 +368,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
     s->has_ufo = tap_probe_has_ufo(s->fd);
     s->has_uso = tap_probe_has_uso(s->fd);
     s->enabled = true;
-    tap_set_offload(&s->nc, 0, 0, 0, 0, 0, 0, 0);
+    tap_set_offload(&s->nc, &ol);
     /*
      * Make sure host header length is set correctly in tap:
      * it might have been modified by another instance of qemu.
net/tap_int.h +2 -2
index 8857ff299d..f8bbe1cb0c 100644
@@ -27,6 +27,7 @@
 #define NET_TAP_INT_H
 
 #include "qapi/qapi-types-net.h"
+#include "net/net.h"
 
 int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
              int vnet_hdr_required, int mq_required, Error **errp);
@@ -37,8 +38,7 @@ void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp);
 int tap_probe_vnet_hdr(int fd, Error **errp);
 int tap_probe_has_ufo(int fd);
 int tap_probe_has_uso(int fd);
-void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo,
-                        int uso4, int uso6);
+void tap_fd_set_offload(int fd, const NetOffloads *ol);
 void tap_fd_set_vnet_hdr_len(int fd, int len);
 int tap_fd_set_vnet_le(int fd, int vnet_is_le);
 int tap_fd_set_vnet_be(int fd, int vnet_is_be);
Signature · 1 line
2.50.0
#2 lore raw

Re: [PATCH RFC v2 01/13] net: bundle all offloads in a single struct

Quoted 6 lines · Paolo Abeni

The set_offload() argument list is already pretty long and we are going to introduce soon a bunch of additional offloads.

Replace the offload arguments with a single struct and update all the relevant call-sites.

Quoted 6 lines

No functional changes intended.

---
Note: I maintained the struct usage as opposed to uint64_t bitmask usage as suggested by Akihiko, because the latter feel a bit more invasive.

I think a bitmask will be invasive to the same extent with the current version; most part of this change comes from the parameter passing, which does not depend on the representation of the parameter.

author maintainer
#3 lore raw

Re: [PATCH RFC v2 01/13] net: bundle all offloads in a single struct

Quoted 9 lines · Akihiko Odaki
Quoted 8 lines · Paolo Abeni

The set_offload() argument list is already pretty long and we are going to introduce soon a bunch of additional offloads.

Replace the offload arguments with a single struct and update all the relevant call-sites.

No functional changes intended.

Quoted 8 lines
Quoted 4 lines

---
Note: I maintained the struct usage as opposed to uint64_t bitmask usage as suggested by Akihiko, because the latter feel a bit more invasive.

I think a bitmask will be invasive to the same extent with the current version; most part of this change comes from the parameter passing, which does not depend on the representation of the parameter.

Do you have strong feeling WRT the bitmask usage?

Another argument vs the bitmask usage is that it will requires some extra input validation of the selected offload bits (most of them don't make sense in this context).

Thanks,

Paolo

#4 lore raw

Re: [PATCH RFC v2 01/13] net: bundle all offloads in a single struct

Quoted 19 lines · Paolo Abeni
Quoted 9 lines · Akihiko Odaki
Quoted 8 lines · Paolo Abeni

The set_offload() argument list is already pretty long and we are going to introduce soon a bunch of additional offloads.

Replace the offload arguments with a single struct and update all the relevant call-sites.

No functional changes intended.

Quoted 8 lines
Quoted 4 lines

---
Note: I maintained the struct usage as opposed to uint64_t bitmask usage as suggested by Akihiko, because the latter feel a bit more invasive.

I think a bitmask will be invasive to the same extent with the current version; most part of this change comes from the parameter passing, which does not depend on the representation of the parameter.

Quoted 5 lines

Do you have strong feeling WRT the bitmask usage?

Another argument vs the bitmask usage is that it will requires some extra input validation of the selected offload bits (most of them don't make sense in this context).

I don't think such a validation is necessary.

There is practically no chance to have a wrong bit set by mistake when callers specify bits with macros prefixed with VIRTIO_NET_O_GUEST_. There will be a compilation error if the caller specify a offload bit that doesn't exist.

It is also obvious if a caller specified something unrelated (i.e., not prefixed with VIRTIO_NET_O_GUEST_). A real downside here would be that we will need to type VIRTIO_NET_O_GUEST_ each time referring an offload bit; such a redundancy does not exist with struct because the type system knows the bits bound to the type.

That said, 1) I prefer bitmasks much over struct though 2) I will be in favor of merging this series if everything else gets sorted out while the struct remains.

I'll explain the reason for 1) first:

There are both a downside and an upside with bitmasks. The downside is the redundancy of syntax, which I have just pointed out. The upside is the consistency with virtio's offload configuration, which defines the functionality of set_offload().

I consider the following two factors in such a trade-off scenario:
a) The balance between the downside and upside
b) Prior examples in the code base

Regarding a), I think the upside outweighs the downside but its extent is small.

b) matters more for this particular case; there are bunch of examples that use bitmasks in "include", excluding "include/hw" but there are none of structs that are dedicated for bools. Consistency matters for a big code base like QEMU so I want a good reason when making an exception.

The reasoning behind 2) is that having this patch is still better than the status quo.

Regards,
Akihiko Odaki

author maintainer
#5 lore raw

RFC PATCH v2 2/13 linux-headers: Update to Linux ~v6.16-rc5 net-next

In reply to Paolo Abeni

Update headers to include the virtio GSO over UDP tunnel features

--
Note: while the relevant change are not into Linus's tree yet, they have been merged in the net-next tree and they should land into the vanilla tree during the next merge window.

---
 include/standard-headers/linux/ethtool.h     |  4 +--
 include/standard-headers/linux/vhost_types.h |  5 +++
 include/standard-headers/linux/virtio_net.h  | 33 ++++++++++++++++++++
 linux-headers/asm-x86/kvm.h                  |  8 ++++-
 linux-headers/linux/kvm.h                    |  4 +++
 linux-headers/linux/vhost.h                  |  7 +++++
 6 files changed, 58 insertions(+), 3 deletions(-)
include/standard-headers/linux/ethtool.h +2 -2
index cef0d207a6..eb80314028 100644
@@ -2314,7 +2314,7 @@ enum {
 	IPV6_USER_FLOW	= 0x0e, /* spec only (usr_ip6_spec; nfc only) */
 	IPV4_FLOW	= 0x10, /* hash only */
 	IPV6_FLOW	= 0x11, /* hash only */
-	ETHER_FLOW	= 0x12, /* spec only (ether_spec) */
+	ETHER_FLOW	= 0x12, /* hash or spec (ether_spec) */
 
 	/* Used for GTP-U IPv4 and IPv6.
 	 * The format of GTP packets only includes
@@ -2371,7 +2371,7 @@ enum {
 /* Flag to enable RSS spreading of traffic matching rule (nfc only) */
 #define	FLOW_RSS	0x20000000
 
-/* L3-L4 network traffic flow hash options */
+/* L2-L4 network traffic flow hash options */
 #define	RXH_L2DA	(1 << 1)
 #define	RXH_VLAN	(1 << 2)
 #define	RXH_L3_PROTO	(1 << 3)
include/standard-headers/linux/vhost_types.h +5 -0
index fd54044936..4a3aacc39e 100644
@@ -110,6 +110,11 @@ struct vhost_msg_v2 {
 	};
 };
 
+struct vhost_features_array {
+	uint64_t count; /* number of entries present in features array */
+	uint64_t features[] __counted_by(count);
+};
+
 struct vhost_memory_region {
 	uint64_t guest_phys_addr;
 	uint64_t memory_size; /* bytes */
include/standard-headers/linux/virtio_net.h +33 -0
index 982e854f14..93abaae0b9 100644
@@ -70,6 +70,28 @@
 					 * with the same MAC.
 					 */
 #define VIRTIO_NET_F_SPEED_DUPLEX 63	/* Device set linkspeed and duplex */
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO 65 /* Driver can receive
+					      * GSO-over-UDP-tunnel packets
+					      */
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM 66 /* Driver handles
+						   * GSO-over-UDP-tunnel
+						   * packets with partial csum
+						   * for the outer header
+						   */
+#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO 67 /* Device can receive
+					     * GSO-over-UDP-tunnel packets
+					     */
+#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM 68 /* Device handles
+						  * GSO-over-UDP-tunnel
+						  * packets with partial csum
+						  * for the outer header
+						  */
+
+/* Offloads bits corresponding to VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO{,_CSUM}
+ * features
+ */
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED	46
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED	47
 
 #ifndef VIRTIO_NET_NO_LEGACY
 #define VIRTIO_NET_F_GSO	6	/* Host handles pkts w/ any GSO type */
@@ -131,12 +153,17 @@ struct virtio_net_hdr_v1 {
 #define VIRTIO_NET_HDR_F_NEEDS_CSUM	1	/* Use csum_start, csum_offset */
 #define VIRTIO_NET_HDR_F_DATA_VALID	2	/* Csum is valid */
 #define VIRTIO_NET_HDR_F_RSC_INFO	4	/* rsc info in csum_ fields */
+#define VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM 8	/* UDP tunnel csum offload */
 	uint8_t flags;
 #define VIRTIO_NET_HDR_GSO_NONE		0	/* Not a GSO frame */
 #define VIRTIO_NET_HDR_GSO_TCPV4	1	/* GSO frame, IPv4 TCP (TSO) */
 #define VIRTIO_NET_HDR_GSO_UDP		3	/* GSO frame, IPv4 UDP (UFO) */
 #define VIRTIO_NET_HDR_GSO_TCPV6	4	/* GSO frame, IPv6 TCP */
 #define VIRTIO_NET_HDR_GSO_UDP_L4	5	/* GSO frame, IPv4& IPv6 UDP (USO) */
+#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 0x20 /* UDPv4 tunnel present */
+#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 0x40 /* UDPv6 tunnel present */
+#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL (VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 | \
+				       VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6)
 #define VIRTIO_NET_HDR_GSO_ECN		0x80	/* TCP has ECN set */
 	uint8_t gso_type;
 	__virtio16 hdr_len;	/* Ethernet + IP + tcp/udp hdrs */
@@ -181,6 +208,12 @@ struct virtio_net_hdr_v1_hash {
 	uint16_t padding;
 };
 
+struct virtio_net_hdr_v1_hash_tunnel {
+	struct virtio_net_hdr_v1_hash hash_hdr;
+	uint16_t outer_th_offset;
+	uint16_t inner_nh_offset;
+};
+
 #ifndef VIRTIO_NET_NO_LEGACY
 /* This header comes first in the scatter-gather list.
  * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must
linux-headers/asm-x86/kvm.h +7 -1
index cd275ae76d..f0c1a730d9 100644
@@ -963,7 +963,13 @@ struct kvm_tdx_cmd {
 struct kvm_tdx_capabilities {
 	__u64 supported_attrs;
 	__u64 supported_xfam;
-	__u64 reserved[254];
+
+	__u64 kernel_tdvmcallinfo_1_r11;
+	__u64 user_tdvmcallinfo_1_r11;
+	__u64 kernel_tdvmcallinfo_1_r12;
+	__u64 user_tdvmcallinfo_1_r12;
+
+	__u64 reserved[250];
 
 	/* Configurable CPUID bits for userspace */
 	struct kvm_cpuid2 cpuid;
linux-headers/linux/kvm.h +4 -0
index 0690743944..32c5885a3c 100644
@@ -459,6 +459,10 @@ struct kvm_run {
 					__u64 leaf;
 					__u64 r11, r12, r13, r14;
 				} get_tdvmcall_info;
+				struct {
+					__u64 ret;
+					__u64 vector;
+				} setup_event_notify;
 			};
 		} tdx;
 		/* Fix the size of the union. */
linux-headers/linux/vhost.h +7 -0
index d4b3e2ae13..d6ad01fbb8 100644
@@ -235,4 +235,11 @@
  */
 #define VHOST_VDPA_GET_VRING_SIZE	_IOWR(VHOST_VIRTIO, 0x82,	\
 					      struct vhost_vring_state)
+
+/* Extended features manipulation */
+#define VHOST_GET_FEATURES_ARRAY _IOR(VHOST_VIRTIO, 0x83, \
+				       struct vhost_features_array)
+#define VHOST_SET_FEATURES_ARRAY _IOW(VHOST_VIRTIO, 0x83, \
+				       struct vhost_features_array)
+
 #endif
Signature · 1 line
2.50.0
author maintainer
#6 lore raw

RFC PATCH v2 3/13 virtio: introduce extended features type

In reply to Paolo Abeni

The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the virtio net driver.

Represent the virtio features bitmask with a fixes size array, and introduce a few helpers to help manipulate them.

Most drivers will keep using only 64 bits features space: use union to allow them access the lower part of the extended space without any per driver change.

---
v1 -> v2:
- use a fixed size array for features instead of uint128
- use union with u64 to reduce the needed code churn

---
 include/hw/virtio/virtio-features.h | 124 ++++++++++++++++++++++++++++
 include/hw/virtio/virtio.h          |   7 +-
 2 files changed, 128 insertions(+), 3 deletions(-)
 create mode 100644 include/hw/virtio/virtio-features.h
include/hw/virtio/virtio-features.h new file +124 -0
new file mode 100644
index 0000000000..cc735f7f81
@@ -0,0 +1,124 @@
+/*
+ * Virtio features helpers
+ *
+ * Copyright 2025 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _QEMU_VIRTIO_FEATURES_H
+#define _QEMU_VIRTIO_FEATURES_H
+
+#define VIRTIO_FEATURES_FMT        "%016"PRIx64"%016"PRIx64
+#define VIRTIO_FEATURES_PR(f)      f[1], f[0]
+
+#define VIRTIO_FEATURES_MAX        128
+#define VIRTIO_BIT(b)              (1ULL << (b & 0x3f))
+#define VIRTIO_DWORD(b)            ((b) >> 6)
+#define VIRTIO_FEATURES_WORDS     (VIRTIO_FEATURES_MAX >> 5)
+#define VIRTIO_FEATURES_DWORDS      (VIRTIO_FEATURES_WORDS >> 1)
+
+#define VIRTIO_DECLARE_FEATURES(name)                        \
+    union {                                                  \
+        uint64_t name;                                       \
+        uint64_t name##_array[VIRTIO_FEATURES_DWORDS];       \
+    }
+
+static inline void virtio_features_clear(uint64_t *features)
+{
+    memset(features, 0, sizeof(features[0]) * VIRTIO_FEATURES_DWORDS);
+}
+
+static inline void virtio_features_from_u64(uint64_t *features, uint64_t from)
+{
+    virtio_features_clear(features);
+    features[0] = from;
+}
+
+static inline bool virtio_has_feature_ex(const uint64_t *features,
+                                         unsigned int fbit)
+{
+    assert(fbit < VIRTIO_FEATURES_MAX);
+    return features[VIRTIO_DWORD(fbit)] & VIRTIO_BIT(fbit);
+}
+
+static inline void virtio_add_feature_ex(uint64_t *features,
+                                         unsigned int fbit)
+{
+    assert(fbit < VIRTIO_FEATURES_MAX);
+    features[VIRTIO_DWORD(fbit)] |= VIRTIO_BIT(fbit);
+}
+
+static inline void virtio_clear_feature_ex(uint64_t *features,
+                                           unsigned int fbit)
+{
+    assert(fbit < VIRTIO_FEATURES_MAX);
+    features[VIRTIO_DWORD(fbit)] &= ~VIRTIO_BIT(fbit);
+}
+
+static inline bool virtio_features_equal(const uint64_t *f1,
+                                         const uint64_t *f2)
+{
+    uint64_t diff = 0;
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; ++i) {
+        diff |= f1[i] ^ f2[i];
+    }
+    return !!diff;
+}
+
+static inline bool virtio_features_use_extended(const uint64_t *features)
+{
+    int i;
+
+    for (i = 1; i < VIRTIO_FEATURES_DWORDS; ++i) {
+        if (features[i]) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static inline bool virtio_features_is_empty(const uint64_t *features)
+{
+    return !virtio_features_use_extended(features) && !features[0];
+}
+
+static inline void virtio_features_copy(uint64_t *to, const uint64_t *from)
+{
+    memcpy(to, from, sizeof(to[0]) * VIRTIO_FEATURES_DWORDS);
+}
+
+static inline void virtio_features_andnot(uint64_t *to, const uint64_t *f1,
+                                           const uint64_t *f2)
+{
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; i++) {
+        to[i] = f1[i] & ~f2[i];
+    }
+}
+
+static inline void virtio_features_and(uint64_t *to, const uint64_t *f1,
+                                       const uint64_t *f2)
+{
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; i++) {
+        to[i] = f1[i] & f2[i];
+    }
+}
+
+static inline void virtio_features_or(uint64_t *to, const uint64_t *f1,
+                                       const uint64_t *f2)
+{
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; i++) {
+        to[i] = f1[i] | f2[i];
+    }
+}
+
+#endif
+
include/hw/virtio/virtio.h +4 -3
index 214d4a77e9..0d1eb20489 100644
@@ -16,6 +16,7 @@
 
 #include "system/memory.h"
 #include "hw/qdev-core.h"
+#include "hw/virtio/virtio-features.h"
 #include "net/net.h"
 #include "migration/vmstate.h"
 #include "qemu/event_notifier.h"
@@ -121,9 +122,9 @@ struct VirtIODevice
      * backend (e.g. vhost) and could potentially be a subset of the
      * total feature set offered by QEMU.
      */
-    uint64_t host_features;
-    uint64_t guest_features;
-    uint64_t backend_features;
+    VIRTIO_DECLARE_FEATURES(host_features);
+    VIRTIO_DECLARE_FEATURES(guest_features);
+    VIRTIO_DECLARE_FEATURES(backend_features);
 
     size_t config_len;
     void *config;
Signature · 1 line
2.50.0
#7 lore raw

Re: [PATCH RFC v2 03/13] virtio: introduce extended features type

Quoted 28 lines · Paolo Abeni

The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the virtio net driver.

Represent the virtio features bitmask with a fixes size array, and introduce a few helpers to help manipulate them.

Most drivers will keep using only 64 bits features space: use union to allow them access the lower part of the extended space without any per driver change.

---
v1 -> v2:
- use a fixed size array for features instead of uint128
- use union with u64 to reduce the needed code churn

---
  include/hw/virtio/virtio-features.h | 124 ++++++++++++++++++++++++++++
  include/hw/virtio/virtio.h          |   7 +-
  2 files changed, 128 insertions(+), 3 deletions(-)

create mode 100644 include/hw/virtio/virtio-features.h

include/hw/virtio/virtio-features.h new file +0 -0
new file mode 100644
index 0000000000..cc735f7f81

include/hw/virtio/virtio-features.h line 10

Quoted 10 lines
include/hw/virtio/virtio-features.h new file +10 -0
@@ -0,0 +1,124 @@
+/*
+ * Virtio features helpers
+ *
+ * Copyright 2025 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _QEMU_VIRTIO_FEATURES_H
+#define _QEMU_VIRTIO_FEATURES_H

An identifier prefixed with an underscore and a capital letter is reserved and better to avoid. Let's also keep consistent with QEMU_VIRTIO_H.

include/hw/virtio/virtio-features.h line 16

Quoted 6 lines
include/hw/virtio/virtio-features.h new file +6 -0
+
+#define VIRTIO_FEATURES_FMT        "%016"PRIx64"%016"PRIx64
+#define VIRTIO_FEATURES_PR(f)      f[1], f[0]
+
+#define VIRTIO_FEATURES_MAX        128
+#define VIRTIO_BIT(b)              (1ULL << (b & 0x3f))

Let's use BIT_ULL().

I also think (b % 64) is easier to understand than (b & 0x3f); you may refer to BIT_MASK() and BIT_WORD() as prior examples.

include/hw/virtio/virtio-features.h line 17

Quoted 1 line
include/hw/virtio/virtio-features.h new file +1 -0
+#define VIRTIO_DWORD(b)            ((b) >> 6)

VIRTIO_FEATURES_PR() and VIRTIO_BIT() should have parentheses around their parameters as VIRTIO_DWORD() does.

include/hw/virtio/virtio-features.h line 18

Quoted 1 line
include/hw/virtio/virtio-features.h new file +1 -0
+#define VIRTIO_FEATURES_WORDS     (VIRTIO_FEATURES_MAX >> 5)

This macro is misaligned; personally I prefer just use one whitespace to delimit the macro name and value to avoid the trouble of aligning them and polluting "git blame".

Quoted 40 lines
include/hw/virtio/virtio-features.h new file +40 -0
+#define VIRTIO_FEATURES_DWORDS      (VIRTIO_FEATURES_WORDS >> 1)
+
+#define VIRTIO_DECLARE_FEATURES(name)                        \
+    union {                                                  \
+        uint64_t name;                                       \
+        uint64_t name##_array[VIRTIO_FEATURES_DWORDS];       \
+    }
+
+static inline void virtio_features_clear(uint64_t *features)
+{
+    memset(features, 0, sizeof(features[0]) * VIRTIO_FEATURES_DWORDS);
+}
+
+static inline void virtio_features_from_u64(uint64_t *features, uint64_t from)
+{
+    virtio_features_clear(features);
+    features[0] = from;
+}
+
+static inline bool virtio_has_feature_ex(const uint64_t *features,
+                                         unsigned int fbit)
+{
+    assert(fbit < VIRTIO_FEATURES_MAX);
+    return features[VIRTIO_DWORD(fbit)] & VIRTIO_BIT(fbit);
+}
+
+static inline void virtio_add_feature_ex(uint64_t *features,
+                                         unsigned int fbit)
+{
+    assert(fbit < VIRTIO_FEATURES_MAX);
+    features[VIRTIO_DWORD(fbit)] |= VIRTIO_BIT(fbit);
+}
+
+static inline void virtio_clear_feature_ex(uint64_t *features,
+                                           unsigned int fbit)
+{
+    assert(fbit < VIRTIO_FEATURES_MAX);
+    features[VIRTIO_DWORD(fbit)] &= ~VIRTIO_BIT(fbit);
+}
+

include/hw/virtio/virtio-features.h line 68

Quoted 10 lines
include/hw/virtio/virtio-features.h new file +10 -0
+static inline bool virtio_features_equal(const uint64_t *f1,
+                                         const uint64_t *f2)
+{
+    uint64_t diff = 0;
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; ++i) {
+        diff |= f1[i] ^ f2[i];
+    }
+    return !!diff;

Let's use memcmp().

Quoted 5 lines
include/hw/virtio/virtio-features.h new file +5 -0
+}
+
+static inline bool virtio_features_use_extended(const uint64_t *features)
+{
+    int i;

include/hw/virtio/virtio-features.h line 83

Quoted 10 lines
include/hw/virtio/virtio-features.h new file +10 -0
+
+    for (i = 1; i < VIRTIO_FEATURES_DWORDS; ++i) {
+        if (features[i]) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static inline bool virtio_features_is_empty(const uint64_t *features)

Let's follow bitmap_empty() and omit "is".

Quoted 66 lines
include/hw/virtio/virtio-features.h new file +41 -0
+{
+    return !virtio_features_use_extended(features) && !features[0];
+}
+
+static inline void virtio_features_copy(uint64_t *to, const uint64_t *from)
+{
+    memcpy(to, from, sizeof(to[0]) * VIRTIO_FEATURES_DWORDS);
+}
+
+static inline void virtio_features_andnot(uint64_t *to, const uint64_t *f1,
+                                           const uint64_t *f2)
+{
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; i++) {
+        to[i] = f1[i] & ~f2[i];
+    }
+}
+
+static inline void virtio_features_and(uint64_t *to, const uint64_t *f1,
+                                       const uint64_t *f2)
+{
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; i++) {
+        to[i] = f1[i] & f2[i];
+    }
+}
+
+static inline void virtio_features_or(uint64_t *to, const uint64_t *f1,
+                                       const uint64_t *f2)
+{
+    int i;
+
+    for (i = 0; i < VIRTIO_FEATURES_DWORDS; i++) {
+        to[i] = f1[i] | f2[i];
+    }
+}
+
+#endif
+
include/hw/virtio/virtio.h +4 -3
index 214d4a77e9..0d1eb20489 100644
@@ -16,6 +16,7 @@
 
  #include "system/memory.h"
  #include "hw/qdev-core.h"
+#include "hw/virtio/virtio-features.h"
  #include "net/net.h"
  #include "migration/vmstate.h"
  #include "qemu/event_notifier.h"
@@ -121,9 +122,9 @@ struct VirtIODevice
       * backend (e.g. vhost) and could potentially be a subset of the
       * total feature set offered by QEMU.
       */
-    uint64_t host_features;
-    uint64_t guest_features;
-    uint64_t backend_features;
+    VIRTIO_DECLARE_FEATURES(host_features);
+    VIRTIO_DECLARE_FEATURES(guest_features);
+    VIRTIO_DECLARE_FEATURES(backend_features);
 
      size_t config_len;
      void *config;
author maintainer
#8 lore raw

RFC PATCH v2 4/13 virtio: serialize extended features state

In reply to Paolo Abeni

If the driver uses any of the extended features (i.e. above 64), serialize the full features range (128 bits).

This is one of the few spots that need explicitly to know and set in stone the extended features array size; add a build bug to prevent breaking the migration should such size change again in the future:
more serialization plumbing will be needed.

---
v1 -> v2:
- uint128_t -> u64[2]

---
 hw/virtio/virtio.c | 97 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 86 insertions(+), 11 deletions(-)
hw/virtio/virtio.c +86 -11
index 82a285a31d..6a313313dd 100644
@@ -2954,6 +2954,24 @@ static const VMStateDescription vmstate_virtio_disabled = {
     }
 };
 
+static bool virtio_128bit_features_needed(void *opaque)
+{
+    VirtIODevice *vdev = opaque;
+
+    return virtio_features_use_extended(vdev->host_features_array);
+}
+
+static const VMStateDescription vmstate_virtio_128bit_features = {
+    .name = "virtio/128bit_features",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = &virtio_128bit_features_needed,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT64_ARRAY(guest_features_array, VirtIODevice, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_virtio = {
     .name = "virtio",
     .version_id = 1,
@@ -2963,6 +2981,7 @@ static const VMStateDescription vmstate_virtio = {
     },
     .subsections = (const VMStateDescription * const []) {
         &vmstate_virtio_device_endian,
+        &vmstate_virtio_128bit_features,
         &vmstate_virtio_64bit_features,
         &vmstate_virtio_virtqueues,
         &vmstate_virtio_ringsize,
@@ -3059,23 +3078,30 @@ const VMStateInfo  virtio_vmstate_info = {
     .put = virtio_device_put,
 };
 
-static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val)
+static int virtio_set_features_nocheck(VirtIODevice *vdev, const uint64_t *val)
 {
     VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
-    bool bad = (val & ~(vdev->host_features)) != 0;
+    uint64_t tmp[VIRTIO_FEATURES_DWORDS];
+    bool bad;
+
+    virtio_features_andnot(tmp, val, vdev->host_features_array);
+    bad = !virtio_features_is_empty(tmp);
+
+    virtio_features_and(tmp, val, vdev->host_features_array);
 
-    val &= vdev->host_features;
     if (k->set_features) {
-        k->set_features(vdev, val);
+        bad = bad || virtio_features_use_extended(tmp);
+        k->set_features(vdev, tmp[0]);
     }
-    vdev->guest_features = val;
+
+    virtio_features_copy(vdev->guest_features_array, tmp);
     return bad ? -1 : 0;
 }
 
 typedef struct VirtioSetFeaturesNocheckData {
     Coroutine *co;
     VirtIODevice *vdev;
-    uint64_t val;
+    uint64_t val[VIRTIO_FEATURES_DWORDS];
     int ret;
 } VirtioSetFeaturesNocheckData;
 
@@ -3094,12 +3120,41 @@ virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val)
         VirtioSetFeaturesNocheckData data = {
             .co = qemu_coroutine_self(),
             .vdev = vdev,
-            .val = val,
         };
+        virtio_features_from_u64(data.val, val);
         aio_bh_schedule_oneshot(qemu_get_current_aio_context(),
                                 virtio_set_features_nocheck_bh, &data);
         qemu_coroutine_yield();
         return data.ret;
+    } else {
+        uint64_t features[VIRTIO_FEATURES_DWORDS];
+        virtio_features_from_u64(features, val);
+        return virtio_set_features_nocheck(vdev, features);
+    }
+}
+
+static void virtio_set_128bit_features_nocheck_bh(void *opaque)
+{
+    VirtioSetFeaturesNocheckData *data = opaque;
+
+    data->ret = virtio_set_features_nocheck(data->vdev, data->val);
+    aio_co_wake(data->co);
+}
+
+static int coroutine_mixed_fn
+virtio_set_128bit_features_nocheck_maybe_co(VirtIODevice *vdev,
+                                            const uint64_t *val)
+{
+    if (qemu_in_coroutine()) {
+        VirtioSetFeaturesNocheckData data = {
+            .co = qemu_coroutine_self(),
+            .vdev = vdev,
+        };
+        virtio_features_copy(data.val, val);
+        aio_bh_schedule_oneshot(qemu_get_current_aio_context(),
+                                virtio_set_128bit_features_nocheck_bh, &data);
+        qemu_coroutine_yield();
+        return data.ret;
     } else {
         return virtio_set_features_nocheck(vdev, val);
     }
@@ -3107,6 +3162,7 @@ virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val)
 
 int virtio_set_features(VirtIODevice *vdev, uint64_t val)
 {
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
     int ret;
     /*
      * The driver must not attempt to set features after feature negotiation
@@ -3122,7 +3178,8 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
                       __func__, vdev->name);
     }
 
-    ret = virtio_set_features_nocheck(vdev, val);
+    virtio_features_from_u64(features, val);
+    ret = virtio_set_features_nocheck(vdev, features);
     if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
         /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches.  */
         int i;
@@ -3145,6 +3202,7 @@ void virtio_reset(void *opaque)
 {
     VirtIODevice *vdev = opaque;
     VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
     int i;
 
     virtio_set_status(vdev, 0);
@@ -3171,7 +3229,8 @@ void virtio_reset(void *opaque)
     vdev->start_on_kick = false;
     vdev->started = false;
     vdev->broken = false;
-    virtio_set_features_nocheck(vdev, 0);
+    virtio_features_clear(features);
+    virtio_set_features_nocheck(vdev, features);
     vdev->queue_sel = 0;
     vdev->status = 0;
     vdev->disabled = false;
@@ -3254,7 +3313,7 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
      * Note: devices should always test host features in future - don't create
      * new dependencies like this.
      */
-    vdev->guest_features = features;
+    virtio_features_from_u64(vdev->guest_features_array, features);
 
     config_len = qemu_get_be32(f);
 
@@ -3333,7 +3392,23 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
         vdev->device_endian = virtio_default_endian();
     }
 
-    if (virtio_64bit_features_needed(vdev)) {
+    /*
+     * Serialization needs constant size features array. Avoid
+     * silently breaking migration should the feature space increase
+     * even more in the (far away) future
+     */
+    QEMU_BUILD_BUG_ON(VIRTIO_FEATURES_DWORDS != 2);
+    if (virtio_128bit_features_needed(vdev)) {
+        uint64_t *val = vdev->guest_features_array;
+
+        if (virtio_set_128bit_features_nocheck_maybe_co(vdev, val) < 0) {
+            error_report("Features 0x" VIRTIO_FEATURES_FMT " unsupported. "
+                         "Allowed features: 0x" VIRTIO_FEATURES_FMT,
+                         VIRTIO_FEATURES_PR(val),
+                         VIRTIO_FEATURES_PR(vdev->host_features_array));
+            return -1;
+        }
+    } else if (virtio_64bit_features_needed(vdev)) {
         /*
          * Subsection load filled vdev->guest_features.  Run them
          * through virtio_set_features to sanity-check them against
Signature · 1 line
2.50.0
#9 lore raw

Re: [PATCH RFC v2 04/13] virtio: serialize extended features state

Quoted 28 lines · Paolo Abeni

If the driver uses any of the extended features (i.e. above 64), serialize the full features range (128 bits).

This is one of the few spots that need explicitly to know and set in stone the extended features array size; add a build bug to prevent breaking the migration should such size change again in the future:
more serialization plumbing will be needed.

---
v1 -> v2:
- uint128_t -> u64[2]

---
  hw/virtio/virtio.c | 97 ++++++++++++++++++++++++++++++++++++++++------
  1 file changed, 86 insertions(+), 11 deletions(-)
hw/virtio/virtio.c +4 -0
index 82a285a31d..6a313313dd 100644
@@ -2954,6 +2954,24 @@ static const VMStateDescription vmstate_virtio_disabled = {
      }
  };
 
+static bool virtio_128bit_features_needed(void *opaque)
+{
+    VirtIODevice *vdev = opaque;
+

hw/virtio/virtio.c static const VMStateDescription vmstate_virtio_disabled = { · line 2970

Quoted 10 lines
hw/virtio/virtio.c +10 -0
+    return virtio_features_use_extended(vdev->host_features_array);
+}
+
+static const VMStateDescription vmstate_virtio_128bit_features = {
+    .name = "virtio/128bit_features",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = &virtio_128bit_features_needed,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT64_ARRAY(guest_features_array, VirtIODevice, 2),

We only need to save the second element so it can be reduced to:
VMSTATE_UINT64(guest_features_array[1], VirtIODevice)

Quoted 19 lines
hw/virtio/virtio.c +5 -0
+        VMSTATE_END_OF_LIST()
+    }
+};
+
  static const VMStateDescription vmstate_virtio = {
      .name = "virtio",
      .version_id = 1,
@@ -2963,6 +2981,7 @@ static const VMStateDescription vmstate_virtio = {
      },
      .subsections = (const VMStateDescription * const []) {
          &vmstate_virtio_device_endian,
+        &vmstate_virtio_128bit_features,
          &vmstate_virtio_64bit_features,
          &vmstate_virtio_virtqueues,
          &vmstate_virtio_ringsize,
@@ -3059,23 +3078,30 @@ const VMStateInfo  virtio_vmstate_info = {
      .put = virtio_device_put,
  };
 

hw/virtio/virtio.c const VMStateInfo virtio_vmstate_info = { · line 3088

Quoted 10 lines
hw/virtio/virtio.c +6 -2
-static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val)
+static int virtio_set_features_nocheck(VirtIODevice *vdev, const uint64_t *val)
  {
      VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
-    bool bad = (val & ~(vdev->host_features)) != 0;
+    uint64_t tmp[VIRTIO_FEATURES_DWORDS];
+    bool bad;
+
+    virtio_features_andnot(tmp, val, vdev->host_features_array);
+    bad = !virtio_features_is_empty(tmp);

bitmap_andnot() returns a value representing if some bit in the resulting bitmap is set. We can remove the virtio_features_is_empty() call if virtio_features_andnot() does the same.

Quoted 32 lines
hw/virtio/virtio.c +8 -5
+
+    virtio_features_and(tmp, val, vdev->host_features_array);
 
-    val &= vdev->host_features;
      if (k->set_features) {
-        k->set_features(vdev, val);
+        bad = bad || virtio_features_use_extended(tmp);
+        k->set_features(vdev, tmp[0]);
      }
-    vdev->guest_features = val;
+
+    virtio_features_copy(vdev->guest_features_array, tmp);
      return bad ? -1 : 0;
  }
 
  typedef struct VirtioSetFeaturesNocheckData {
      Coroutine *co;
      VirtIODevice *vdev;
-    uint64_t val;
+    uint64_t val[VIRTIO_FEATURES_DWORDS];
      int ret;
  } VirtioSetFeaturesNocheckData;
 
@@ -3094,12 +3120,41 @@ virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val)
          VirtioSetFeaturesNocheckData data = {
              .co = qemu_coroutine_self(),
              .vdev = vdev,
-            .val = val,
          };
+        virtio_features_from_u64(data.val, val);
          aio_bh_schedule_oneshot(qemu_get_current_aio_context(),
                                  virtio_set_features_nocheck_bh, &data);

hw/virtio/virtio.c virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val) · line 3136

Quoted 10 lines
hw/virtio/virtio.c +8 -0
          qemu_coroutine_yield();
          return data.ret;
+    } else {
+        uint64_t features[VIRTIO_FEATURES_DWORDS];
+        virtio_features_from_u64(features, val);
+        return virtio_set_features_nocheck(vdev, features);
+    }
+}
+
+static void virtio_set_128bit_features_nocheck_bh(void *opaque)

"128bit" should be omitted for consistency with virtio_set_features_nocheck() and for extensibility.

Quoted 70 lines
hw/virtio/virtio.c +28 -3
+{
+    VirtioSetFeaturesNocheckData *data = opaque;
+
+    data->ret = virtio_set_features_nocheck(data->vdev, data->val);
+    aio_co_wake(data->co);
+}
+
+static int coroutine_mixed_fn
+virtio_set_128bit_features_nocheck_maybe_co(VirtIODevice *vdev,
+                                            const uint64_t *val)
+{
+    if (qemu_in_coroutine()) {
+        VirtioSetFeaturesNocheckData data = {
+            .co = qemu_coroutine_self(),
+            .vdev = vdev,
+        };
+        virtio_features_copy(data.val, val);
+        aio_bh_schedule_oneshot(qemu_get_current_aio_context(),
+                                virtio_set_128bit_features_nocheck_bh, &data);
+        qemu_coroutine_yield();
+        return data.ret;
      } else {
          return virtio_set_features_nocheck(vdev, val);
      }
@@ -3107,6 +3162,7 @@ virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val)
 
  int virtio_set_features(VirtIODevice *vdev, uint64_t val)
  {
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
      int ret;
      /*
       * The driver must not attempt to set features after feature negotiation
@@ -3122,7 +3178,8 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
                        __func__, vdev->name);
      }
 
-    ret = virtio_set_features_nocheck(vdev, val);
+    virtio_features_from_u64(features, val);
+    ret = virtio_set_features_nocheck(vdev, features);
      if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
          /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches.  */
          int i;
@@ -3145,6 +3202,7 @@ void virtio_reset(void *opaque)
  {
      VirtIODevice *vdev = opaque;
      VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
      int i;
 
      virtio_set_status(vdev, 0);
@@ -3171,7 +3229,8 @@ void virtio_reset(void *opaque)
      vdev->start_on_kick = false;
      vdev->started = false;
      vdev->broken = false;
-    virtio_set_features_nocheck(vdev, 0);
+    virtio_features_clear(features);
+    virtio_set_features_nocheck(vdev, features);
      vdev->queue_sel = 0;
      vdev->status = 0;
      vdev->disabled = false;
@@ -3254,7 +3313,7 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
       * Note: devices should always test host features in future - don't create
       * new dependencies like this.
       */
-    vdev->guest_features = features;
+    virtio_features_from_u64(vdev->guest_features_array, features);
 
      config_len = qemu_get_be32(f);
 

hw/virtio/virtio.c virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) · line 3398

Quoted 8 lines
hw/virtio/virtio.c +4 -1
@@ -3333,7 +3392,23 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
          vdev->device_endian = virtio_default_endian();
      }
 
-    if (virtio_64bit_features_needed(vdev)) {
+    /*
+     * Serialization needs constant size features array. Avoid
+     * silently breaking migration should the feature space increase
+     * even more in the (far away) future

Serialization is not done here and irrlevant.

hw/virtio/virtio.c virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) · line 3401

Quoted 3 lines
hw/virtio/virtio.c +3 -0
+     */
+    QEMU_BUILD_BUG_ON(VIRTIO_FEATURES_DWORDS != 2);
+    if (virtio_128bit_features_needed(vdev)) {

There is no need to distinguish virtio_128bit_features_needed() and virtio_64bit_features_needed() here.

For the 32-bit case, it will be simpler to have an array here and use virtio_set_128bit_features_nocheck_maybe_co() instead of having virtio_set_features_nocheck_maybe_co().

Quoted 13 lines
hw/virtio/virtio.c +10 -0
+        uint64_t *val = vdev->guest_features_array;
+
+        if (virtio_set_128bit_features_nocheck_maybe_co(vdev, val) < 0) {
+            error_report("Features 0x" VIRTIO_FEATURES_FMT " unsupported. "
+                         "Allowed features: 0x" VIRTIO_FEATURES_FMT,
+                         VIRTIO_FEATURES_PR(val),
+                         VIRTIO_FEATURES_PR(vdev->host_features_array));
+            return -1;
+        }
+    } else if (virtio_64bit_features_needed(vdev)) {
          /*
           * Subsection load filled vdev->guest_features.  Run them
           * through virtio_set_features to sanity-check them against
author maintainer
#10 lore raw

Re: [PATCH RFC v2 04/13] virtio: serialize extended features state

Quoted 7 lines · Akihiko Odaki

hw/virtio/virtio.c virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) · line 3401

Quoted 3 lines · Paolo Abeni
hw/virtio/virtio.c +3 -0
+     */
+    QEMU_BUILD_BUG_ON(VIRTIO_FEATURES_DWORDS != 2);
+    if (virtio_128bit_features_needed(vdev)) {

There is no need to distinguish virtio_128bit_features_needed() and virtio_64bit_features_needed() here.

Double checking I'm reading the above correctly. Are you suggesting to replace this chunk with something alike:

    if (virtio_64bit_features_needed(vdev)) {
        /* The 64 highest bit has been cleared by the previous
         *  virtio_features_from_u64() and ev.
         * initialized as needed when loading
         * "virtio/128bit_features"*/
        uint64_t *val = vdev->guest_features_array;

        if (virtio_set_128bit_features_nocheck_maybe_co(vdev, val) < 0)

// ...

Quoted 3 lines

For the 32-bit case, it will be simpler to have an array here and use virtio_set_128bit_features_nocheck_maybe_co() instead of having virtio_set_features_nocheck_maybe_co().

Again double checking I'm parsing the above correctly. You are suggesting to dismiss the virtio_set_features_nocheck_maybe_co() helper entirely and use virtio_set_128bit_features_nocheck_maybe_co() even when only 32bit features are loaded. Am I correct?

Thanks,

Paolo

#11 lore raw

Re: [PATCH RFC v2 04/13] virtio: serialize extended features state

Quoted 6 lines · Paolo Abeni
Quoted 5 lines · Akihiko Odaki
Quoted 3 lines · Paolo Abeni
hw/virtio/virtio.c +3 -0
+     */
+    QEMU_BUILD_BUG_ON(VIRTIO_FEATURES_DWORDS != 2);
+    if (virtio_128bit_features_needed(vdev)) {
Quoted 7 lines
Quoted 2 lines

There is no need to distinguish virtio_128bit_features_needed() and virtio_64bit_features_needed() here.

Double checking I'm reading the above correctly. Are you suggesting to replace this chunk with something alike:

     if (virtio_64bit_features_needed(vdev)) {

This condition is not right as virtio_64bit_features_needed() doesn't return true when the some of bits [64, 128) is set while bits [32, 64) are cleared. I see two options to fix:

- Check: virtio_64bit_features_needed(vdev) ||
virtio_128bit_features_needed(vdev)

- Ensure that virtio_64bit_features_needed(vdev) returns true when a bit more significant than bit 31 is set.

Quoted 8 lines

/* The 64 highest bit has been cleared by the previous
* virtio_features_from_u64() and ev.
* initialized as needed when loading
* "virtio/128bit_features"*/
uint64_t *val = vdev->guest_features_array;

if (virtio_set_128bit_features_nocheck_maybe_co(vdev, val) < 0)
// ...> >> For the 32-bit case, it will be simpler to have an array here and use

Quoted 7 lines
Quoted 2 lines

virtio_set_128bit_features_nocheck_maybe_co() instead of having virtio_set_features_nocheck_maybe_co().

Again double checking I'm parsing the above correctly. You are suggesting to dismiss the virtio_set_features_nocheck_maybe_co() helper entirely and use virtio_set_128bit_features_nocheck_maybe_co() even when only 32bit features are loaded. Am I correct?

Yes, but now I found it is unnecessary to special-case even the 32-bit case.

Commit 019a3edbb25f ("virtio: make features 64bit wide") had to add a conditional to distinguish the 64-bit and 32-bit cases because vdev->guest_features was not set before executing this part of code.

However, commit 62cee1a28aad ("virtio: set low features early on load") later added preceding code to set vdev->guest_features. In summary, this part of code can be simply replaced with:

if (virtio_set_128bit_features_nocheck_maybe_co(vdev,

vdev->guest_features_array) < 0) {
        error_report("Features 0x" VIRTIO_FEATURES_FMT " unsupported. "
                        "Allowed features: 0x" VIRTIO_FEATURES_FMT,
                        VIRTIO_FEATURES_PR(val),
                        VIRTIO_FEATURES_PR(vdev->host_features_array));
        return -1;
    }

There is no need of virtio_64bit_features_needed(vdev) or virtio_128bit_features_needed(vdev) at all.

I have another finding by the way; there are three phrases that refers to the new extension: array (e.g., guest_features_array), _ex (e.g., virtio_add_feature_ex), 128bit (e.g., virtio_128bit_features_needed).

It makes sense to make "128bit" an exception in the migration code because the migration format is fixed and will require e.g., "192bit" for a future extension. But two suffixes, _ex and _array, can be unified.

Regards,
Akihiko Odaki

author maintainer
#12 lore raw

RFC PATCH v2 5/13 virtio: add support for negotiating extended features

In reply to Paolo Abeni

The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the virtio net driver.

Add support to allow extended features negotiation on a per devices basis. Devices willing to negotiated extended features need to implemented a new pair of features getter/setter, the core will conditionally use them instead of the basic one.

Note that 'bad_features' don't need to be extended, as they are bound to the 64 bits limit.

---
v1 -> v2:
- uint128_t -> uint64_t[]

---
 hw/virtio/virtio-bus.c     | 11 ++++++++---
 hw/virtio/virtio.c         | 18 +++++++++++++-----
 include/hw/virtio/virtio.h |  4 ++++
 3 files changed, 25 insertions(+), 8 deletions(-)
hw/virtio/virtio-bus.c +8 -3
index 11adfbf3ab..4a80f0b4d0 100644
@@ -62,9 +62,14 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
     }
 
     /* Get the features of the plugged device. */
-    assert(vdc->get_features != NULL);
-    vdev->host_features = vdc->get_features(vdev, vdev->host_features,
-                                            &local_err);
+    if (vdc->get_features_ex) {
+        vdc->get_features_ex(vdev, vdev->host_features_array, &local_err);
+    } else {
+        assert(vdc->get_features != NULL);
+        virtio_features_from_u64(vdev->host_features_array,
+                                 vdc->get_features(vdev, vdev->host_features,
+                                                   &local_err));
+    }
     if (local_err) {
         error_propagate(errp, local_err);
         return;
hw/virtio/virtio.c +13 -5
index 6a313313dd..dd876d058e 100644
@@ -3089,7 +3089,9 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, const uint64_t *val)
 
     virtio_features_and(tmp, val, vdev->host_features_array);
 
-    if (k->set_features) {
+    if (k->set_features_ex) {
+        k->set_features_ex(vdev, val);
+    } else if (k->set_features) {
         bad = bad || virtio_features_use_extended(tmp);
         k->set_features(vdev, tmp[0]);
     }
@@ -3160,9 +3162,8 @@ virtio_set_128bit_features_nocheck_maybe_co(VirtIODevice *vdev,
     }
 }
 
-int virtio_set_features(VirtIODevice *vdev, uint64_t val)
+int virtio_set_features_ex(VirtIODevice *vdev, const uint64_t *features)
 {
-    uint64_t features[VIRTIO_FEATURES_DWORDS];
     int ret;
     /*
      * The driver must not attempt to set features after feature negotiation
@@ -3172,13 +3173,12 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
         return -EINVAL;
     }
 
-    if (val & (1ull << VIRTIO_F_BAD_FEATURE)) {
+    if (features[0] & (1ull << VIRTIO_F_BAD_FEATURE)) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "%s: guest driver for %s has enabled UNUSED(30) feature bit!\n",
                       __func__, vdev->name);
     }
 
-    virtio_features_from_u64(features, val);
     ret = virtio_set_features_nocheck(vdev, features);
     if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
         /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches.  */
@@ -3198,6 +3198,14 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
     return ret;
 }
 
+int virtio_set_features(VirtIODevice *vdev, uint64_t val)
+{
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
+
+    virtio_features_from_u64(features, val);
+    return virtio_set_features_ex(vdev, features);
+}
+
 void virtio_reset(void *opaque)
 {
     VirtIODevice *vdev = opaque;
include/hw/virtio/virtio.h +4 -0
index 0d1eb20489..6a22c28d82 100644
@@ -178,6 +178,9 @@ struct VirtioDeviceClass {
     /* This is what a VirtioDevice must implement */
     DeviceRealize realize;
     DeviceUnrealize unrealize;
+    void (*get_features_ex)(VirtIODevice *vdev, uint64_t *requested_features,
+                            Error **errp);
+    void (*set_features_ex)(VirtIODevice *vdev, const uint64_t *val);
     uint64_t (*get_features)(VirtIODevice *vdev,
                              uint64_t requested_features,
                              Error **errp);
@@ -367,6 +370,7 @@ void virtio_queue_reset(VirtIODevice *vdev, uint32_t queue_index);
 void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index);
 void virtio_update_irq(VirtIODevice *vdev);
 int virtio_set_features(VirtIODevice *vdev, uint64_t val);
+int virtio_set_features_ex(VirtIODevice *vdev, const uint64_t *val);
 
 /* Base devices.  */
 typedef struct VirtIOBlkConf VirtIOBlkConf;
Signature · 1 line
2.50.0
author maintainer
#13 lore raw

RFC PATCH v2 6/13 virtio-pci: implement support for extended features

In reply to Paolo Abeni

Extend the features configuration space to 128 bits, and allow the common read/write operation to access all of it.

On migration, save the 128 bit version of the features only if the upper bits are non zero; after load zero the upper bits if the extended features were not loaded.

Note that we must clear the proxy-ed features on device reset, otherwise a guest kernel not supporting extended features booted after an extended features enabled one could end-up wrongly inheriting extended features.

---
v1 -> v2:
- use separate VMStateDescription and pre/post load to avoid breaking migration
- clear proxy features on device reset

---
 hw/virtio/virtio-pci.c         | 101 +++++++++++++++++++++++++++++----
 include/hw/virtio/virtio-pci.h |   6 +-
 2 files changed, 96 insertions(+), 11 deletions(-)
hw/virtio/virtio-pci.c +91 -10
index fba2372c93..dc5e7eaf81 100644
@@ -108,6 +108,39 @@ static const VMStateDescription vmstate_virtio_pci_modern_queue_state = {
     }
 };
 
+static bool virtio_pci_modern_state_features128_needed(void *opaque)
+{
+    VirtIOPCIProxy *proxy = opaque;
+    uint32_t features = 0;
+    int i;
+
+    for (i = 2; i < ARRAY_SIZE(proxy->guest_features128); ++i) {
+        features |= proxy->guest_features128[i];
+    }
+    return !!features;
+}
+
+static int virtio_pci_modern_state_features128_post_load(void *opaque,
+                                                         int version_id)
+{
+    VirtIOPCIProxy *proxy = opaque;
+
+    proxy->extended_features_loaded = true;
+    return 0;
+}
+
+static const VMStateDescription vmstate_virtio_pci_modern_state_features128 = {
+    .name = "virtio_pci/modern_state/features128",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = &virtio_pci_modern_state_features128_post_load,
+    .needed = &virtio_pci_modern_state_features128_needed,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(guest_features128, VirtIOPCIProxy, 4),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static bool virtio_pci_modern_state_needed(void *opaque)
 {
     VirtIOPCIProxy *proxy = opaque;
@@ -115,10 +148,40 @@ static bool virtio_pci_modern_state_needed(void *opaque)
     return virtio_pci_modern(proxy);
 }
 
+static int virtio_pci_modern_state_pre_load(void *opaque)
+{
+    VirtIOPCIProxy *proxy = opaque;
+
+    proxy->extended_features_loaded = false;
+    return 0;
+}
+
+static int virtio_pci_modern_state_post_load(void *opaque, int version_id)
+{
+    VirtIOPCIProxy *proxy = opaque;
+    int i;
+
+    if (proxy->extended_features_loaded) {
+        return 0;
+    }
+
+    QEMU_BUILD_BUG_ON(offsetof(VirtIOPCIProxy, guest_features[0]) !=
+                      offsetof(VirtIOPCIProxy, guest_features128[0]));
+    QEMU_BUILD_BUG_ON(offsetof(VirtIOPCIProxy, guest_features[1]) !=
+                      offsetof(VirtIOPCIProxy, guest_features128[1]));
+
+    for (i = 2; i < ARRAY_SIZE(proxy->guest_features128); ++i) {
+        proxy->guest_features128[i] = 0;
+    }
+    return 0;
+}
+
 static const VMStateDescription vmstate_virtio_pci_modern_state_sub = {
     .name = "virtio_pci/modern_state",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_load = &virtio_pci_modern_state_pre_load,
+    .post_load = &virtio_pci_modern_state_post_load,
     .needed = &virtio_pci_modern_state_needed,
     .fields = (const VMStateField[]) {
         VMSTATE_UINT32(dfselect, VirtIOPCIProxy),
@@ -128,6 +191,10 @@ static const VMStateDescription vmstate_virtio_pci_modern_state_sub = {
                              vmstate_virtio_pci_modern_queue_state,
                              VirtIOPCIQueue),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * const []) {
+        &vmstate_virtio_pci_modern_state_features128,
+        NULL
     }
 };
 
@@ -1493,19 +1560,22 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr,
         val = proxy->dfselect;
         break;
     case VIRTIO_PCI_COMMON_DF:
-        if (proxy->dfselect <= 1) {
+        if (proxy->dfselect < VIRTIO_FEATURES_WORDS) {
             VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
 
-            val = (vdev->host_features & ~vdc->legacy_features) >>
-                (32 * proxy->dfselect);
+            val = vdev->host_features_array[proxy->dfselect >> 1] >>
+                  (32 * (proxy->dfselect & 1));
+            if (proxy->dfselect <= 1) {
+                val &= (~vdc->legacy_features) >> (32 * proxy->dfselect);
+            }
         }
         break;
     case VIRTIO_PCI_COMMON_GFSELECT:
         val = proxy->gfselect;
         break;
     case VIRTIO_PCI_COMMON_GF:
-        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) {
-            val = proxy->guest_features[proxy->gfselect];
+        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features128)) {
+            val = proxy->guest_features128[proxy->gfselect];
         }
         break;
     case VIRTIO_PCI_COMMON_MSIX:
@@ -1587,11 +1657,18 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
         proxy->gfselect = val;
         break;
     case VIRTIO_PCI_COMMON_GF:
-        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) {
-            proxy->guest_features[proxy->gfselect] = val;
-            virtio_set_features(vdev,
-                                (((uint64_t)proxy->guest_features[1]) << 32) |
-                                proxy->guest_features[0]);
+        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features128)) {
+            uint64_t features[VIRTIO_FEATURES_DWORDS];
+            int i;
+
+            proxy->guest_features128[proxy->gfselect] = val;
+            virtio_features_clear(features);
+            for (i = 0; i < ARRAY_SIZE(proxy->guest_features128); ++i) {
+                uint64_t cur = proxy->guest_features128[i];
+
+                features[i >> 1] |= cur << ((i & 1) * 32);
+            }
+            virtio_set_features_ex(vdev, features);
         }
         break;
     case VIRTIO_PCI_COMMON_MSIX:
@@ -2310,6 +2387,10 @@ static void virtio_pci_reset(DeviceState *qdev)
     virtio_bus_reset(bus);
     msix_unuse_all_vectors(&proxy->pci_dev);
 
+    /* be sure to not carry over any feature across reset */
+    memset(proxy->guest_features128, 0, sizeof(uint32_t) *
+           ARRAY_SIZE(proxy->guest_features128));
+
     for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
         proxy->vqs[i].enabled = 0;
         proxy->vqs[i].reset = 0;
include/hw/virtio/virtio-pci.h +5 -1
index eab5394898..1868e3b106 100644
@@ -151,6 +151,7 @@ struct VirtIOPCIProxy {
     uint32_t flags;
     bool disable_modern;
     bool ignore_backend_features;
+    bool extended_features_loaded;
     OnOffAuto disable_legacy;
     /* Transitional device id */
     uint16_t trans_devid;
@@ -158,7 +159,10 @@ struct VirtIOPCIProxy {
     uint32_t nvectors;
     uint32_t dfselect;
     uint32_t gfselect;
-    uint32_t guest_features[2];
+    union {
+        uint32_t guest_features[2];
+        uint32_t guest_features128[4];
+    };
     VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX];
 
     VirtIOIRQFD *vector_irqfd;
Signature · 1 line
2.50.0
#14 lore raw

Re: [PATCH RFC v2 06/13] virtio-pci: implement support for extended features

Quoted 30 lines · Paolo Abeni

Extend the features configuration space to 128 bits, and allow the common read/write operation to access all of it.

On migration, save the 128 bit version of the features only if the upper bits are non zero; after load zero the upper bits if the extended features were not loaded.

Note that we must clear the proxy-ed features on device reset, otherwise a guest kernel not supporting extended features booted after an extended features enabled one could end-up wrongly inheriting extended features.

---
v1 -> v2:
- use separate VMStateDescription and pre/post load to avoid breaking migration
- clear proxy features on device reset

---
  hw/virtio/virtio-pci.c         | 101 +++++++++++++++++++++++++++++----
  include/hw/virtio/virtio-pci.h |   6 +-
  2 files changed, 96 insertions(+), 11 deletions(-)
hw/virtio/virtio-pci.c +0 -0
index fba2372c93..dc5e7eaf81 100644
@@ -108,6 +108,39 @@ static const VMStateDescription vmstate_virtio_pci_modern_queue_state = {
      }
  };
 

hw/virtio/virtio-pci.c static const VMStateDescription vmstate_virtio_pci_modern_queue_state = { · line 120

Quoted 10 lines
hw/virtio/virtio-pci.c +10 -0
+static bool virtio_pci_modern_state_features128_needed(void *opaque)
+{
+    VirtIOPCIProxy *proxy = opaque;
+    uint32_t features = 0;
+    int i;
+
+    for (i = 2; i < ARRAY_SIZE(proxy->guest_features128); ++i) {
+        features |= proxy->guest_features128[i];
+    }
+    return !!features;

"!!" is unnecessary; the implicit cast will clamp the value into true/false.

Quoted 48 lines
hw/virtio/virtio-pci.c +41 -0
+}
+
+static int virtio_pci_modern_state_features128_post_load(void *opaque,
+                                                         int version_id)
+{
+    VirtIOPCIProxy *proxy = opaque;
+
+    proxy->extended_features_loaded = true;
+    return 0;
+}
+
+static const VMStateDescription vmstate_virtio_pci_modern_state_features128 = {
+    .name = "virtio_pci/modern_state/features128",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = &virtio_pci_modern_state_features128_post_load,
+    .needed = &virtio_pci_modern_state_features128_needed,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(guest_features128, VirtIOPCIProxy, 4),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
  static bool virtio_pci_modern_state_needed(void *opaque)
  {
      VirtIOPCIProxy *proxy = opaque;
@@ -115,10 +148,40 @@ static bool virtio_pci_modern_state_needed(void *opaque)
      return virtio_pci_modern(proxy);
  }
 
+static int virtio_pci_modern_state_pre_load(void *opaque)
+{
+    VirtIOPCIProxy *proxy = opaque;
+
+    proxy->extended_features_loaded = false;
+    return 0;
+}
+
+static int virtio_pci_modern_state_post_load(void *opaque, int version_id)
+{
+    VirtIOPCIProxy *proxy = opaque;
+    int i;
+
+    if (proxy->extended_features_loaded) {
+        return 0;
+    }
+
+    QEMU_BUILD_BUG_ON(offsetof(VirtIOPCIProxy, guest_features[0]) !=

hw/virtio/virtio-pci.c static bool virtio_pci_modern_state_needed(void *opaque) · line 178

Quoted 10 lines
hw/virtio/virtio-pci.c +10 -0
+                      offsetof(VirtIOPCIProxy, guest_features128[0]));
+    QEMU_BUILD_BUG_ON(offsetof(VirtIOPCIProxy, guest_features[1]) !=
+                      offsetof(VirtIOPCIProxy, guest_features128[1]));
+
+    for (i = 2; i < ARRAY_SIZE(proxy->guest_features128); ++i) {
+        proxy->guest_features128[i] = 0;
+    }
+    return 0;
+}
+

You can expect the device is in the reset state when migrating so expect guest_features128 is initialized as zero; there are already plenty of code expecting the reset state.

Quoted 73 lines
hw/virtio/virtio-pci.c +26 -10
  static const VMStateDescription vmstate_virtio_pci_modern_state_sub = {
      .name = "virtio_pci/modern_state",
      .version_id = 1,
      .minimum_version_id = 1,
+    .pre_load = &virtio_pci_modern_state_pre_load,
+    .post_load = &virtio_pci_modern_state_post_load,
      .needed = &virtio_pci_modern_state_needed,
      .fields = (const VMStateField[]) {
          VMSTATE_UINT32(dfselect, VirtIOPCIProxy),
@@ -128,6 +191,10 @@ static const VMStateDescription vmstate_virtio_pci_modern_state_sub = {
                               vmstate_virtio_pci_modern_queue_state,
                               VirtIOPCIQueue),
          VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * const []) {
+        &vmstate_virtio_pci_modern_state_features128,
+        NULL
      }
  };
 
@@ -1493,19 +1560,22 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr,
          val = proxy->dfselect;
          break;
      case VIRTIO_PCI_COMMON_DF:
-        if (proxy->dfselect <= 1) {
+        if (proxy->dfselect < VIRTIO_FEATURES_WORDS) {
              VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
 
-            val = (vdev->host_features & ~vdc->legacy_features) >>
-                (32 * proxy->dfselect);
+            val = vdev->host_features_array[proxy->dfselect >> 1] >>
+                  (32 * (proxy->dfselect & 1));
+            if (proxy->dfselect <= 1) {
+                val &= (~vdc->legacy_features) >> (32 * proxy->dfselect);
+            }
          }
          break;
      case VIRTIO_PCI_COMMON_GFSELECT:
          val = proxy->gfselect;
          break;
      case VIRTIO_PCI_COMMON_GF:
-        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) {
-            val = proxy->guest_features[proxy->gfselect];
+        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features128)) {
+            val = proxy->guest_features128[proxy->gfselect];
          }
          break;
      case VIRTIO_PCI_COMMON_MSIX:
@@ -1587,11 +1657,18 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
          proxy->gfselect = val;
          break;
      case VIRTIO_PCI_COMMON_GF:
-        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) {
-            proxy->guest_features[proxy->gfselect] = val;
-            virtio_set_features(vdev,
-                                (((uint64_t)proxy->guest_features[1]) << 32) |
-                                proxy->guest_features[0]);
+        if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features128)) {
+            uint64_t features[VIRTIO_FEATURES_DWORDS];
+            int i;
+
+            proxy->guest_features128[proxy->gfselect] = val;
+            virtio_features_clear(features);
+            for (i = 0; i < ARRAY_SIZE(proxy->guest_features128); ++i) {
+                uint64_t cur = proxy->guest_features128[i];
+
+                features[i >> 1] |= cur << ((i & 1) * 32);
+            }
+            virtio_set_features_ex(vdev, features);
          }
          break;
      case VIRTIO_PCI_COMMON_MSIX:

hw/virtio/virtio-pci.c static void virtio_pci_reset(DeviceState *qdev) · line 2390

Quoted 4 lines
hw/virtio/virtio-pci.c +1 -0
@@ -2310,6 +2387,10 @@ static void virtio_pci_reset(DeviceState *qdev)
      virtio_bus_reset(bus);
      msix_unuse_all_vectors(&proxy->pci_dev);
 
+    /* be sure to not carry over any feature across reset */

It's obvious so I don't think the comment makes difference.

hw/virtio/virtio-pci.c static void virtio_pci_reset(DeviceState *qdev) · line 2392

Quoted 2 lines
hw/virtio/virtio-pci.c +2 -0
+    memset(proxy->guest_features128, 0, sizeof(uint32_t) *
+           ARRAY_SIZE(proxy->guest_features128));

Simpler:
memset(proxy->guest_features128, 0, sizeof(proxy->guest_features128);

Quoted 17 lines
hw/virtio/virtio-pci.c +1 -0
+
      for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
          proxy->vqs[i].enabled = 0;
          proxy->vqs[i].reset = 0;
include/hw/virtio/virtio-pci.h +1 -0
index eab5394898..1868e3b106 100644
@@ -151,6 +151,7 @@ struct VirtIOPCIProxy {
      uint32_t flags;
      bool disable_modern;
      bool ignore_backend_features;
+    bool extended_features_loaded;
      OnOffAuto disable_legacy;
      /* Transitional device id */
      uint16_t trans_devid;

include/hw/virtio/virtio-pci.h struct VirtIOPCIProxy { · line 165

Quoted 8 lines
include/hw/virtio/virtio-pci.h +4 -1
@@ -158,7 +159,10 @@ struct VirtIOPCIProxy {
      uint32_t nvectors;
      uint32_t dfselect;
      uint32_t gfselect;
-    uint32_t guest_features[2];
+    union {
+        uint32_t guest_features[2];
+        uint32_t guest_features128[4];
+    };

I don't see anything preventing you from directly extending guest_features.

Quoted 3 lines
include/hw/virtio/virtio-pci.h +0 -0
      VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX];
 
      VirtIOIRQFD *vector_irqfd;
author maintainer
#15 lore raw

Re: [PATCH RFC v2 06/13] virtio-pci: implement support for extended features

Quoted 12 lines · Akihiko Odaki

include/hw/virtio/virtio-pci.h struct VirtIOPCIProxy { · line 165

Quoted 9 lines · Paolo Abeni
include/hw/virtio/virtio-pci.h +4 -1
@@ -158,7 +159,10 @@ struct VirtIOPCIProxy {
      uint32_t nvectors;
      uint32_t dfselect;
      uint32_t gfselect;
-    uint32_t guest_features[2];
+    union {
+        uint32_t guest_features[2];
+        uint32_t guest_features128[4];
+    };

I don't see anything preventing you from directly extending guest_features.

Uhmm... I have a vague memory of some interim revisions doing that and failing miserably (but I have no log of the actual details). I'll try to have another shot at it.

Thanks,

Paolo

author maintainer
#16 lore raw

Re: [PATCH RFC v2 06/13] virtio-pci: implement support for extended features

Quoted 14 lines · Paolo Abeni
Quoted 12 lines · Akihiko Odaki

include/hw/virtio/virtio-pci.h struct VirtIOPCIProxy { · line 165

Quoted 9 lines · Paolo Abeni
include/hw/virtio/virtio-pci.h +4 -1
@@ -158,7 +159,10 @@ struct VirtIOPCIProxy {
      uint32_t nvectors;
      uint32_t dfselect;
      uint32_t gfselect;
-    uint32_t guest_features[2];
+    union {
+        uint32_t guest_features[2];
+        uint32_t guest_features128[4];
+    };

I don't see anything preventing you from directly extending guest_features.

Quoted 3 lines

Uhmm... I have a vague memory of some interim revisions doing that and failing miserably (but I have no log of the actual details). I'll try to have another shot at it.

The VMSTATE_ARRAY() macro has explicit checks on the specified array matching exactly the specified array size. Using a single:

	uint32_t guest_features[4];

variable, this statement

VMSTATE_UINT32_ARRAY(guest_features, VirtIOPCIProxy, 2),

causes the following build error:

--
include/migration/vmstate.h:259:48: error: invalid operands to binary - (have ‘uint32_t (*)[2]’ {aka ‘unsigned int (*)[2]’} and ‘uint32_t (*)[4]’ {aka ‘unsigned int (*)[4]’})
259 | #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
| ^
include/migration/vmstate.h:282:6: note: in expansion of macro ‘type_check_array’

  282 |      type_check_array(_type, typeof_field(_state, _field), _num))
      |      ^~~~~~~~~~~~~~~~
include/migration/vmstate.h:373:19: note: in expansion of macro
‘vmstate_offset_array’
  373 |     .offset     = vmstate_offset_array(_state, _field, _type,
_num), \
      |                   ^~~~~~~~~~~~~~~~~~~~
include/migration/vmstate.h:1090:5: note: in expansion of macro
‘VMSTATE_ARRAY’
 1090 |     VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint32, uint32_t)
      |     ^~~~~~~~~~~~~
include/migration/vmstate.h:1096:5: note: in expansion of macro
‘VMSTATE_UINT32_ARRAY_V’
 1096 |     VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
      |     ^~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/virtio-pci.c:168:9: note: in expansion of macro
‘VMSTATE_UINT32_ARRAY’
  168 |         VMSTATE_UINT32_ARRAY(guest_features, VirtIOPCIProxy, 2),
      |         ^~~~~~~~~~~~~~~~~~~~

--

I'll keep the union here.

Thanks,

Paolo

#17 lore raw

Re: [PATCH RFC v2 06/13] virtio-pci: implement support for extended features

Quoted 57 lines · Paolo Abeni
Quoted 14 lines · Paolo Abeni
Quoted 12 lines · Akihiko Odaki

include/hw/virtio/virtio-pci.h struct VirtIOPCIProxy { · line 165

Quoted 9 lines · Paolo Abeni
include/hw/virtio/virtio-pci.h +4 -1
@@ -158,7 +159,10 @@ struct VirtIOPCIProxy {
       uint32_t nvectors;
       uint32_t dfselect;
       uint32_t gfselect;
-    uint32_t guest_features[2];
+    union {
+        uint32_t guest_features[2];
+        uint32_t guest_features128[4];
+    };

I don't see anything preventing you from directly extending guest_features.

Quoted 3 lines

Uhmm... I have a vague memory of some interim revisions doing that and failing miserably (but I have no log of the actual details). I'll try to have another shot at it.

The VMSTATE_ARRAY() macro has explicit checks on the specified array matching exactly the specified array size. Using a single:

	uint32_t guest_features[4];

variable, this statement

VMSTATE_UINT32_ARRAY(guest_features, VirtIOPCIProxy, 2),

causes the following build error:

--
include/migration/vmstate.h:259:48: error: invalid operands to binary - (have ‘uint32_t (*)[2]’ {aka ‘unsigned int (*)[2]’} and ‘uint32_t (*)[4]’ {aka ‘unsigned int (*)[4]’})
259 | #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
| ^
include/migration/vmstate.h:282:6: note: in expansion of macro ‘type_check_array’

   282 |      type_check_array(_type, typeof_field(_state, _field), _num))
       |      ^~~~~~~~~~~~~~~~
include/migration/vmstate.h:373:19: note: in expansion of macro
‘vmstate_offset_array’
   373 |     .offset     = vmstate_offset_array(_state, _field, _type,
_num), \
       |                   ^~~~~~~~~~~~~~~~~~~~
include/migration/vmstate.h:1090:5: note: in expansion of macro
‘VMSTATE_ARRAY’
  1090 |     VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint32, uint32_t)
       |     ^~~~~~~~~~~~~
include/migration/vmstate.h:1096:5: note: in expansion of macro
‘VMSTATE_UINT32_ARRAY_V’
  1096 |     VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
       |     ^~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/virtio-pci.c:168:9: note: in expansion of macro
‘VMSTATE_UINT32_ARRAY’
   168 |         VMSTATE_UINT32_ARRAY(guest_features, VirtIOPCIProxy, 2),
       |         ^~~~~~~~~~~~~~~~~~~~
Quoted 3 lines

--

I'll keep the union here.

I think you can use VMSTATE_UINT32_SUB_ARRAY().

Regards,
Akihiko Odaki

author maintainer
#18 lore raw

RFC PATCH v2 7/13 vhost: add support for negotiating extended features

In reply to Paolo Abeni

Similar to virtio infra, vhost core maintain the features status in the full extended format and allow the devices to implement extended version of the getter/setter.

Note that 'protocol_features' are not extended: they are only used by vhost-user, and the latter device is not going to implement extended features soon.

---
v1 -> v2:
- uint128_t -> uint64_t[]
- add _ex() variant of features manipulation helpers

---
 hw/virtio/vhost.c                 | 73 +++++++++++++++++++++++++++----
 include/hw/virtio/vhost-backend.h |  6 +++
 include/hw/virtio/vhost.h         | 36 +++++++++++++--
 3 files changed, 102 insertions(+), 13 deletions(-)
hw/virtio/vhost.c +64 -9
index fc43853704..2eee9b0886 100644
@@ -985,20 +985,34 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
 static int vhost_dev_set_features(struct vhost_dev *dev,
                                   bool enable_log)
 {
-    uint64_t features = dev->acked_features;
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
     int r;
+
+    virtio_features_copy(features, dev->acked_features_array);
     if (enable_log) {
-        features |= 0x1ULL << VHOST_F_LOG_ALL;
+        virtio_add_feature_ex(features, VHOST_F_LOG_ALL);
     }
     if (!vhost_dev_has_iommu(dev)) {
-        features &= ~(0x1ULL << VIRTIO_F_IOMMU_PLATFORM);
+        virtio_clear_feature_ex(features, VIRTIO_F_IOMMU_PLATFORM);
     }
     if (dev->vhost_ops->vhost_force_iommu) {
         if (dev->vhost_ops->vhost_force_iommu(dev) == true) {
-            features |= 0x1ULL << VIRTIO_F_IOMMU_PLATFORM;
+            virtio_add_feature_ex(features, VIRTIO_F_IOMMU_PLATFORM);
        }
     }
-    r = dev->vhost_ops->vhost_set_features(dev, features);
+
+    if (virtio_features_use_extended(features) &&
+        !dev->vhost_ops->vhost_set_features_ex) {
+        VHOST_OPS_DEBUG(r, "extended features without device support");
+        r = -EINVAL;
+        goto out;
+    }
+
+    if (dev->vhost_ops->vhost_set_features_ex) {
+        r = dev->vhost_ops->vhost_set_features_ex(dev, features);
+    } else {
+        r = dev->vhost_ops->vhost_set_features(dev, features[0]);
+    }
     if (r < 0) {
         VHOST_OPS_DEBUG(r, "vhost_set_features failed");
         goto out;
@@ -1506,12 +1520,27 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq)
     }
 }
 
+static int vhost_dev_get_features(struct vhost_dev *hdev,
+                                  uint64_t *features)
+{
+    uint64_t features64;
+    int r;
+
+    if (hdev->vhost_ops->vhost_get_features_ex) {
+        return hdev->vhost_ops->vhost_get_features_ex(hdev, features);
+    }
+
+    r = hdev->vhost_ops->vhost_get_features(hdev, &features64);
+    virtio_features_from_u64(features, features64);
+    return r;
+}
+
 int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
                    VhostBackendType backend_type, uint32_t busyloop_timeout,
                    Error **errp)
 {
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
     unsigned int used, reserved, limit;
-    uint64_t features;
     int i, r, n_initialized_vqs = 0;
 
     hdev->vdev = NULL;
@@ -1531,7 +1560,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
         goto fail;
     }
 
-    r = hdev->vhost_ops->vhost_get_features(hdev, &features);
+    r = vhost_dev_get_features(hdev, features);
     if (r < 0) {
         error_setg_errno(errp, -r, "vhost_get_features failed");
         goto fail;
@@ -1569,7 +1598,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
         }
     }
 
-    hdev->features = features;
+    virtio_features_copy(hdev->features_array, features);
 
     hdev->memory_listener = (MemoryListener) {
         .name = "vhost",
@@ -1592,7 +1621,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
     };
 
     if (hdev->migration_blocker == NULL) {
-        if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) {
+        if (!virtio_has_feature_ex(hdev->features_array, VHOST_F_LOG_ALL)) {
             error_setg(&hdev->migration_blocker,
                        "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature.");
         } else if (vhost_dev_log_is_shared(hdev) && !qemu_memfd_alloc_check()) {
@@ -1875,6 +1904,20 @@ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
     return features;
 }
 
+void vhost_get_features_ex(struct vhost_dev *hdev,
+                           const int *feature_bits,
+                           uint64_t *features)
+{
+    const int *bit = feature_bits;
+
+    while (*bit != VHOST_INVALID_FEATURE_BIT) {
+        if (!virtio_has_feature_ex(hdev->features_array, *bit)) {
+            virtio_clear_feature_ex(features, *bit);
+        }
+        bit++;
+    }
+}
+
 void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
                         uint64_t features)
 {
@@ -1888,6 +1931,18 @@ void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
     }
 }
 
+void vhost_ack_features_ex(struct vhost_dev *hdev, const int *feature_bits,
+                           const uint64_t *features)
+{
+    const int *bit = feature_bits;
+    while (*bit != VHOST_INVALID_FEATURE_BIT) {
+        if (virtio_has_feature_ex(features, *bit)) {
+            virtio_add_feature_ex(hdev->acked_features_array, *bit);
+        }
+        bit++;
+    }
+}
+
 int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config,
                          uint32_t config_len, Error **errp)
 {
include/hw/virtio/vhost-backend.h +6 -0
index d6df209a2f..ff94fa1734 100644
@@ -95,6 +95,10 @@ typedef int (*vhost_new_worker_op)(struct vhost_dev *dev,
                                    struct vhost_worker_state *worker);
 typedef int (*vhost_free_worker_op)(struct vhost_dev *dev,
                                     struct vhost_worker_state *worker);
+typedef int (*vhost_set_features_ex_op)(struct vhost_dev *dev,
+                                        const uint64_t *features);
+typedef int (*vhost_get_features_ex_op)(struct vhost_dev *dev,
+                                        uint64_t *features);
 typedef int (*vhost_set_features_op)(struct vhost_dev *dev,
                                      uint64_t features);
 typedef int (*vhost_get_features_op)(struct vhost_dev *dev,
@@ -186,6 +190,8 @@ typedef struct VhostOps {
     vhost_free_worker_op vhost_free_worker;
     vhost_get_vring_worker_op vhost_get_vring_worker;
     vhost_attach_vring_worker_op vhost_attach_vring_worker;
+    vhost_set_features_ex_op vhost_set_features_ex;
+    vhost_get_features_ex_op vhost_get_features_ex;
     vhost_set_features_op vhost_set_features;
     vhost_get_features_op vhost_get_features;
     vhost_set_backend_cap_op vhost_set_backend_cap;
include/hw/virtio/vhost.h +32 -4
index 38800a7156..2eec457559 100644
@@ -106,9 +106,9 @@ struct vhost_dev {
      * future use should be discouraged and the variable retired as
      * its easy to confuse with the VirtIO backend_features.
      */
-    uint64_t features;
-    uint64_t acked_features;
-    uint64_t backend_features;
+    VIRTIO_DECLARE_FEATURES(features);
+    VIRTIO_DECLARE_FEATURES(acked_features);
+    VIRTIO_DECLARE_FEATURES(backend_features);
 
     /**
      * @protocol_features: is the vhost-user only feature set by
@@ -310,9 +310,25 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
  * is supported by the vhost backend (hdev->features), the supported
  * feature_bits and the requested feature set.
  */
-uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
+uint64_t vhost_get_features(struct vhost_dev *hdev,
+                            const int *feature_bits,
                             uint64_t features);
 
+/**
+ * vhost_get_features_ex() - sanitize the extended features set
+ * @hdev: common vhost_dev structure
+ * @feature_bits: pointer to terminated table of feature bits
+ * @features: original features set, filtered out on return
+ *
+ * This is the extended variant of vhost_get_features(), supporting the
+ * the extended features set. Filter it with the intersection of what is
+ * supported by the vhost backend (hdev->features) and the supported
+ * feature_bits.
+ */
+void vhost_get_features_ex(struct vhost_dev *hdev,
+                           const int *feature_bits,
+                           uint64_t *features);
+
 /**
  * vhost_ack_features() - set vhost acked_features
  * @hdev: common vhost_dev structure
@@ -324,6 +340,18 @@ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
  */
 void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
                         uint64_t features);
+
+/**
+ * vhost_ack_features_ex() - set vhost full set of acked_features
+ * @hdev: common vhost_dev structure
+ * @feature_bits: pointer to terminated table of feature bits
+ * @features: requested feature set
+ *
+ * This sets the internal hdev->acked_features to the intersection of
+ * the backends advertised features and the supported feature_bits.
+ */
+void vhost_ack_features_ex(struct vhost_dev *hdev, const int *feature_bits,
+                           const uint64_t *features);
 unsigned int vhost_get_max_memslots(void);
 unsigned int vhost_get_free_memslots(void);
 
Signature · 1 line
2.50.0
author maintainer
#19 lore raw

RFC PATCH v2 8/13 qmp: update virtio features map to support extended features

In reply to Paolo Abeni

Extend the VirtioDeviceFeatures struct with an additional u64 to track unknown features in the 65-128 bit range and decode the full virtio features spaces for vhost and virtio devices.

---
I'm unsure if it's actually legit to update a qapi struct definition?

v1 -> v2:
- uint128_t -> uint64_t[]

---
 hw/virtio/virtio-hmp-cmds.c |  3 +-
 hw/virtio/virtio-qmp.c      | 89 ++++++++++++++++++++++++++-----------
 hw/virtio/virtio-qmp.h      |  3 +-
 qapi/virtio.json            |  8 +++-
 4 files changed, 73 insertions(+), 30 deletions(-)
hw/virtio/virtio-hmp-cmds.c +2 -1
index 7d8677bcf0..e8c2a76a2a 100644
@@ -74,7 +74,8 @@ static void hmp_virtio_dump_features(Monitor *mon,
     }
 
     if (features->has_unknown_dev_features) {
-        monitor_printf(mon, "  unknown-features(0x%016"PRIx64")\n",
+        monitor_printf(mon, "  unknown-features(0x%016"PRIx64"%016"PRIx64")\n",
+                       features->unknown_dev_features_dword2,
                        features->unknown_dev_features);
     }
 }
hw/virtio/virtio-qmp.c +63 -26

215 lines not on this page · raw message

hw/virtio/virtio-qmp.h +2 -1
index 245a446a56..e0a1e49035 100644
@@ -18,6 +18,7 @@
 VirtIODevice *qmp_find_virtio_device(const char *path);
 VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
 VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
+                                          const uint64_t *bitmap);
 
 #endif
qapi/virtio.json +6 -2
index 73df718a26..f0442e144b 100644
@@ -488,14 +488,18 @@
 #     unique features)
 #
 # @unknown-dev-features: Virtio device features bitmap that have not
-#     been decoded
+#     been decoded (lower 64 bit)
+#
+# @unknown-dev-features-dword2: Virtio device features bitmap that have not
+#     been decoded (bits 65-128)
 #
 # Since: 7.2
 ##
 { 'struct': 'VirtioDeviceFeatures',
   'data': { 'transports': [ 'str' ],
             '*dev-features': [ 'str' ],
-            '*unknown-dev-features': 'uint64' } }
+            '*unknown-dev-features': 'uint64',
+            '*unknown-dev-features-dword2': 'uint64' } }
 
 ##
 # @VirtQueueStatus:
Signature · 1 line
2.50.0
#20 lore raw

Re: [PATCH RFC v2 08/13] qmp: update virtio features map to support extended features

Quoted 282 lines · Paolo Abeni

Extend the VirtioDeviceFeatures struct with an additional u64 to track unknown features in the 65-128 bit range and decode the full virtio features spaces for vhost and virtio devices.

---
I'm unsure if it's actually legit to update a qapi struct definition?

v1 -> v2:
- uint128_t -> uint64_t[]

---
  hw/virtio/virtio-hmp-cmds.c |  3 +-
  hw/virtio/virtio-qmp.c      | 89 ++++++++++++++++++++++++++-----------
  hw/virtio/virtio-qmp.h      |  3 +-
  qapi/virtio.json            |  8 +++-
  4 files changed, 73 insertions(+), 30 deletions(-)
hw/virtio/virtio-hmp-cmds.c +2 -1
index 7d8677bcf0..e8c2a76a2a 100644
@@ -74,7 +74,8 @@ static void hmp_virtio_dump_features(Monitor *mon,
      }
 
      if (features->has_unknown_dev_features) {
-        monitor_printf(mon, "  unknown-features(0x%016"PRIx64")\n",
+        monitor_printf(mon, "  unknown-features(0x%016"PRIx64"%016"PRIx64")\n",
+                       features->unknown_dev_features_dword2,
                         features->unknown_dev_features);
      }
  }
hw/virtio/virtio-qmp.c +63 -26
index 3b6377cf0d..0d06e7a7db 100644
@@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = {
      FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \
              "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features "
              "negotiation supported"),
+    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \
+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
+            "UDP tunnel packets"),
+    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \
+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
+            "UDP tunnel packets requiring checksum offload for the outer "
+            "header"),
+    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \
+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Device can receive GSO over "
+            "UDP tunnel packets"),
+    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \
+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Device can receive GSO over "
+            "UDP tunnel packets requiring checksum offload for the outer "
+            "header"),
      { -1, "" }
  };
  #endif
@@ -510,6 +524,24 @@ static const qmp_virtio_feature_map_t virtio_gpio_feature_map[] = {
          list;                                            \
      })
 
+#define CONVERT_FEATURES_EX(type, map, bitmap)           \
+    ({                                                   \
+        type *list = NULL;                               \
+        type *node;                                      \
+        for (i = 0; map[i].virtio_bit != -1; i++) {      \
+            bit = map[i].virtio_bit;                     \
+            if (!virtio_has_feature_ex(bitmap, bit)) {   \
+                continue;                                \
+            }                                            \
+            node = g_new0(type, 1);                      \
+            node->value = g_strdup(map[i].feature_desc); \
+            node->next = list;                           \
+            list = node;                                 \
+            virtio_clear_feature_ex(bitmap, bit);        \
+        }                                                \
+        list;                                            \
+    })
+
  VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap)
  {
      VirtioDeviceStatus *status;
@@ -545,109 +577,112 @@ VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap)
      return vhu_protocols;
  }
 
-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
+                                          const uint64_t *bmap)
  {
+    uint64_t bitmap[VIRTIO_FEATURES_DWORDS];
      VirtioDeviceFeatures *features;
      uint64_t bit;
      int i;
 
+    virtio_features_copy(bitmap, bmap);
      features = g_new0(VirtioDeviceFeatures, 1);
      features->has_dev_features = true;
 
      /* transport features */
-    features->transports = CONVERT_FEATURES(strList, virtio_transport_map, 0,
-                                            bitmap);
+    features->transports = CONVERT_FEATURES_EX(strList, virtio_transport_map,
+                                               bitmap);
 
      /* device features */
      switch (device_id) {
  #ifdef CONFIG_VIRTIO_SERIAL
      case VIRTIO_ID_CONSOLE:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_serial_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_serial_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_BLK
      case VIRTIO_ID_BLOCK:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_blk_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_blk_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_GPU
      case VIRTIO_ID_GPU:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_gpu_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_gpu_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_NET
      case VIRTIO_ID_NET:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_net_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_net_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_SCSI
      case VIRTIO_ID_SCSI:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_scsi_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_scsi_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_BALLOON
      case VIRTIO_ID_BALLOON:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_balloon_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_balloon_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_IOMMU
      case VIRTIO_ID_IOMMU:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_iommu_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_iommu_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_INPUT
      case VIRTIO_ID_INPUT:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_input_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_input_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VHOST_USER_FS
      case VIRTIO_ID_FS:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_fs_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_fs_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VHOST_VSOCK
      case VIRTIO_ID_VSOCK:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_vsock_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_vsock_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_CRYPTO
      case VIRTIO_ID_CRYPTO:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_crypto_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_crypto_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_MEM
      case VIRTIO_ID_MEM:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_mem_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_mem_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_I2C_ADAPTER
      case VIRTIO_ID_I2C_ADAPTER:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_i2c_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_i2c_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VIRTIO_RNG
      case VIRTIO_ID_RNG:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_rng_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_rng_feature_map, bitmap);
          break;
  #endif
  #ifdef CONFIG_VHOST_USER_GPIO
      case VIRTIO_ID_GPIO:
          features->dev_features =
-            CONVERT_FEATURES(strList, virtio_gpio_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_gpio_feature_map, bitmap);
          break;
  #endif
      /* No features */
@@ -680,9 +715,10 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
          g_assert_not_reached();
      }
 
-    features->has_unknown_dev_features = bitmap != 0;
+    features->has_unknown_dev_features = virtio_features_is_empty(bitmap);
      if (features->has_unknown_dev_features) {
-        features->unknown_dev_features = bitmap;
+        features->unknown_dev_features = bitmap[0];
+        features->unknown_dev_features_dword2 = bitmap[1];
      }
 
      return features;
@@ -743,11 +779,11 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
      status->device_id = vdev->device_id;
      status->vhost_started = vdev->vhost_started;
      status->guest_features = qmp_decode_features(vdev->device_id,
-                                                 vdev->guest_features);
+                                                 vdev->guest_features_array);
      status->host_features = qmp_decode_features(vdev->device_id,
-                                                vdev->host_features);
+                                                vdev->host_features_array);
      status->backend_features = qmp_decode_features(vdev->device_id,
-                                                   vdev->backend_features);
+                                                 vdev->backend_features_array);
 
      switch (vdev->device_endian) {
      case VIRTIO_DEVICE_ENDIAN_LITTLE:
@@ -785,11 +821,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
          status->vhost_dev->nvqs = hdev->nvqs;
          status->vhost_dev->vq_index = hdev->vq_index;
          status->vhost_dev->features =
-            qmp_decode_features(vdev->device_id, hdev->features);
+            qmp_decode_features(vdev->device_id, hdev->features_array);
          status->vhost_dev->acked_features =
-            qmp_decode_features(vdev->device_id, hdev->acked_features);
+            qmp_decode_features(vdev->device_id, hdev->acked_features_array);
          status->vhost_dev->backend_features =
-            qmp_decode_features(vdev->device_id, hdev->backend_features);
+            qmp_decode_features(vdev->device_id, hdev->backend_features_array);
+
          status->vhost_dev->protocol_features =
              qmp_decode_protocols(hdev->protocol_features);
          status->vhost_dev->max_queues = hdev->max_queues;
hw/virtio/virtio-qmp.h +2 -1
index 245a446a56..e0a1e49035 100644
@@ -18,6 +18,7 @@
  VirtIODevice *qmp_find_virtio_device(const char *path);
  VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
  VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
+                                          const uint64_t *bitmap);
 
  #endif
qapi/virtio.json +3 -1
index 73df718a26..f0442e144b 100644
@@ -488,14 +488,18 @@
  #     unique features)
  #
  # @unknown-dev-features: Virtio device features bitmap that have not
-#     been decoded
+#     been decoded (lower 64 bit)
+#
+# @unknown-dev-features-dword2: Virtio device features bitmap that have not

qapi/virtio.json line 502

Quoted 10 lines
qapi/virtio.json +3 -1
+#     been decoded (bits 65-128)
  #
  # Since: 7.2
  ##
  { 'struct': 'VirtioDeviceFeatures',
    'data': { 'transports': [ 'str' ],
              '*dev-features': [ 'str' ],
-            '*unknown-dev-features': 'uint64' } }
+            '*unknown-dev-features': 'uint64',
+            '*unknown-dev-features-dword2': 'uint64' } }

Let's omit "dword" for consistency with unknown-dev-features, which is also uint64 but don't have the keyword.

By the way, "dword" is somewhat confusing in QEMU; in many cases (if not all), it represents 32-bit because of Windows and PCI, which define dword as 32-bit. "U64" is a more common phrase.

Quoted 3 lines
qapi/virtio.json +0 -0
  ##
  # @VirtQueueStatus:
author maintainer
#21 lore raw

Re: [PATCH RFC v2 08/13] qmp: update virtio features map to support extended features

Quoted 47 lines · Akihiko Odaki
Quoted 46 lines · Paolo Abeni
hw/virtio/virtio-qmp.c +4 -3
@@ -785,11 +821,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
          status->vhost_dev->nvqs = hdev->nvqs;
          status->vhost_dev->vq_index = hdev->vq_index;
          status->vhost_dev->features =
-            qmp_decode_features(vdev->device_id, hdev->features);
+            qmp_decode_features(vdev->device_id, hdev->features_array);
          status->vhost_dev->acked_features =
-            qmp_decode_features(vdev->device_id, hdev->acked_features);
+            qmp_decode_features(vdev->device_id, hdev->acked_features_array);
          status->vhost_dev->backend_features =
-            qmp_decode_features(vdev->device_id, hdev->backend_features);
+            qmp_decode_features(vdev->device_id, hdev->backend_features_array);
+
          status->vhost_dev->protocol_features =
              qmp_decode_protocols(hdev->protocol_features);
          status->vhost_dev->max_queues = hdev->max_queues;
hw/virtio/virtio-qmp.h +2 -1
index 245a446a56..e0a1e49035 100644
@@ -18,6 +18,7 @@
  VirtIODevice *qmp_find_virtio_device(const char *path);
  VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
  VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
+                                          const uint64_t *bitmap);
 
  #endif
qapi/virtio.json +4 -1
index 73df718a26..f0442e144b 100644
@@ -488,14 +488,18 @@
  #     unique features)
  #
  # @unknown-dev-features: Virtio device features bitmap that have not
-#     been decoded
+#     been decoded (lower 64 bit)
+#
+# @unknown-dev-features-dword2: Virtio device features bitmap that have not
+#     been decoded (bits 65-128)
  #
  # Since: 7.2
  ##
  { 'struct': 'VirtioDeviceFeatures',
Quoted 8 lines

qapi/virtio.json line 502

Quoted 5 lines
qapi/virtio.json +2 -1
    'data': { 'transports': [ 'str' ],
              '*dev-features': [ 'str' ],
-            '*unknown-dev-features': 'uint64' } }
+            '*unknown-dev-features': 'uint64',
+            '*unknown-dev-features-dword2': 'uint64' } }

Let's omit "dword" for consistency with unknown-dev-features, which is also uint64 but don't have the keyword.

Ok. Can I infer that is actually legit to update a qapi struct definition? It's not clear to me it such change violates any qemu assumptions.

/P

#22 lore raw

Re: [PATCH RFC v2 08/13] qmp: update virtio features map to support extended features

Quoted 57 lines · Paolo Abeni
Quoted 47 lines · Akihiko Odaki
Quoted 46 lines · Paolo Abeni
hw/virtio/virtio-qmp.c +4 -3
@@ -785,11 +821,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
           status->vhost_dev->nvqs = hdev->nvqs;
           status->vhost_dev->vq_index = hdev->vq_index;
           status->vhost_dev->features =
-            qmp_decode_features(vdev->device_id, hdev->features);
+            qmp_decode_features(vdev->device_id, hdev->features_array);
           status->vhost_dev->acked_features =
-            qmp_decode_features(vdev->device_id, hdev->acked_features);
+            qmp_decode_features(vdev->device_id, hdev->acked_features_array);
           status->vhost_dev->backend_features =
-            qmp_decode_features(vdev->device_id, hdev->backend_features);
+            qmp_decode_features(vdev->device_id, hdev->backend_features_array);
+
           status->vhost_dev->protocol_features =
               qmp_decode_protocols(hdev->protocol_features);
           status->vhost_dev->max_queues = hdev->max_queues;
hw/virtio/virtio-qmp.h +2 -1
index 245a446a56..e0a1e49035 100644
@@ -18,6 +18,7 @@
   VirtIODevice *qmp_find_virtio_device(const char *path);
   VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
   VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
+                                          const uint64_t *bitmap);
 
   #endif
qapi/virtio.json +4 -1
index 73df718a26..f0442e144b 100644
@@ -488,14 +488,18 @@
   #     unique features)
   #
   # @unknown-dev-features: Virtio device features bitmap that have not
-#     been decoded
+#     been decoded (lower 64 bit)
+#
+# @unknown-dev-features-dword2: Virtio device features bitmap that have not
+#     been decoded (bits 65-128)
   #
   # Since: 7.2
   ##
   { 'struct': 'VirtioDeviceFeatures',
Quoted 8 lines

qapi/virtio.json line 502

Quoted 5 lines
qapi/virtio.json +2 -1
     'data': { 'transports': [ 'str' ],
               '*dev-features': [ 'str' ],
-            '*unknown-dev-features': 'uint64' } }
+            '*unknown-dev-features': 'uint64',
+            '*unknown-dev-features-dword2': 'uint64' } }

Let's omit "dword" for consistency with unknown-dev-features, which is also uint64 but don't have the keyword.

Quoted 3 lines

Ok. Can I infer that is actually legit to update a qapi struct definition? It's not clear to me it such change violates any qemu assumptions.

Adding a property is fine but renaming one is not as it can break application that use QAPI such as libvirt. For some guidance, please see: docs/devel/qapi-code-gen.rst

author maintainer
#23 lore raw

RFC PATCH v2 9/13 vhost-backend: implement extended features support

In reply to Paolo Abeni

Leverage the kernel extended features manipulation ioctls(), if available, and fallback to old ops otherwise. Error out when setting extended features but kernel support is not available.

Note that extended support for get/set backend features is not needed, as the only feature that can be changed belongs to the 64 bit range.

---
v1 -> v2:
- synced with kernel ioctl changes

---
 hw/virtio/vhost-backend.c | 62 ++++++++++++++++++++++++++++++++-------
 1 file changed, 51 insertions(+), 11 deletions(-)
hw/virtio/vhost-backend.c +51 -11
index 833804dd40..6fde994403 100644
@@ -20,6 +20,11 @@
 #include <linux/vhost.h>
 #include <sys/ioctl.h>
 
+struct vhost_features {
+    uint64_t count;
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
+};
+
 static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request,
                              void *arg)
 {
@@ -182,12 +187,6 @@ static int vhost_kernel_get_vring_worker(struct vhost_dev *dev,
     return vhost_kernel_call(dev, VHOST_GET_VRING_WORKER, worker);
 }
 
-static int vhost_kernel_set_features(struct vhost_dev *dev,
-                                     uint64_t features)
-{
-    return vhost_kernel_call(dev, VHOST_SET_FEATURES, &features);
-}
-
 static int vhost_kernel_set_backend_cap(struct vhost_dev *dev)
 {
     uint64_t features;
@@ -210,10 +209,51 @@ static int vhost_kernel_set_backend_cap(struct vhost_dev *dev)
     return 0;
 }
 
-static int vhost_kernel_get_features(struct vhost_dev *dev,
-                                     uint64_t *features)
+static int vhost_kernel_set_features(struct vhost_dev *dev,
+                                     const uint64_t *features)
 {
-    return vhost_kernel_call(dev, VHOST_GET_FEATURES, features);
+    struct vhost_features farray;
+    bool extended_in_use;
+    int r;
+
+    farray.count = VIRTIO_FEATURES_DWORDS;
+    virtio_features_copy(farray.features, features);
+    extended_in_use = virtio_features_use_extended(farray.features);
+
+    /*
+     * Can't check for ENOTTY: for unknown ioctls the kernel interprets
+     * the argument as a virtio queue id and most likely errors out validating
+     * such id, instead of reporting an unknown operation.
+     */
+    r = vhost_kernel_call(dev, VHOST_SET_FEATURES_ARRAY, &farray);
+    if (!r) {
+        return 0;
+    }
+
+    if (extended_in_use) {
+        error_report("Trying to set extended features without kernel support");
+        return -EINVAL;
+    }
+    return vhost_kernel_call(dev, VHOST_SET_FEATURES, &farray.features[0]);
+}
+
+static int vhost_kernel_get_features(struct vhost_dev *dev, uint64_t *features)
+{
+    struct vhost_features farray;
+    int r;
+
+    farray.count = VIRTIO_FEATURES_DWORDS;
+    r = vhost_kernel_call(dev, VHOST_GET_FEATURES_ARRAY, &farray);
+    if (r) {
+        memset(&farray, 0, sizeof(farray));
+        r = vhost_kernel_call(dev, VHOST_GET_FEATURES, &farray.features[0]);
+    }
+    if (r) {
+        return r;
+    }
+
+    virtio_features_copy(features, farray.features);
+    return 0;
 }
 
 static int vhost_kernel_set_owner(struct vhost_dev *dev)
@@ -341,8 +381,8 @@ const VhostOps kernel_ops = {
         .vhost_attach_vring_worker = vhost_kernel_attach_vring_worker,
         .vhost_new_worker = vhost_kernel_new_worker,
         .vhost_free_worker = vhost_kernel_free_worker,
-        .vhost_set_features = vhost_kernel_set_features,
-        .vhost_get_features = vhost_kernel_get_features,
+        .vhost_set_features_ex = vhost_kernel_set_features,
+        .vhost_get_features_ex = vhost_kernel_get_features,
         .vhost_set_backend_cap = vhost_kernel_set_backend_cap,
         .vhost_set_owner = vhost_kernel_set_owner,
         .vhost_get_vq_index = vhost_kernel_get_vq_index,
Signature · 1 line
2.50.0
author maintainer
#24 lore raw

RFC PATCH v2 10/13 vhost-net: implement extended features support

In reply to Paolo Abeni

Provide extended version of the features manipulation helpers, and let the device initialization deal with the full features space, adjusting the relevant format strings accordingly.

---
v1 -> v2:
- uint128_t -> uint64_t[]
- provide extended variant of the features manipulation helpers

---
 hw/net/vhost_net-stub.c |  8 +++----
 hw/net/vhost_net.c      | 48 +++++++++++++++++++++++------------------
 include/net/vhost_net.h | 33 +++++++++++++++++++++++++---
 3 files changed, 60 insertions(+), 29 deletions(-)
hw/net/vhost_net-stub.c +3 -5
index 72df6d757e..f20b8a216d 100644
@@ -47,9 +47,8 @@ void vhost_net_cleanup(struct vhost_net *net)
 {
 }
 
-uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
+void vhost_net_get_features_ex(struct vhost_net *net, uint64_t *features)
 {
-    return features;
 }
 
 int vhost_net_get_config(struct vhost_net *net,  uint8_t *config,
@@ -63,13 +62,12 @@ int vhost_net_set_config(struct vhost_net *net, const uint8_t *data,
     return 0;
 }
 
-void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
+void vhost_net_ack_features_ex(struct vhost_net *net, const uint64_t *features)
 {
 }
 
-uint64_t vhost_net_get_acked_features(VHostNetState *net)
+void vhost_net_get_acked_features_ex(VHostNetState *net, uint64_t *features)
 {
-    return 0;
 }
 
 bool vhost_net_virtqueue_pending(VHostNetState *net, int idx)
hw/net/vhost_net.c +27 -21

99 lines not on this page · raw message

include/net/vhost_net.h +30 -3
index c6a5361a2a..f30a206e73 100644
@@ -2,6 +2,7 @@
 #define VHOST_NET_H
 
 #include "net/net.h"
+#include "hw/virtio/virtio-features.h"
 #include "hw/virtio/vhost-backend.h"
 
 struct vhost_net;
@@ -25,8 +26,26 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs,
 
 void vhost_net_cleanup(VHostNetState *net);
 
-uint64_t vhost_net_get_features(VHostNetState *net, uint64_t features);
-void vhost_net_ack_features(VHostNetState *net, uint64_t features);
+void vhost_net_get_features_ex(VHostNetState *net, uint64_t *features);
+static inline uint64_t vhost_net_get_features(VHostNetState *net,
+                                              uint64_t features)
+{
+    uint64_t features_array[VIRTIO_FEATURES_DWORDS];
+
+    virtio_features_from_u64(features_array, features);
+    vhost_net_get_features_ex(net, features_array);
+    return features_array[0];
+}
+
+void vhost_net_ack_features_ex(VHostNetState *net, const uint64_t *features);
+static inline void vhost_net_ack_features(VHostNetState *net,
+                                          uint64_t features)
+{
+    uint64_t features_array[VIRTIO_FEATURES_DWORDS];
+
+    virtio_features_from_u64(features_array, features);
+    vhost_net_ack_features_ex(net, features_array);
+}
 
 int vhost_net_get_config(struct vhost_net *net,  uint8_t *config,
                          uint32_t config_len);
@@ -43,7 +62,15 @@ VHostNetState *get_vhost_net(NetClientState *nc);
 
 int vhost_set_vring_enable(NetClientState * nc, int enable);
 
-uint64_t vhost_net_get_acked_features(VHostNetState *net);
+void vhost_net_get_acked_features_ex(VHostNetState *net, uint64_t *features);
+static inline uint64_t vhost_net_get_acked_features(VHostNetState *net)
+{
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
+
+    vhost_net_get_acked_features_ex(net, features);
+    assert(!virtio_features_use_extended(features));
+    return features[0];
+}
 
 int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu);
 
Signature · 1 line
2.50.0
author maintainer
#25 lore raw

RFC PATCH v2 11/13 virtio-net: implement extended features support

In reply to Paolo Abeni

Use the extended types and helpers to manipulate the virtio_net features.

Note that offloads are still 64bits wide, as per specification, and extended offloads will be mapped into such range.

---
v1 -> v2:
- uint128_t -> uint64_t[]
- more verbose macro definitions

---
 hw/net/virtio-net.c            | 125 +++++++++++++++++++--------------
 include/hw/virtio/virtio-net.h |   2 +-
 2 files changed, 75 insertions(+), 52 deletions(-)
hw/net/virtio-net.c +74 -51
index 16df9e85c8..09d5ef1ece 100644
@@ -90,6 +90,19 @@
                                          VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | \
                                          VIRTIO_NET_RSS_HASH_TYPE_UDP_EX)
 
+/*
+ * Features starting from VIRTIO_NET_FEATURES_MAP_MIN bit correspond
+ * to guest offloads in the VIRTIO_NET_OFFLOAD_MAP range
+ */
+#define VIRTIO_NET_OFFLOAD_MAP_MIN    46
+#define VIRTIO_NET_OFFLOAD_MAP_LENGTH 4
+#define VIRTIO_NET_OFFLOAD_MAP        MAKE_64BIT_MASK(                    \
+                                              VIRTIO_NET_OFFLOAD_MAP_MIN, \
+                                              VIRTIO_NET_OFFLOAD_MAP_LENGTH)
+#define VIRTIO_NET_FEATURES_MAP_MIN   65
+#define VIRTIO_NET_F2O_SHIFT          (VIRTIO_NET_OFFLOAD_MAP_MIN - \
+                                       VIRTIO_NET_FEATURES_MAP_MIN + 64)
+
 static const VirtIOFeature feature_sizes[] = {
     {.flags = 1ULL << VIRTIO_NET_F_MAC,
      .end = endof(struct virtio_net_config, mac)},
@@ -752,59 +765,59 @@ static void virtio_net_set_queue_pairs(VirtIONet *n)
 
 static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
 
-static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
-                                        Error **errp)
+static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
+                                    Error **errp)
 {
     VirtIONet *n = VIRTIO_NET(vdev);
     NetClientState *nc = qemu_get_queue(n->nic);
 
     /* Firstly sync all virtio-net possible supported features */
-    features |= n->host_features;
+    virtio_features_or(features, features, n->host_features_array);
 
-    virtio_add_feature(&features, VIRTIO_NET_F_MAC);
+    virtio_add_feature_ex(features, VIRTIO_NET_F_MAC);
 
     if (!peer_has_vnet_hdr(n)) {
-        virtio_clear_feature(&features, VIRTIO_NET_F_CSUM);
-        virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4);
-        virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6);
-        virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_CSUM);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_TSO4);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_TSO6);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_ECN);
 
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM);
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_CSUM);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_TSO4);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_TSO6);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_ECN);
 
-        virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO);
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4);
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_USO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO4);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6);
 
-        virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT);
     }
 
     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO);
-        virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UFO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UFO);
     }
 
     if (!peer_has_uso(n)) {
-        virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO);
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4);
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_USO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO4);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6);
     }
 
     if (!get_vhost_net(nc->peer)) {
-        return features;
+        return;
     }
 
     if (!ebpf_rss_is_loaded(&n->ebpf_rss)) {
-        virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_RSS);
     }
-    features = vhost_net_get_features(get_vhost_net(nc->peer), features);
-    vdev->backend_features = features;
+    vhost_net_get_features_ex(get_vhost_net(nc->peer), features);
+    virtio_features_copy(vdev->backend_features_array, features);
 
     if (n->mtu_bypass_backend &&
             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
-        features |= (1ULL << VIRTIO_NET_F_MTU);
+        virtio_add_feature_ex(features, VIRTIO_NET_F_MTU);
     }
 
     /*
@@ -819,10 +832,8 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
      * support it.
      */
     if (!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_CTRL_VQ)) {
-        virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ANNOUNCE);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_ANNOUNCE);
     }
-
-    return features;
 }
 
 static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
@@ -855,7 +866,14 @@ static void virtio_net_apply_guest_offloads(VirtIONet *n)
     qemu_set_offload(qemu_get_queue(n->nic)->peer, &ol);
 }
 
-static uint64_t virtio_net_guest_offloads_by_features(uint64_t features)
+static uint64_t virtio_net_features_to_offload(const uint64_t *features)
+{
+    return (features[0] & ~VIRTIO_NET_OFFLOAD_MAP) |
+           ((features[1] << VIRTIO_NET_F2O_SHIFT) & VIRTIO_NET_OFFLOAD_MAP);
+}
+
+static uint64_t
+virtio_net_guest_offloads_by_features(const uint64_t *features)
 {
     static const uint64_t guest_offloads_mask =
         (1ULL << VIRTIO_NET_F_GUEST_CSUM) |
@@ -866,13 +884,13 @@ static uint64_t virtio_net_guest_offloads_by_features(uint64_t features)
         (1ULL << VIRTIO_NET_F_GUEST_USO4) |
         (1ULL << VIRTIO_NET_F_GUEST_USO6);
 
-    return guest_offloads_mask & features;
+    return guest_offloads_mask & virtio_net_features_to_offload(features);
 }
 
 uint64_t virtio_net_supported_guest_offloads(const VirtIONet *n)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(n);
-    return virtio_net_guest_offloads_by_features(vdev->guest_features);
+    return virtio_net_guest_offloads_by_features(vdev->guest_features_array);
 }
 
 typedef struct {
@@ -951,34 +969,39 @@ static void failover_add_primary(VirtIONet *n, Error **errp)
     error_propagate(errp, err);
 }
 
-static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
+static void virtio_net_set_features(VirtIODevice *vdev,
+                                    const uint64_t *in_features)
 {
+    uint64_t features[VIRTIO_FEATURES_DWORDS];
     VirtIONet *n = VIRTIO_NET(vdev);
     Error *err = NULL;
     int i;
 
+    virtio_features_copy(features, in_features);
     if (n->mtu_bypass_backend &&
             !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
-        features &= ~(1ULL << VIRTIO_NET_F_MTU);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_MTU);
     }
 
     virtio_net_set_multiqueue(n,
-                              virtio_has_feature(features, VIRTIO_NET_F_RSS) ||
-                              virtio_has_feature(features, VIRTIO_NET_F_MQ));
+                              virtio_has_feature_ex(features,
+                                                    VIRTIO_NET_F_RSS) ||
+                              virtio_has_feature_ex(features,
+                                                    VIRTIO_NET_F_MQ));
 
     virtio_net_set_mrg_rx_bufs(n,
-                               virtio_has_feature(features,
+                               virtio_has_feature_ex(features,
                                                   VIRTIO_NET_F_MRG_RXBUF),
-                               virtio_has_feature(features,
+                               virtio_has_feature_ex(features,
                                                   VIRTIO_F_VERSION_1),
-                               virtio_has_feature(features,
+                               virtio_has_feature_ex(features,
                                                   VIRTIO_NET_F_HASH_REPORT));
 
-    n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
-        virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
-    n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
-        virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
-    n->rss_data.redirect = virtio_has_feature(features, VIRTIO_NET_F_RSS);
+    n->rsc4_enabled = virtio_has_feature_ex(features, VIRTIO_NET_F_RSC_EXT) &&
+        virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_TSO4);
+    n->rsc6_enabled = virtio_has_feature_ex(features, VIRTIO_NET_F_RSC_EXT) &&
+        virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_TSO6);
+    n->rss_data.redirect = virtio_has_feature_ex(features, VIRTIO_NET_F_RSS);
 
     if (n->has_vnet_hdr) {
         n->curr_guest_offloads =
@@ -992,7 +1015,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
         if (!get_vhost_net(nc->peer)) {
             continue;
         }
-        vhost_net_ack_features(get_vhost_net(nc->peer), features);
+        vhost_net_ack_features_ex(get_vhost_net(nc->peer), features);
 
         /*
          * keep acked_features in NetVhostUserState up-to-date so it
@@ -1001,11 +1024,11 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
         vhost_net_save_acked_features(nc->peer);
     }
 
-    if (!virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
+    if (!virtio_has_feature_ex(features, VIRTIO_NET_F_CTRL_VLAN)) {
         memset(n->vlans, 0xff, MAX_VLAN >> 3);
     }
 
-    if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) {
+    if (virtio_has_feature_ex(features, VIRTIO_NET_F_STANDBY)) {
         qapi_event_send_failover_negotiated(n->netclient_name);
         qatomic_set(&n->failover_primary_hidden, false);
         failover_add_primary(n, &err);
@@ -1966,10 +1989,10 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
                 virtio_error(vdev, "virtio-net unexpected empty queue: "
                              "i %zd mergeable %d offset %zd, size %zd, "
                              "guest hdr len %zd, host hdr len %zd "
-                             "guest features 0x%" PRIx64,
+                             "guest features 0x" VIRTIO_FEATURES_FMT,
                              i, n->mergeable_rx_bufs, offset, size,
                              n->guest_hdr_len, n->host_hdr_len,
-                             vdev->guest_features);
+                             VIRTIO_FEATURES_PR(vdev->guest_features_array));
             }
             err = -1;
             goto err;
@@ -4150,8 +4173,8 @@ static void virtio_net_class_init(ObjectClass *klass, const void *data)
     vdc->unrealize = virtio_net_device_unrealize;
     vdc->get_config = virtio_net_get_config;
     vdc->set_config = virtio_net_set_config;
-    vdc->get_features = virtio_net_get_features;
-    vdc->set_features = virtio_net_set_features;
+    vdc->get_features_ex = virtio_net_get_features;
+    vdc->set_features_ex = virtio_net_set_features;
     vdc->bad_features = virtio_net_bad_features;
     vdc->reset = virtio_net_reset;
     vdc->queue_reset = virtio_net_queue_reset;
include/hw/virtio/virtio-net.h +1 -1
index b9ea9e824e..5168027267 100644
@@ -178,7 +178,7 @@ struct VirtIONet {
     uint32_t has_vnet_hdr;
     size_t host_hdr_len;
     size_t guest_hdr_len;
-    uint64_t host_features;
+    VIRTIO_DECLARE_FEATURES(host_features);
     uint32_t rsc_timeout;
     uint8_t rsc4_enabled;
     uint8_t rsc6_enabled;
Signature · 1 line
2.50.0
author maintainer
#26 lore raw

RFC PATCH v2 12/13 net: implement tunnel probing

In reply to Paolo Abeni

Tap devices support GSO over UDP tunnel offload. Probe for such feature in a similar manner to other offloads.

GSO over UDP tunnel needs to be enabled in addition to a "plain" offload (TSO or USO).

No need to check separately for the outer header checksum offload:
the kernel is going to support both of them or none.

---
v1 -> v2:
- peer_has_tunnel return a bool
- move TUN_F definition in net/tun-linux.h

---
 hw/net/virtio-net.c | 37 +++++++++++++++++++++++++++++++++++++
 include/net/net.h   |  3 +++
 net/net.c           |  9 +++++++++
 net/tap-bsd.c       |  5 +++++
 net/tap-linux.c     | 11 +++++++++++
 net/tap-linux.h     |  9 +++++++++
 net/tap-solaris.c   |  5 +++++
 net/tap-stub.c      |  5 +++++
 net/tap.c           | 11 +++++++++++
 net/tap_int.h       |  1 +
 10 files changed, 96 insertions(+)
hw/net/virtio-net.c +37 -0
index 09d5ef1ece..8ed1cad363 100644
@@ -649,6 +649,15 @@ static int peer_has_uso(VirtIONet *n)
     return qemu_has_uso(qemu_get_queue(n->nic)->peer);
 }
 
+static bool peer_has_tunnel(VirtIONet *n)
+{
+    if (!peer_has_vnet_hdr(n)) {
+        return 0;
+    }
+
+    return qemu_has_tunnel(qemu_get_queue(n->nic)->peer);
+}
+
 static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
                                        int version_1, int hash_report)
 {
@@ -791,6 +800,13 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
         virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO4);
         virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6);
 
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM);
+
         virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT);
     }
 
@@ -805,6 +821,15 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
         virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6);
     }
 
+    if (!peer_has_tunnel(n)) {
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM);
+    }
+
     if (!get_vhost_net(nc->peer)) {
         return;
     }
@@ -4087,6 +4112,10 @@ static const VMStateDescription vmstate_virtio_net = {
     .dev_unplug_pending = dev_unplug_pending,
 };
 
+#define DEFINE_PROP_FEATURE(_name, _state, _field, _bit, _defval)   \
+    DEFINE_PROP_BIT64(_name, _state, _field[VIRTIO_DWORD(_bit)],    \
+                      _bit & 0x3f, _defval)
+
 static const Property virtio_net_properties[] = {
     DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
                     VIRTIO_NET_F_CSUM, true),
@@ -4159,6 +4188,14 @@ static const Property virtio_net_properties[] = {
                       VIRTIO_NET_F_GUEST_USO6, true),
     DEFINE_PROP_BIT64("host_uso", VirtIONet, host_features,
                       VIRTIO_NET_F_HOST_USO, true),
+    DEFINE_PROP_FEATURE("host_tunnel", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, true),
+    DEFINE_PROP_FEATURE("host_tunnel_csum", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, true),
+    DEFINE_PROP_FEATURE("guest_tunnel", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, true),
+    DEFINE_PROP_FEATURE("guest_tunnel_csum", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, true),
 };
 
 static void virtio_net_class_init(ObjectClass *klass, const void *data)
include/net/net.h +3 -0
index 5edea7671a..c71d7c6074 100644
@@ -65,6 +65,7 @@ typedef void (NetClientDestructor)(NetClientState *);
 typedef RxFilterInfo *(QueryRxFilter)(NetClientState *);
 typedef bool (HasUfo)(NetClientState *);
 typedef bool (HasUso)(NetClientState *);
+typedef bool (HasTunnel)(NetClientState *);
 typedef bool (HasVnetHdr)(NetClientState *);
 typedef bool (HasVnetHdrLen)(NetClientState *, int);
 typedef void (SetOffload)(NetClientState *, const NetOffloads *);
@@ -93,6 +94,7 @@ typedef struct NetClientInfo {
     NetPoll *poll;
     HasUfo *has_ufo;
     HasUso *has_uso;
+    HasTunnel *has_tunnel;
     HasVnetHdr *has_vnet_hdr;
     HasVnetHdrLen *has_vnet_hdr_len;
     SetOffload *set_offload;
@@ -193,6 +195,7 @@ void qemu_set_info_str(NetClientState *nc,
 void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]);
 bool qemu_has_ufo(NetClientState *nc);
 bool qemu_has_uso(NetClientState *nc);
+bool qemu_has_tunnel(NetClientState *nc);
 bool qemu_has_vnet_hdr(NetClientState *nc);
 bool qemu_has_vnet_hdr_len(NetClientState *nc, int len);
 void qemu_set_offload(NetClientState *nc, const NetOffloads *ol);
net/net.c +9 -0
index 053db7c314..5a2f00c108 100644
@@ -522,6 +522,15 @@ bool qemu_has_uso(NetClientState *nc)
     return nc->info->has_uso(nc);
 }
 
+bool qemu_has_tunnel(NetClientState *nc)
+{
+    if (!nc || !nc->info->has_tunnel) {
+        return false;
+    }
+
+    return nc->info->has_tunnel(nc);
+}
+
 bool qemu_has_vnet_hdr(NetClientState *nc)
 {
     if (!nc || !nc->info->has_vnet_hdr) {
net/tap-bsd.c +5 -0
index 86b6edee94..e7de0672f4 100644
@@ -217,6 +217,11 @@ int tap_probe_has_uso(int fd)
     return 0;
 }
 
+int tap_probe_has_tunnel(int fd)
+{
+    return 0;
+}
+
 void tap_fd_set_vnet_hdr_len(int fd, int len)
 {
 }
net/tap-linux.c +11 -0
index a1c58f74f5..4ec638add6 100644
@@ -196,6 +196,17 @@ int tap_probe_has_uso(int fd)
     return 1;
 }
 
+int tap_probe_has_tunnel(int fd)
+{
+    unsigned offload;
+
+    offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_UDP_TUNNEL_GSO;
+    if (ioctl(fd, TUNSETOFFLOAD, offload) < 0) {
+        return 0;
+    }
+    return 1;
+}
+
 void tap_fd_set_vnet_hdr_len(int fd, int len)
 {
     if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
net/tap-linux.h +9 -0
index 9a58cecb7f..8cd6b5874b 100644
@@ -53,4 +53,13 @@
 #define TUN_F_USO4    0x20    /* I can handle USO for IPv4 packets */
 #define TUN_F_USO6    0x40    /* I can handle USO for IPv6 packets */
 
+/* I can handle TSO/USO for UDP tunneled packets */
+#define TUN_F_UDP_TUNNEL_GSO       0x080
+
+/*
+ * I can handle TSO/USO for UDP tunneled packets requiring csum offload for
+ * the outer header
+ */
+#define TUN_F_UDP_TUNNEL_GSO_CSUM  0x100
+
 #endif /* QEMU_TAP_LINUX_H */
net/tap-solaris.c +5 -0
index 833c066bee..ac09ae03c0 100644
@@ -222,6 +222,11 @@ int tap_probe_has_uso(int fd)
     return 0;
 }
 
+int tap_probe_has_tunnel(int fd)
+{
+    return 0;
+}
+
 void tap_fd_set_vnet_hdr_len(int fd, int len)
 {
 }
net/tap-stub.c +5 -0
index 67d14ad4d5..66abbbc392 100644
@@ -52,6 +52,11 @@ int tap_probe_has_uso(int fd)
     return 0;
 }
 
+int tap_probe_has_tunnel(int fd)
+{
+    return 0;
+}
+
 void tap_fd_set_vnet_hdr_len(int fd, int len)
 {
 }
net/tap.c +11 -0
index 13e19130ce..c7612fb91b 100644
@@ -58,6 +58,7 @@ typedef struct TAPState {
     bool using_vnet_hdr;
     bool has_ufo;
     bool has_uso;
+    bool has_tunnel;
     bool enabled;
     VHostNetState *vhost_net;
     unsigned host_vnet_hdr_len;
@@ -223,6 +224,14 @@ static bool tap_has_uso(NetClientState *nc)
     return s->has_uso;
 }
 
+static bool tap_has_tunnel(NetClientState *nc)
+{
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
+    return s->has_tunnel;
+}
+
 static bool tap_has_vnet_hdr(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
@@ -339,6 +348,7 @@ static NetClientInfo net_tap_info = {
     .cleanup = tap_cleanup,
     .has_ufo = tap_has_ufo,
     .has_uso = tap_has_uso,
+    .has_tunnel = tap_has_tunnel,
     .has_vnet_hdr = tap_has_vnet_hdr,
     .has_vnet_hdr_len = tap_has_vnet_hdr_len,
     .set_offload = tap_set_offload,
@@ -367,6 +377,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
     s->using_vnet_hdr = false;
     s->has_ufo = tap_probe_has_ufo(s->fd);
     s->has_uso = tap_probe_has_uso(s->fd);
+    s->has_tunnel = tap_probe_has_tunnel(s->fd);
     s->enabled = true;
     tap_set_offload(&s->nc, &ol);
     /*
net/tap_int.h +1 -0
index f8bbe1cb0c..327d10f68b 100644
@@ -38,6 +38,7 @@ void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp);
 int tap_probe_vnet_hdr(int fd, Error **errp);
 int tap_probe_has_ufo(int fd);
 int tap_probe_has_uso(int fd);
+int tap_probe_has_tunnel(int fd);
 void tap_fd_set_offload(int fd, const NetOffloads *ol);
 void tap_fd_set_vnet_hdr_len(int fd, int len);
 int tap_fd_set_vnet_le(int fd, int vnet_is_le);
Signature · 1 line
2.50.0
#27 lore raw

Re: [PATCH RFC v2 12/13] net: implement tunnel probing

Quoted 32 lines · Paolo Abeni

Tap devices support GSO over UDP tunnel offload. Probe for such feature in a similar manner to other offloads.

GSO over UDP tunnel needs to be enabled in addition to a "plain" offload (TSO or USO).

No need to check separately for the outer header checksum offload:
the kernel is going to support both of them or none.

---
v1 -> v2:
- peer_has_tunnel return a bool
- move TUN_F definition in net/tun-linux.h

---
  hw/net/virtio-net.c | 37 +++++++++++++++++++++++++++++++++++++
  include/net/net.h   |  3 +++
  net/net.c           |  9 +++++++++
  net/tap-bsd.c       |  5 +++++
  net/tap-linux.c     | 11 +++++++++++
  net/tap-linux.h     |  9 +++++++++
  net/tap-solaris.c   |  5 +++++
  net/tap-stub.c      |  5 +++++
  net/tap.c           | 11 +++++++++++
  net/tap_int.h       |  1 +
  10 files changed, 96 insertions(+)
hw/net/virtio-net.c +0 -0
index 09d5ef1ece..8ed1cad363 100644

hw/net/virtio-net.c static int peer_has_uso(VirtIONet *n) · line 655

Quoted 7 lines
hw/net/virtio-net.c +4 -0
@@ -649,6 +649,15 @@ static int peer_has_uso(VirtIONet *n)
      return qemu_has_uso(qemu_get_queue(n->nic)->peer);
  }
 
+static bool peer_has_tunnel(VirtIONet *n)
+{
+    if (!peer_has_vnet_hdr(n)) {
+        return 0;

Please return "false" for type consistency.

Quoted 117 lines
hw/net/virtio-net.c +33 -0
+    }
+
+    return qemu_has_tunnel(qemu_get_queue(n->nic)->peer);
+}
+
  static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
                                         int version_1, int hash_report)
  {
@@ -791,6 +800,13 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
          virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO4);
          virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6);
 
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM);
+
          virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT);
      }
 
@@ -805,6 +821,15 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
          virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6);
      }
 
+    if (!peer_has_tunnel(n)) {
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM);
+        virtio_clear_feature_ex(features,
+                                VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM);
+    }
+
      if (!get_vhost_net(nc->peer)) {
          return;
      }
@@ -4087,6 +4112,10 @@ static const VMStateDescription vmstate_virtio_net = {
      .dev_unplug_pending = dev_unplug_pending,
  };
 
+#define DEFINE_PROP_FEATURE(_name, _state, _field, _bit, _defval)   \
+    DEFINE_PROP_BIT64(_name, _state, _field[VIRTIO_DWORD(_bit)],    \
+                      _bit & 0x3f, _defval)
+
  static const Property virtio_net_properties[] = {
      DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
                      VIRTIO_NET_F_CSUM, true),
@@ -4159,6 +4188,14 @@ static const Property virtio_net_properties[] = {
                        VIRTIO_NET_F_GUEST_USO6, true),
      DEFINE_PROP_BIT64("host_uso", VirtIONet, host_features,
                        VIRTIO_NET_F_HOST_USO, true),
+    DEFINE_PROP_FEATURE("host_tunnel", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, true),
+    DEFINE_PROP_FEATURE("host_tunnel_csum", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, true),
+    DEFINE_PROP_FEATURE("guest_tunnel", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, true),
+    DEFINE_PROP_FEATURE("guest_tunnel_csum", VirtIONet, host_features_array,
+                        VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, true),
  };
 
  static void virtio_net_class_init(ObjectClass *klass, const void *data)
include/net/net.h +3 -0
index 5edea7671a..c71d7c6074 100644
@@ -65,6 +65,7 @@ typedef void (NetClientDestructor)(NetClientState *);
  typedef RxFilterInfo *(QueryRxFilter)(NetClientState *);
  typedef bool (HasUfo)(NetClientState *);
  typedef bool (HasUso)(NetClientState *);
+typedef bool (HasTunnel)(NetClientState *);
  typedef bool (HasVnetHdr)(NetClientState *);
  typedef bool (HasVnetHdrLen)(NetClientState *, int);
  typedef void (SetOffload)(NetClientState *, const NetOffloads *);
@@ -93,6 +94,7 @@ typedef struct NetClientInfo {
      NetPoll *poll;
      HasUfo *has_ufo;
      HasUso *has_uso;
+    HasTunnel *has_tunnel;
      HasVnetHdr *has_vnet_hdr;
      HasVnetHdrLen *has_vnet_hdr_len;
      SetOffload *set_offload;
@@ -193,6 +195,7 @@ void qemu_set_info_str(NetClientState *nc,
  void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]);
  bool qemu_has_ufo(NetClientState *nc);
  bool qemu_has_uso(NetClientState *nc);
+bool qemu_has_tunnel(NetClientState *nc);
  bool qemu_has_vnet_hdr(NetClientState *nc);
  bool qemu_has_vnet_hdr_len(NetClientState *nc, int len);
  void qemu_set_offload(NetClientState *nc, const NetOffloads *ol);
net/net.c +9 -0
index 053db7c314..5a2f00c108 100644
@@ -522,6 +522,15 @@ bool qemu_has_uso(NetClientState *nc)
      return nc->info->has_uso(nc);
  }
 
+bool qemu_has_tunnel(NetClientState *nc)
+{
+    if (!nc || !nc->info->has_tunnel) {
+        return false;
+    }
+
+    return nc->info->has_tunnel(nc);
+}
+
  bool qemu_has_vnet_hdr(NetClientState *nc)
  {
      if (!nc || !nc->info->has_vnet_hdr) {
net/tap-bsd.c +0 -0
index 86b6edee94..e7de0672f4 100644

net/tap-bsd.c int tap_probe_has_uso(int fd) · line 220

Quoted 4 lines
net/tap-bsd.c +1 -0
@@ -217,6 +217,11 @@ int tap_probe_has_uso(int fd)
      return 0;
  }
 
+int tap_probe_has_tunnel(int fd)

This should return bool for consistency.

Quoted 134 lines
net/tap-bsd.c +4 -0
+{
+    return 0;
+}
+
  void tap_fd_set_vnet_hdr_len(int fd, int len)
  {
  }
net/tap-linux.c +11 -0
index a1c58f74f5..4ec638add6 100644
@@ -196,6 +196,17 @@ int tap_probe_has_uso(int fd)
      return 1;
  }
 
+int tap_probe_has_tunnel(int fd)
+{
+    unsigned offload;
+
+    offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_UDP_TUNNEL_GSO;
+    if (ioctl(fd, TUNSETOFFLOAD, offload) < 0) {
+        return 0;
+    }
+    return 1;
+}
+
  void tap_fd_set_vnet_hdr_len(int fd, int len)
  {
      if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
net/tap-linux.h +9 -0
index 9a58cecb7f..8cd6b5874b 100644
@@ -53,4 +53,13 @@
  #define TUN_F_USO4    0x20    /* I can handle USO for IPv4 packets */
  #define TUN_F_USO6    0x40    /* I can handle USO for IPv6 packets */
 
+/* I can handle TSO/USO for UDP tunneled packets */
+#define TUN_F_UDP_TUNNEL_GSO       0x080
+
+/*
+ * I can handle TSO/USO for UDP tunneled packets requiring csum offload for
+ * the outer header
+ */
+#define TUN_F_UDP_TUNNEL_GSO_CSUM  0x100
+
  #endif /* QEMU_TAP_LINUX_H */
net/tap-solaris.c +5 -0
index 833c066bee..ac09ae03c0 100644
@@ -222,6 +222,11 @@ int tap_probe_has_uso(int fd)
      return 0;
  }
 
+int tap_probe_has_tunnel(int fd)
+{
+    return 0;
+}
+
  void tap_fd_set_vnet_hdr_len(int fd, int len)
  {
  }
net/tap-stub.c +5 -0
index 67d14ad4d5..66abbbc392 100644
@@ -52,6 +52,11 @@ int tap_probe_has_uso(int fd)
      return 0;
  }
 
+int tap_probe_has_tunnel(int fd)
+{
+    return 0;
+}
+
  void tap_fd_set_vnet_hdr_len(int fd, int len)
  {
  }
net/tap.c +11 -0
index 13e19130ce..c7612fb91b 100644
@@ -58,6 +58,7 @@ typedef struct TAPState {
      bool using_vnet_hdr;
      bool has_ufo;
      bool has_uso;
+    bool has_tunnel;
      bool enabled;
      VHostNetState *vhost_net;
      unsigned host_vnet_hdr_len;
@@ -223,6 +224,14 @@ static bool tap_has_uso(NetClientState *nc)
      return s->has_uso;
  }
 
+static bool tap_has_tunnel(NetClientState *nc)
+{
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
+    return s->has_tunnel;
+}
+
  static bool tap_has_vnet_hdr(NetClientState *nc)
  {
      TAPState *s = DO_UPCAST(TAPState, nc, nc);
@@ -339,6 +348,7 @@ static NetClientInfo net_tap_info = {
      .cleanup = tap_cleanup,
      .has_ufo = tap_has_ufo,
      .has_uso = tap_has_uso,
+    .has_tunnel = tap_has_tunnel,
      .has_vnet_hdr = tap_has_vnet_hdr,
      .has_vnet_hdr_len = tap_has_vnet_hdr_len,
      .set_offload = tap_set_offload,
@@ -367,6 +377,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
      s->using_vnet_hdr = false;
      s->has_ufo = tap_probe_has_ufo(s->fd);
      s->has_uso = tap_probe_has_uso(s->fd);
+    s->has_tunnel = tap_probe_has_tunnel(s->fd);
      s->enabled = true;
      tap_set_offload(&s->nc, &ol);
      /*
net/tap_int.h +1 -0
index f8bbe1cb0c..327d10f68b 100644
@@ -38,6 +38,7 @@ void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp);
  int tap_probe_vnet_hdr(int fd, Error **errp);
  int tap_probe_has_ufo(int fd);
  int tap_probe_has_uso(int fd);
+int tap_probe_has_tunnel(int fd);
  void tap_fd_set_offload(int fd, const NetOffloads *ol);
  void tap_fd_set_vnet_hdr_len(int fd, int len);
  int tap_fd_set_vnet_le(int fd, int vnet_is_le);
author maintainer
#28 lore raw

Re: [PATCH RFC v2 12/13] net: implement tunnel probing

Quoted 9 lines · Akihiko Odaki
Quoted 8 lines · Paolo Abeni
net/tap-bsd.c +0 -0
index 86b6edee94..e7de0672f4 100644
@@ -217,6 +217,11 @@ int tap_probe_has_uso(int fd)
      return 0;
  }
 
Quoted 3 lines

net/tap-bsd.c int tap_probe_has_uso(int fd) · line 220

Quoted 1 line
net/tap-bsd.c +1 -0
+int tap_probe_has_tunnel(int fd)

This should return bool for consistency.

Some inconsistency will persist, as others bsd helpers supposed to return a bool currently return an int. I tried to be consistent with the surrounding code, but no strong objections.

/P

#29 lore raw

Re: [PATCH RFC v2 12/13] net: implement tunnel probing

Quoted 10 lines · Paolo Abeni
Quoted 9 lines · Akihiko Odaki
Quoted 8 lines · Paolo Abeni
net/tap-bsd.c +0 -0
index 86b6edee94..e7de0672f4 100644
@@ -217,6 +217,11 @@ int tap_probe_has_uso(int fd)
       return 0;
   }
 
Quoted 7 lines
Quoted 3 lines

net/tap-bsd.c int tap_probe_has_uso(int fd) · line 220

Quoted 1 line
net/tap-bsd.c +1 -0
+int tap_probe_has_tunnel(int fd)

This should return bool for consistency.

Some inconsistency will persist, as others bsd helpers supposed to return a bool currently return an int. I tried to be consistent with the surrounding code, but no strong objections.

That's true, but when considering the entire codebase, it is so common to return bool instead of int. So I regard the existing tap-local functions as exceptional, and making a new function return bool results in a better global consistency.

Regards,
Akihiko Odaki

author maintainer
#30 lore raw

RFC PATCH v2 13/13 net: implement UDP tunnel features offloading

In reply to Paolo Abeni

When any host or guest GSO over UDP tunnel offload is enabled the virtio net header includes the additional tunnel-related fields, update the size accordingly.

Push the GSO over UDP tunnel offloads all the way down to the tap device extending the newly introduced NetFeatures struct, and eventually enable the associated features.

As per virtio specification, to convert features bit to offload bit, map the extended features into the reserved range.

Finally, make the vhost backend aware of the exact header layout, to copy it correctly. The tunnel-related field are present if either the guest or the host negotiated any UDP tunnel related feature:
add them to host kernel supported features list, to allow qemu transfer to such backend the needed information.

---
v1 -> v2:
- squashed vhost support into this patch
- dropped tun offload consistency checks; they are implemented in the kernel side
- virtio_has_tnl_hdr ->virtio_has_tunnel_hdr

---
 hw/net/vhost_net.c  |  2 ++
 hw/net/virtio-net.c | 34 ++++++++++++++++++++++++++--------
 include/net/net.h   |  2 ++
 net/net.c           |  3 ++-
 net/tap-linux.c     |  6 ++++++
 5 files changed, 38 insertions(+), 9 deletions(-)
hw/net/vhost_net.c +2 -0
index bae595607a..a15dc51d84 100644
@@ -52,6 +52,8 @@ static const int kernel_feature_bits[] = {
     VIRTIO_F_NOTIFICATION_DATA,
     VIRTIO_NET_F_RSC_EXT,
     VIRTIO_NET_F_HASH_REPORT,
+    VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO,
+    VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO,
     VHOST_INVALID_FEATURE_BIT
 };
 
hw/net/virtio-net.c +26 -8
index 8ed1cad363..ff2f34d98a 100644
@@ -103,6 +103,12 @@
 #define VIRTIO_NET_F2O_SHIFT          (VIRTIO_NET_OFFLOAD_MAP_MIN - \
                                        VIRTIO_NET_FEATURES_MAP_MIN + 64)
 
+static bool virtio_has_tunnel_hdr(const uint64_t *features)
+{
+    return virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO) |
+           virtio_has_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO);
+}
+
 static const VirtIOFeature feature_sizes[] = {
     {.flags = 1ULL << VIRTIO_NET_F_MAC,
      .end = endof(struct virtio_net_config, mac)},
@@ -659,7 +665,8 @@ static bool peer_has_tunnel(VirtIONet *n)
 }
 
 static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
-                                       int version_1, int hash_report)
+                                       int version_1, int hash_report,
+                                       int tunnel)
 {
     int i;
     NetClientState *nc;
@@ -667,9 +674,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
     n->mergeable_rx_bufs = mergeable_rx_bufs;
 
     if (version_1) {
-        n->guest_hdr_len = hash_report ?
-            sizeof(struct virtio_net_hdr_v1_hash) :
-            sizeof(struct virtio_net_hdr_mrg_rxbuf);
+        n->guest_hdr_len = tunnel ?
+            sizeof(struct virtio_net_hdr_v1_hash_tunnel) :
+            (hash_report ?
+             sizeof(struct virtio_net_hdr_v1_hash) :
+             sizeof(struct virtio_net_hdr_mrg_rxbuf));
         n->rss_data.populate_hash = !!hash_report;
     } else {
         n->guest_hdr_len = n->mergeable_rx_bufs ?
@@ -886,6 +895,10 @@ static void virtio_net_apply_guest_offloads(VirtIONet *n)
        .ufo  = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)),
        .uso4 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)),
        .uso6 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6)),
+       .tnl  = !!(n->curr_guest_offloads &
+                  (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED)),
+       .tnl_csum = !!(n->curr_guest_offloads &
+                      (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED)),
     };
 
     qemu_set_offload(qemu_get_queue(n->nic)->peer, &ol);
@@ -907,7 +920,9 @@ virtio_net_guest_offloads_by_features(const uint64_t *features)
         (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
         (1ULL << VIRTIO_NET_F_GUEST_UFO)  |
         (1ULL << VIRTIO_NET_F_GUEST_USO4) |
-        (1ULL << VIRTIO_NET_F_GUEST_USO6);
+        (1ULL << VIRTIO_NET_F_GUEST_USO6) |
+        (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED) |
+        (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED);
 
     return guest_offloads_mask & virtio_net_features_to_offload(features);
 }
@@ -1020,7 +1035,8 @@ static void virtio_net_set_features(VirtIODevice *vdev,
                                virtio_has_feature_ex(features,
                                                   VIRTIO_F_VERSION_1),
                                virtio_has_feature_ex(features,
-                                                  VIRTIO_NET_F_HASH_REPORT));
+                                                  VIRTIO_NET_F_HASH_REPORT),
+                               virtio_has_tunnel_hdr(features));
 
     n->rsc4_enabled = virtio_has_feature_ex(features, VIRTIO_NET_F_RSC_EXT) &&
         virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_TSO4);
@@ -3132,13 +3148,15 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
     VirtIONet *n = opaque;
     VirtIODevice *vdev = VIRTIO_DEVICE(n);
     int i, link_down;
+    bool has_tunnel_hdr = virtio_has_tunnel_hdr(vdev->guest_features_array);
 
     trace_virtio_net_post_load_device();
     virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
                                virtio_vdev_has_feature(vdev,
                                                        VIRTIO_F_VERSION_1),
                                virtio_vdev_has_feature(vdev,
-                                                       VIRTIO_NET_F_HASH_REPORT));
+                                                      VIRTIO_NET_F_HASH_REPORT),
+                               has_tunnel_hdr);
 
     /* MAC_TABLE_ENTRIES may be different from the saved image */
     if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
@@ -3945,7 +3963,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
 
     n->vqs[0].tx_waiting = 0;
     n->tx_burst = n->net_conf.txburst;
-    virtio_net_set_mrg_rx_bufs(n, 0, 0, 0);
+    virtio_net_set_mrg_rx_bufs(n, 0, 0, 0, 0);
     n->promisc = 1; /* for compatibility */
 
     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
include/net/net.h +2 -0
index c71d7c6074..5049d293f2 100644
@@ -43,6 +43,8 @@ typedef struct NetOffloads {
     bool ufo;
     bool uso4;
     bool uso6;
+    bool tnl;
+    bool tnl_csum;
 } NetOffloads;
 
 #define DEFINE_NIC_PROPERTIES(_state, _conf)                            \
net/net.c +2 -1
index 5a2f00c108..eb98c8a8b9 100644
@@ -575,7 +575,8 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len)
 
     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
            len == sizeof(struct virtio_net_hdr) ||
-           len == sizeof(struct virtio_net_hdr_v1_hash));
+           len == sizeof(struct virtio_net_hdr_v1_hash) ||
+           len == sizeof(struct virtio_net_hdr_v1_hash_tunnel));
 
     nc->vnet_hdr_len = len;
     nc->info->set_vnet_hdr_len(nc, len);
net/tap-linux.c +6 -0
index 4ec638add6..5e6c2d3cbd 100644
@@ -279,6 +279,12 @@ void tap_fd_set_offload(int fd, const NetOffloads *ol)
         if (ol->uso6) {
             offload |= TUN_F_USO6;
         }
+        if (ol->tnl) {
+            offload |= TUN_F_UDP_TUNNEL_GSO;
+        }
+        if (ol->tnl_csum) {
+            offload |= TUN_F_UDP_TUNNEL_GSO_CSUM;
+        }
     }
 
     if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
Signature · 1 line
2.50.0
#31 lore raw

Re: [PATCH RFC v2 13/13] net: implement UDP tunnel features offloading

Quoted 8 lines · Paolo Abeni

When any host or guest GSO over UDP tunnel offload is enabled the virtio net header includes the additional tunnel-related fields, update the size accordingly.

Push the GSO over UDP tunnel offloads all the way down to the tap device extending the newly introduced NetFeatures struct, and eventually enable the associated features.

Quoted 8 lines

As per virtio specification, to convert features bit to offload bit, map the extended features into the reserved range.

Finally, make the vhost backend aware of the exact header layout, to copy it correctly. The tunnel-related field are present if either the guest or the host negotiated any UDP tunnel related feature:
add them to host kernel supported features list, to allow qemu transfer to such backend the needed information.

Please also update: hw/virtio/virtio-qmp.c

Quoted 173 lines

---
v1 -> v2:
- squashed vhost support into this patch
- dropped tun offload consistency checks; they are implemented in the kernel side
- virtio_has_tnl_hdr ->virtio_has_tunnel_hdr

---
  hw/net/vhost_net.c  |  2 ++
  hw/net/virtio-net.c | 34 ++++++++++++++++++++++++++--------
  include/net/net.h   |  2 ++
  net/net.c           |  3 ++-
  net/tap-linux.c     |  6 ++++++
  5 files changed, 38 insertions(+), 9 deletions(-)
hw/net/vhost_net.c +2 -0
index bae595607a..a15dc51d84 100644
@@ -52,6 +52,8 @@ static const int kernel_feature_bits[] = {
      VIRTIO_F_NOTIFICATION_DATA,
      VIRTIO_NET_F_RSC_EXT,
      VIRTIO_NET_F_HASH_REPORT,
+    VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO,
+    VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO,
      VHOST_INVALID_FEATURE_BIT
  };
 
hw/net/virtio-net.c +26 -8
index 8ed1cad363..ff2f34d98a 100644
@@ -103,6 +103,12 @@
  #define VIRTIO_NET_F2O_SHIFT          (VIRTIO_NET_OFFLOAD_MAP_MIN - \
                                         VIRTIO_NET_FEATURES_MAP_MIN + 64)
 
+static bool virtio_has_tunnel_hdr(const uint64_t *features)
+{
+    return virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO) |
+           virtio_has_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO);
+}
+
  static const VirtIOFeature feature_sizes[] = {
      {.flags = 1ULL << VIRTIO_NET_F_MAC,
       .end = endof(struct virtio_net_config, mac)},
@@ -659,7 +665,8 @@ static bool peer_has_tunnel(VirtIONet *n)
  }
 
  static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
-                                       int version_1, int hash_report)
+                                       int version_1, int hash_report,
+                                       int tunnel)
  {
      int i;
      NetClientState *nc;
@@ -667,9 +674,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
      n->mergeable_rx_bufs = mergeable_rx_bufs;
 
      if (version_1) {
-        n->guest_hdr_len = hash_report ?
-            sizeof(struct virtio_net_hdr_v1_hash) :
-            sizeof(struct virtio_net_hdr_mrg_rxbuf);
+        n->guest_hdr_len = tunnel ?
+            sizeof(struct virtio_net_hdr_v1_hash_tunnel) :
+            (hash_report ?
+             sizeof(struct virtio_net_hdr_v1_hash) :
+             sizeof(struct virtio_net_hdr_mrg_rxbuf));
          n->rss_data.populate_hash = !!hash_report;
      } else {
          n->guest_hdr_len = n->mergeable_rx_bufs ?
@@ -886,6 +895,10 @@ static void virtio_net_apply_guest_offloads(VirtIONet *n)
         .ufo  = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)),
         .uso4 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)),
         .uso6 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6)),
+       .tnl  = !!(n->curr_guest_offloads &
+                  (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED)),
+       .tnl_csum = !!(n->curr_guest_offloads &
+                      (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED)),
      };
 
      qemu_set_offload(qemu_get_queue(n->nic)->peer, &ol);
@@ -907,7 +920,9 @@ virtio_net_guest_offloads_by_features(const uint64_t *features)
          (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
          (1ULL << VIRTIO_NET_F_GUEST_UFO)  |
          (1ULL << VIRTIO_NET_F_GUEST_USO4) |
-        (1ULL << VIRTIO_NET_F_GUEST_USO6);
+        (1ULL << VIRTIO_NET_F_GUEST_USO6) |
+        (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED) |
+        (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED);
 
      return guest_offloads_mask & virtio_net_features_to_offload(features);
  }
@@ -1020,7 +1035,8 @@ static void virtio_net_set_features(VirtIODevice *vdev,
                                 virtio_has_feature_ex(features,
                                                    VIRTIO_F_VERSION_1),
                                 virtio_has_feature_ex(features,
-                                                  VIRTIO_NET_F_HASH_REPORT));
+                                                  VIRTIO_NET_F_HASH_REPORT),
+                               virtio_has_tunnel_hdr(features));
 
      n->rsc4_enabled = virtio_has_feature_ex(features, VIRTIO_NET_F_RSC_EXT) &&
          virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_TSO4);
@@ -3132,13 +3148,15 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
      VirtIONet *n = opaque;
      VirtIODevice *vdev = VIRTIO_DEVICE(n);
      int i, link_down;
+    bool has_tunnel_hdr = virtio_has_tunnel_hdr(vdev->guest_features_array);
 
      trace_virtio_net_post_load_device();
      virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
                                 virtio_vdev_has_feature(vdev,
                                                         VIRTIO_F_VERSION_1),
                                 virtio_vdev_has_feature(vdev,
-                                                       VIRTIO_NET_F_HASH_REPORT));
+                                                      VIRTIO_NET_F_HASH_REPORT),
+                               has_tunnel_hdr);
 
      /* MAC_TABLE_ENTRIES may be different from the saved image */
      if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
@@ -3945,7 +3963,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
 
      n->vqs[0].tx_waiting = 0;
      n->tx_burst = n->net_conf.txburst;
-    virtio_net_set_mrg_rx_bufs(n, 0, 0, 0);
+    virtio_net_set_mrg_rx_bufs(n, 0, 0, 0, 0);
      n->promisc = 1; /* for compatibility */
 
      n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
include/net/net.h +2 -0
index c71d7c6074..5049d293f2 100644
@@ -43,6 +43,8 @@ typedef struct NetOffloads {
      bool ufo;
      bool uso4;
      bool uso6;
+    bool tnl;
+    bool tnl_csum;
  } NetOffloads;
 
  #define DEFINE_NIC_PROPERTIES(_state, _conf)                            \
net/net.c +2 -1
index 5a2f00c108..eb98c8a8b9 100644
@@ -575,7 +575,8 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len)
 
      assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
             len == sizeof(struct virtio_net_hdr) ||
-           len == sizeof(struct virtio_net_hdr_v1_hash));
+           len == sizeof(struct virtio_net_hdr_v1_hash) ||
+           len == sizeof(struct virtio_net_hdr_v1_hash_tunnel));
 
      nc->vnet_hdr_len = len;
      nc->info->set_vnet_hdr_len(nc, len);
net/tap-linux.c +6 -0
index 4ec638add6..5e6c2d3cbd 100644
@@ -279,6 +279,12 @@ void tap_fd_set_offload(int fd, const NetOffloads *ol)
          if (ol->uso6) {
              offload |= TUN_F_USO6;
          }
+        if (ol->tnl) {
+            offload |= TUN_F_UDP_TUNNEL_GSO;
+        }
+        if (ol->tnl_csum) {
+            offload |= TUN_F_UDP_TUNNEL_GSO_CSUM;
+        }
      }
 
      if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
author maintainer
#32 lore raw

Re: [PATCH RFC v2 13/13] net: implement UDP tunnel features offloading

Quoted 12 lines · Akihiko Odaki
Quoted 11 lines · Paolo Abeni

When any host or guest GSO over UDP tunnel offload is enabled the virtio net header includes the additional tunnel-related fields, update the size accordingly.

Push the GSO over UDP tunnel offloads all the way down to the tap device extending the newly introduced NetFeatures struct, and eventually enable the associated features.

As per virtio specification, to convert features bit to offload bit, map the extended features into the reserved range.

Quoted 7 lines
Quoted 5 lines

Finally, make the vhost backend aware of the exact header layout, to copy it correctly. The tunnel-related field are present if either the guest or the host negotiated any UDP tunnel related feature:
add them to host kernel supported features list, to allow qemu transfer to such backend the needed information.

Please also update: hw/virtio/virtio-qmp.c

Do you mean by adding FEATURE_ENTRY() for
{GUEST,HOST}_UDP_TUNNEL_GSO{,_CSUM} ?

Such entries are added in patch 8/13 "qmp: update virtio features map to support extended features". Even if the features are not supported yet in such patch, I think the code could already parse their name.

Do you prefer I move the features entry definition here?

Thanks,

Paolo

#33 lore raw

Re: [PATCH RFC v2 13/13] net: implement UDP tunnel features offloading

Quoted 19 lines · Paolo Abeni
Quoted 18 lines · Akihiko Odaki
Quoted 16 lines · Paolo Abeni

When any host or guest GSO over UDP tunnel offload is enabled the virtio net header includes the additional tunnel-related fields, update the size accordingly.

Push the GSO over UDP tunnel offloads all the way down to the tap device extending the newly introduced NetFeatures struct, and eventually enable the associated features.

As per virtio specification, to convert features bit to offload bit, map the extended features into the reserved range.

Finally, make the vhost backend aware of the exact header layout, to copy it correctly. The tunnel-related field are present if either the guest or the host negotiated any UDP tunnel related feature:
add them to host kernel supported features list, to allow qemu transfer to such backend the needed information.

Quoted 4 lines
Quoted 1 line

Please also update: hw/virtio/virtio-qmp.c

Do you mean by adding FEATURE_ENTRY() for
{GUEST,HOST}_UDP_TUNNEL_GSO{,_CSUM} ?

Yes.

Quoted 6 lines

Such entries are added in patch 8/13 "qmp: update virtio features map to support extended features". Even if the features are not supported yet in such patch, I think the code could already parse their name.

Do you prefer I move the features entry definition here?

No, I missed what patch 8/13 did. There is no change needed here.

Regards,
Akihiko Odaki

#34 lore raw
In reply to Paolo Abeni

Hi Paolo

Does the compile of this series of patches require support for a special kernel environment? I hit a compile issue after applied you patches:
[1440/2928] Compiling C object libsystem.a.p/hw_virtio_vhost.c.o
FAILED: libsystem.a.p/hw_virtio_vhost.c.o
cc -m64 -Ilibsystem.a.p -I. -I.. -Isubprojects/dtc/libfdt -I../subprojects/dtc/libfdt -Isubprojects/libvduse -I../subprojects/libvduse -Iui -Iqapi -Itrace -Iui/shader -I/usr/include/pixman-1 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/sysprof-6 -I/usr/include/gio-unix-2.0 -I/usr/include/slirp -fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O0 -g -fstack-protector-strong -Wempty-body -Wendif-labels -Wexpansion-to-defined -Wformat-security -Wformat-y2k -Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self -Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla -Wwrite-strings -Wno-missing-include-dirs -Wno-psabi -Wno-shift-negative-value -isystem /mnt/tests/distribution/command/qemu/linux-headers -isystem linux-headers -iquote . -iquote /mnt/tests/distribution/command/qemu -iquote /mnt/tests/distribution/command/qemu/include -iquote /mnt/tests/distribution/command/qemu/host/include/x86_64 -iquote /mnt/tests/distribution/command/qemu/host/include/generic -iquote /mnt/tests/distribution/command/qemu/tcg/i386 -pthread -mcx16 -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -ftrivial-auto-var-init=zero -fzero-call-used-regs=used-gpr -fPIE -DWITH_GZFILEOP -DCONFIG_SOFTMMU -DCOMPILING_SYSTEM_VS_USER -MD -MQ libsystem.a.p/hw_virtio_vhost.c.o -MF libsystem.a.p/hw_virtio_vhost.c.o.d -o libsystem.a.p/hw_virtio_vhost.c.o -c ../hw/virtio/vhost.c ../hw/virtio/vhost.c: In function ‘vhost_dev_set_features’:
../hw/virtio/vhost.c:38:9: error: ‘r’ may be used uninitialized [-Werror=maybe-uninitialized]

   38 |         error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   39 |                      strerror(-retval), -retval); \
      |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/vhost.c:1006:9: note: in expansion of macro ‘VHOST_OPS_DEBUG’
 1006 |         VHOST_OPS_DEBUG(r, "extended features without device support");
      |         ^~~~~~~~~~~~~~~

../hw/virtio/vhost.c:989:9: note: ‘r’ was declared here
989 | int r;
| ^
cc1: all warnings being treated as errors
ninja: build stopped: subcommand failed.
make[1]: *** [Makefile:168: run-ninja] Error 1
make[1]: Leaving directory '/mnt/tests/distribution/command/qemu/build' make[1]: Entering directory '/mnt/tests/distribution/command/qemu/build' [1/1493] Generating subprojects/dtc/version_gen.h with a custom command [2/1493] Generating qemu-version.h with a custom command (wrapped by meson to capture output)
[3/1492] Compiling C object libsystem.a.p/hw_virtio_vhost.c.o
FAILED: libsystem.a.p/hw_virtio_vhost.c.o
cc -m64 -Ilibsystem.a.p -I. -I.. -Isubprojects/dtc/libfdt -I../subprojects/dtc/libfdt -Isubprojects/libvduse -I../subprojects/libvduse -Iui -Iqapi -Itrace -Iui/shader -I/usr/include/pixman-1 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/sysprof-6 -I/usr/include/gio-unix-2.0 -I/usr/include/slirp -fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O0 -g -fstack-protector-strong -Wempty-body -Wendif-labels -Wexpansion-to-defined -Wformat-security -Wformat-y2k -Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self -Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla -Wwrite-strings -Wno-missing-include-dirs -Wno-psabi -Wno-shift-negative-value -isystem /mnt/tests/distribution/command/qemu/linux-headers -isystem linux-headers -iquote . -iquote /mnt/tests/distribution/command/qemu -iquote /mnt/tests/distribution/command/qemu/include -iquote /mnt/tests/distribution/command/qemu/host/include/x86_64 -iquote /mnt/tests/distribution/command/qemu/host/include/generic -iquote /mnt/tests/distribution/command/qemu/tcg/i386 -pthread -mcx16 -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -ftrivial-auto-var-init=zero -fzero-call-used-regs=used-gpr -fPIE -DWITH_GZFILEOP -DCONFIG_SOFTMMU -DCOMPILING_SYSTEM_VS_USER -MD -MQ libsystem.a.p/hw_virtio_vhost.c.o -MF libsystem.a.p/hw_virtio_vhost.c.o.d -o libsystem.a.p/hw_virtio_vhost.c.o -c ../hw/virtio/vhost.c ../hw/virtio/vhost.c: In function ‘vhost_dev_set_features’:
../hw/virtio/vhost.c:38:9: error: ‘r’ may be used uninitialized [-Werror=maybe-uninitialized]

   38 |         error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   39 |                      strerror(-retval), -retval); \
      |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/vhost.c:1006:9: note: in expansion of macro ‘VHOST_OPS_DEBUG’
 1006 |         VHOST_OPS_DEBUG(r, "extended features without device support");
      |         ^~~~~~~~~~~~~~~

../hw/virtio/vhost.c:989:9: note: ‘r’ was declared here
989 | int r;
| ^
cc1: all warnings being treated as errors
ninja: build stopped: subcommand failed.
make[1]: *** [Makefile:168: run-ninja] Error 1
make[1]: Leaving directory '/mnt/tests/distribution/command/qemu/build'

Thanks
Lei

Quoted 99 lines · Paolo Abeni

Some virtualized deployments use UDP tunnel pervasively and are impacted negatively by the lack of GSO support for such kind of traffic in the virtual NIC driver.

The virtio_net specification recently introduced support for GSO over UDP tunnel, and the kernel side of the implementation has been merged into the net-next tree; this series updates the virtio implementation to support such a feature.

Currently the qemu virtio support limits the feature space to 64 bits, while the virtio specification allows for a larger number of features. Specifically the GSO-over-UDP-tunnel-related virtio features use bits 65-69; the larger part of this series (patches 3-11) actually deals with extending the features space.

The extended features are carried by fixed size uint64_t arrays, bringing the current maximum features number to 128.

The patches use some syntactic sugar to try to minimize the otherwise very large code churn. Specifically the extended features are boundled in an union with 'legacy' features definition, allowing no changes in the virtio devices not needing the extended features set.

The actual offload implementation is in patches 12 and 13 and boils down to propagating the new offload to the tun devices and the vhost backend.

Finally patch 1 is a small pre-req refactor that ideally could enter the tree separately; it's presented here in the same series to help reviewers more easily getting the full picture and patch 2 is a needed linux headers update.

Tested with basic stream transfer with all the possible permutations of host kernel/qemu/guest kernel with/without GSO over UDP tunnel support, vs snapshots creation and restore and vs migration.

Sharing again as RFC as the kernel bits have not entered the Linus tree yet - but they should on next merge window.

Paolo Abeni (13):
net: bundle all offloads in a single struct
linux-headers: Update to Linux ~v6.16-rc5 net-next
virtio: introduce extended features type
virtio: serialize extended features state
virtio: add support for negotiating extended features
virtio-pci: implement support for extended features
vhost: add support for negotiating extended features
qmp: update virtio features map to support extended features
vhost-backend: implement extended features support
vhost-net: implement extended features support
virtio-net: implement extended features support
net: implement tunnel probing
net: implement UDP tunnel features offloading

 hw/net/e1000e_core.c                         |   5 +-
 hw/net/igb_core.c                            |   5 +-
 hw/net/vhost_net-stub.c                      |   8 +-
 hw/net/vhost_net.c                           |  50 +++--
 hw/net/virtio-net.c                          | 215 +++++++++++++------
 hw/net/vmxnet3.c                             |  13 +-
 hw/virtio/vhost-backend.c                    |  62 +++++-
 hw/virtio/vhost.c                            |  73 ++++++-
 hw/virtio/virtio-bus.c                       |  11 +-
 hw/virtio/virtio-hmp-cmds.c                  |   3 +-
 hw/virtio/virtio-pci.c                       | 101 ++++++++-
 hw/virtio/virtio-qmp.c                       |  89 +++++---
 hw/virtio/virtio-qmp.h                       |   3 +-
 hw/virtio/virtio.c                           | 111 ++++++++--
 include/hw/virtio/vhost-backend.h            |   6 +
 include/hw/virtio/vhost.h                    |  36 +++-
 include/hw/virtio/virtio-features.h          | 124 +++++++++++
 include/hw/virtio/virtio-net.h               |   2 +-
 include/hw/virtio/virtio-pci.h               |   6 +-
 include/hw/virtio/virtio.h                   |  11 +-
 include/net/net.h                            |  20 +-
 include/net/vhost_net.h                      |  33 ++-
 include/standard-headers/linux/ethtool.h     |   4 +-
 include/standard-headers/linux/vhost_types.h |   5 +
 include/standard-headers/linux/virtio_net.h  |  33 +++
 linux-headers/asm-x86/kvm.h                  |   8 +-
 linux-headers/linux/kvm.h                    |   4 +
 linux-headers/linux/vhost.h                  |   7 +
 net/net.c                                    |  17 +-
 net/netmap.c                                 |   3 +-
 net/tap-bsd.c                                |   8 +-
 net/tap-linux.c                              |  38 +++-
 net/tap-linux.h                              |   9 +
 net/tap-solaris.c                            |   9 +-
 net/tap-stub.c                               |   8 +-
 net/tap.c                                    |  19 +-
 net/tap_int.h                                |   5 +-
 qapi/virtio.json                             |   8 +-
 38 files changed, 945 insertions(+), 227 deletions(-)
 create mode 100644 include/hw/virtio/virtio-features.h

--
2.50.0

author maintainer
#35 lore raw
Quoted 94 lines · Lei Yang

Does the compile of this series of patches require support for a special kernel environment? I hit a compile issue after applied you patches:
[1440/2928] Compiling C object libsystem.a.p/hw_virtio_vhost.c.o
FAILED: libsystem.a.p/hw_virtio_vhost.c.o
cc -m64 -Ilibsystem.a.p -I. -I.. -Isubprojects/dtc/libfdt

(quoted diff) +0 -15
-I../subprojects/dtc/libfdt -Isubprojects/libvduse
-I../subprojects/libvduse -Iui -Iqapi -Itrace -Iui/shader
-I/usr/include/pixman-1 -I/usr/include/glib-2.0
-I/usr/lib64/glib-2.0/include -I/usr/include/libmount
-I/usr/include/blkid -I/usr/include/sysprof-6
-I/usr/include/gio-unix-2.0 -I/usr/include/slirp
-fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O0 -g
-fstack-protector-strong -Wempty-body -Wendif-labels
-Wexpansion-to-defined -Wformat-security -Wformat-y2k
-Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self
-Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs
-Wold-style-declaration -Wold-style-definition -Wredundant-decls
-Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla
-Wwrite-strings -Wno-missing-include-dirs -Wno-psabi
-Wno-shift-negative-value -isystem

/mnt/tests/distribution/command/qemu/linux-headers -isystem linux-headers -iquote . -iquote /mnt/tests/distribution/command/qemu -iquote /mnt/tests/distribution/command/qemu/include -iquote /mnt/tests/distribution/command/qemu/host/include/x86_64 -iquote /mnt/tests/distribution/command/qemu/host/include/generic -iquote /mnt/tests/distribution/command/qemu/tcg/i386 -pthread -mcx16 -msse2

(quoted diff) +0 -5
-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-fno-strict-aliasing -fno-common -fwrapv -ftrivial-auto-var-init=zero
-fzero-call-used-regs=used-gpr -fPIE -DWITH_GZFILEOP -DCONFIG_SOFTMMU
-DCOMPILING_SYSTEM_VS_USER -MD -MQ libsystem.a.p/hw_virtio_vhost.c.o
-MF libsystem.a.p/hw_virtio_vhost.c.o.d -o

libsystem.a.p/hw_virtio_vhost.c.o -c ../hw/virtio/vhost.c ../hw/virtio/vhost.c: In function ‘vhost_dev_set_features’:
../hw/virtio/vhost.c:38:9: error: ‘r’ may be used uninitialized [-Werror=maybe-uninitialized]

   38 |         error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   39 |                      strerror(-retval), -retval); \
      |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/vhost.c:1006:9: note: in expansion of macro ‘VHOST_OPS_DEBUG’
 1006 |         VHOST_OPS_DEBUG(r, "extended features without device support");
      |         ^~~~~~~~~~~~~~~

../hw/virtio/vhost.c:989:9: note: ‘r’ was declared here
989 | int r;
| ^
cc1: all warnings being treated as errors
ninja: build stopped: subcommand failed.
make[1]: *** [Makefile:168: run-ninja] Error 1
make[1]: Leaving directory '/mnt/tests/distribution/command/qemu/build' make[1]: Entering directory '/mnt/tests/distribution/command/qemu/build' [1/1493] Generating subprojects/dtc/version_gen.h with a custom command [2/1493] Generating qemu-version.h with a custom command (wrapped by meson to capture output)
[3/1492] Compiling C object libsystem.a.p/hw_virtio_vhost.c.o
FAILED: libsystem.a.p/hw_virtio_vhost.c.o
cc -m64 -Ilibsystem.a.p -I. -I.. -Isubprojects/dtc/libfdt

(quoted diff) +0 -15
-I../subprojects/dtc/libfdt -Isubprojects/libvduse
-I../subprojects/libvduse -Iui -Iqapi -Itrace -Iui/shader
-I/usr/include/pixman-1 -I/usr/include/glib-2.0
-I/usr/lib64/glib-2.0/include -I/usr/include/libmount
-I/usr/include/blkid -I/usr/include/sysprof-6
-I/usr/include/gio-unix-2.0 -I/usr/include/slirp
-fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O0 -g
-fstack-protector-strong -Wempty-body -Wendif-labels
-Wexpansion-to-defined -Wformat-security -Wformat-y2k
-Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self
-Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs
-Wold-style-declaration -Wold-style-definition -Wredundant-decls
-Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla
-Wwrite-strings -Wno-missing-include-dirs -Wno-psabi
-Wno-shift-negative-value -isystem

/mnt/tests/distribution/command/qemu/linux-headers -isystem linux-headers -iquote . -iquote /mnt/tests/distribution/command/qemu -iquote /mnt/tests/distribution/command/qemu/include -iquote /mnt/tests/distribution/command/qemu/host/include/x86_64 -iquote /mnt/tests/distribution/command/qemu/host/include/generic -iquote /mnt/tests/distribution/command/qemu/tcg/i386 -pthread -mcx16 -msse2

(quoted diff) +0 -5
-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-fno-strict-aliasing -fno-common -fwrapv -ftrivial-auto-var-init=zero
-fzero-call-used-regs=used-gpr -fPIE -DWITH_GZFILEOP -DCONFIG_SOFTMMU
-DCOMPILING_SYSTEM_VS_USER -MD -MQ libsystem.a.p/hw_virtio_vhost.c.o
-MF libsystem.a.p/hw_virtio_vhost.c.o.d -o

libsystem.a.p/hw_virtio_vhost.c.o -c ../hw/virtio/vhost.c ../hw/virtio/vhost.c: In function ‘vhost_dev_set_features’:
../hw/virtio/vhost.c:38:9: error: ‘r’ may be used uninitialized [-Werror=maybe-uninitialized]

   38 |         error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   39 |                      strerror(-retval), -retval); \
      |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/vhost.c:1006:9: note: in expansion of macro ‘VHOST_OPS_DEBUG’
 1006 |         VHOST_OPS_DEBUG(r, "extended features without device support");
      |         ^~~~~~~~~~~~~~~
Quoted 7 lines

../hw/virtio/vhost.c:989:9: note: ‘r’ was declared here
989 | int r;
| ^
cc1: all warnings being treated as errors
ninja: build stopped: subcommand failed.
make[1]: *** [Makefile:168: run-ninja] Error 1
make[1]: Leaving directory '/mnt/tests/distribution/command/qemu/build'

Thank you for reporting the problem.

No special kernel requirement to build the series, the above is just a gross mistake on my side in patch 7/13. If you want to test the series, please apply incrementally the following diff.

What baffles me is that gcc 14.3.1 and 11.5.0 are not raising the warning (that looks legit/correct) here.

I'll fix the above in the next revision.

Note that you need a running kernel based on current net-next tree in both the hypervisor and the guest to actually leverage the new feature.

Thanks,

Paolo
---

hw/virtio/vhost.c +0 -0
index 2eee9b0886..c4eab5ce08 100644
@@ -1003,8 +1003,8 @@ static int vhost_dev_set_features(struct vhost_dev

*dev,

     if (virtio_features_use_extended(features) &&
         !dev->vhost_ops->vhost_set_features_ex) {
-        VHOST_OPS_DEBUG(r, "extended features without device support");
         r = -EINVAL;
+        VHOST_OPS_DEBUG(r, "extended features without device support");
         goto out;
     }
#36 lore raw
Quoted 110 lines · Paolo Abeni
Quoted 94 lines · Lei Yang

Does the compile of this series of patches require support for a special kernel environment? I hit a compile issue after applied you patches:
[1440/2928] Compiling C object libsystem.a.p/hw_virtio_vhost.c.o
FAILED: libsystem.a.p/hw_virtio_vhost.c.o
cc -m64 -Ilibsystem.a.p -I. -I.. -Isubprojects/dtc/libfdt

(quoted diff) +0 -15
-I../subprojects/dtc/libfdt -Isubprojects/libvduse
-I../subprojects/libvduse -Iui -Iqapi -Itrace -Iui/shader
-I/usr/include/pixman-1 -I/usr/include/glib-2.0
-I/usr/lib64/glib-2.0/include -I/usr/include/libmount
-I/usr/include/blkid -I/usr/include/sysprof-6
-I/usr/include/gio-unix-2.0 -I/usr/include/slirp
-fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O0 -g
-fstack-protector-strong -Wempty-body -Wendif-labels
-Wexpansion-to-defined -Wformat-security -Wformat-y2k
-Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self
-Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs
-Wold-style-declaration -Wold-style-definition -Wredundant-decls
-Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla
-Wwrite-strings -Wno-missing-include-dirs -Wno-psabi
-Wno-shift-negative-value -isystem

/mnt/tests/distribution/command/qemu/linux-headers -isystem linux-headers -iquote . -iquote /mnt/tests/distribution/command/qemu -iquote /mnt/tests/distribution/command/qemu/include -iquote /mnt/tests/distribution/command/qemu/host/include/x86_64 -iquote /mnt/tests/distribution/command/qemu/host/include/generic -iquote /mnt/tests/distribution/command/qemu/tcg/i386 -pthread -mcx16 -msse2

(quoted diff) +0 -5
-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-fno-strict-aliasing -fno-common -fwrapv -ftrivial-auto-var-init=zero
-fzero-call-used-regs=used-gpr -fPIE -DWITH_GZFILEOP -DCONFIG_SOFTMMU
-DCOMPILING_SYSTEM_VS_USER -MD -MQ libsystem.a.p/hw_virtio_vhost.c.o
-MF libsystem.a.p/hw_virtio_vhost.c.o.d -o

libsystem.a.p/hw_virtio_vhost.c.o -c ../hw/virtio/vhost.c ../hw/virtio/vhost.c: In function ‘vhost_dev_set_features’:
../hw/virtio/vhost.c:38:9: error: ‘r’ may be used uninitialized [-Werror=maybe-uninitialized]

   38 |         error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   39 |                      strerror(-retval), -retval); \
      |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/vhost.c:1006:9: note: in expansion of macro ‘VHOST_OPS_DEBUG’
 1006 |         VHOST_OPS_DEBUG(r, "extended features without device support");
      |         ^~~~~~~~~~~~~~~

../hw/virtio/vhost.c:989:9: note: ‘r’ was declared here
989 | int r;
| ^
cc1: all warnings being treated as errors
ninja: build stopped: subcommand failed.
make[1]: *** [Makefile:168: run-ninja] Error 1
make[1]: Leaving directory '/mnt/tests/distribution/command/qemu/build' make[1]: Entering directory '/mnt/tests/distribution/command/qemu/build' [1/1493] Generating subprojects/dtc/version_gen.h with a custom command [2/1493] Generating qemu-version.h with a custom command (wrapped by meson to capture output)
[3/1492] Compiling C object libsystem.a.p/hw_virtio_vhost.c.o
FAILED: libsystem.a.p/hw_virtio_vhost.c.o
cc -m64 -Ilibsystem.a.p -I. -I.. -Isubprojects/dtc/libfdt

(quoted diff) +0 -15
-I../subprojects/dtc/libfdt -Isubprojects/libvduse
-I../subprojects/libvduse -Iui -Iqapi -Itrace -Iui/shader
-I/usr/include/pixman-1 -I/usr/include/glib-2.0
-I/usr/lib64/glib-2.0/include -I/usr/include/libmount
-I/usr/include/blkid -I/usr/include/sysprof-6
-I/usr/include/gio-unix-2.0 -I/usr/include/slirp
-fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O0 -g
-fstack-protector-strong -Wempty-body -Wendif-labels
-Wexpansion-to-defined -Wformat-security -Wformat-y2k
-Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self
-Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs
-Wold-style-declaration -Wold-style-definition -Wredundant-decls
-Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla
-Wwrite-strings -Wno-missing-include-dirs -Wno-psabi
-Wno-shift-negative-value -isystem

/mnt/tests/distribution/command/qemu/linux-headers -isystem linux-headers -iquote . -iquote /mnt/tests/distribution/command/qemu -iquote /mnt/tests/distribution/command/qemu/include -iquote /mnt/tests/distribution/command/qemu/host/include/x86_64 -iquote /mnt/tests/distribution/command/qemu/host/include/generic -iquote /mnt/tests/distribution/command/qemu/tcg/i386 -pthread -mcx16 -msse2

(quoted diff) +0 -5
-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-fno-strict-aliasing -fno-common -fwrapv -ftrivial-auto-var-init=zero
-fzero-call-used-regs=used-gpr -fPIE -DWITH_GZFILEOP -DCONFIG_SOFTMMU
-DCOMPILING_SYSTEM_VS_USER -MD -MQ libsystem.a.p/hw_virtio_vhost.c.o
-MF libsystem.a.p/hw_virtio_vhost.c.o.d -o

libsystem.a.p/hw_virtio_vhost.c.o -c ../hw/virtio/vhost.c ../hw/virtio/vhost.c: In function ‘vhost_dev_set_features’:
../hw/virtio/vhost.c:38:9: error: ‘r’ may be used uninitialized [-Werror=maybe-uninitialized]

   38 |         error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   39 |                      strerror(-retval), -retval); \
      |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~
../hw/virtio/vhost.c:1006:9: note: in expansion of macro ‘VHOST_OPS_DEBUG’
 1006 |         VHOST_OPS_DEBUG(r, "extended features without device support");
      |         ^~~~~~~~~~~~~~~
Quoted 7 lines

../hw/virtio/vhost.c:989:9: note: ‘r’ was declared here
989 | int r;
| ^
cc1: all warnings being treated as errors
ninja: build stopped: subcommand failed.
make[1]: *** [Makefile:168: run-ninja] Error 1
make[1]: Leaving directory '/mnt/tests/distribution/command/qemu/build'

Thank you for reporting the problem.

No special kernel requirement to build the series, the above is just a gross mistake on my side in patch 7/13. If you want to test the series, please apply incrementally the following diff.

Quoted 7 lines

What baffles me is that gcc 14.3.1 and 11.5.0 are not raising the warning (that looks legit/correct) here.

I'll fix the above in the next revision.

Note that you need a running kernel based on current net-next tree in both the hypervisor and the guest to actually leverage the new feature.

Ok, I will test this series again and update the test results.

Thanks
Lei

Quoted 20 lines

Thanks,

Paolo
---

hw/virtio/vhost.c +1 -1
index 2eee9b0886..c4eab5ce08 100644
@@ -1003,8 +1003,8 @@ static int vhost_dev_set_features(struct vhost_dev
 *dev,
 
     if (virtio_features_use_extended(features) &&
         !dev->vhost_ops->vhost_set_features_ex) {
-        VHOST_OPS_DEBUG(r, "extended features without device support");
         r = -EINVAL;
+        VHOST_OPS_DEBUG(r, "extended features without device support");
         goto out;
     }