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

Paolo Abeni posted 13 patches 3 months, 4 weeks ago
Maintainers: Dmitry Fleytman <dmitry.fleytman@gmail.com>, Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>, Jason Wang <jasowang@redhat.com>, Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>, "Michael S. Tsirkin" <mst@redhat.com>, Stefano Garzarella <sgarzare@redhat.com>, Cornelia Huck <cohuck@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, Luigi Rizzo <rizzo@iet.unipi.it>, Giuseppe Lettieri <g.lettieri@iet.unipi.it>, Vincenzo Maffione <v.maffione@gmail.com>, Eric Blake <eblake@redhat.com>, Markus Armbruster <armbru@redhat.com>
There is a newer version of this series
[PATCH RFC v3 13/13] net: implement UDP tunnel features offloading
Posted by Paolo Abeni 3 months, 4 weeks ago
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 the kernel supported features list, to allow qemu
transfer to the backend the needed information.

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
v2 -> v3:
  - rebased on top of "net: Consolidate vhost feature bits into vhost_net
    structure"
  - _array -> _ex

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/virtio-net.c | 34 ++++++++++++++++++++++++++--------
 include/net/net.h   |  2 ++
 net/net.c           |  3 ++-
 net/tap-linux.c     |  6 ++++++
 net/tap.c           |  2 ++
 5 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 70c85f7f77..46f7efac7d 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -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 ?
@@ -803,6 +812,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);
@@ -824,7 +837,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);
 }
@@ -937,7 +952,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);
@@ -3160,13 +3176,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_ex);
 
     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) {
@@ -3986,7 +4004,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);
diff --git a/include/net/net.h b/include/net/net.h
index 9a9084690d..72b476ee1d 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -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)                            \
diff --git a/net/net.c b/net/net.c
index 9536184a0c..27e0d27807 100644
--- a/net/net.c
+++ b/net/net.c
@@ -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);
diff --git a/net/tap-linux.c b/net/tap-linux.c
index e2628be798..8e275d2ea4 100644
--- a/net/tap-linux.c
+++ b/net/tap-linux.c
@@ -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) {
diff --git a/net/tap.c b/net/tap.c
index 23c6c118e7..2dfa843547 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -62,6 +62,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
 };
 
-- 
2.50.0
Re: [PATCH RFC v3 13/13] net: implement UDP tunnel features offloading
Posted by Stefano Garzarella 3 months, 4 weeks ago
On Fri, Jul 18, 2025 at 10:52:39AM +0200, Paolo Abeni wrote:
>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 the kernel supported features list, to allow qemu
>transfer to the backend the needed information.
>
>Signed-off-by: Paolo Abeni <pabeni@redhat.com>
>---
>v2 -> v3:
>  - rebased on top of "net: Consolidate vhost feature bits into vhost_net
>    structure"
>  - _array -> _ex
>
>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/virtio-net.c | 34 ++++++++++++++++++++++++++--------
> include/net/net.h   |  2 ++
> net/net.c           |  3 ++-
> net/tap-linux.c     |  6 ++++++
> net/tap.c           |  2 ++
> 5 files changed, 38 insertions(+), 9 deletions(-)
>
>diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
>index 70c85f7f77..46f7efac7d 100644
>--- a/hw/net/virtio-net.c
>+++ b/hw/net/virtio-net.c
>@@ -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) |

I'd replace | with || since virtio_has_feature_ex() return a `bool`.

Thanks,
Stefano

>+           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 ?
>@@ -803,6 +812,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);
>@@ -824,7 +837,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);
> }
>@@ -937,7 +952,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);
>@@ -3160,13 +3176,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_ex);
>
>     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) {
>@@ -3986,7 +4004,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);
>diff --git a/include/net/net.h b/include/net/net.h
>index 9a9084690d..72b476ee1d 100644
>--- a/include/net/net.h
>+++ b/include/net/net.h
>@@ -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)                            \
>diff --git a/net/net.c b/net/net.c
>index 9536184a0c..27e0d27807 100644
>--- a/net/net.c
>+++ b/net/net.c
>@@ -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);
>diff --git a/net/tap-linux.c b/net/tap-linux.c
>index e2628be798..8e275d2ea4 100644
>--- a/net/tap-linux.c
>+++ b/net/tap-linux.c
>@@ -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) {
>diff --git a/net/tap.c b/net/tap.c
>index 23c6c118e7..2dfa843547 100644
>--- a/net/tap.c
>+++ b/net/tap.c
>@@ -62,6 +62,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
> };
>
>-- 
>2.50.0
>
Re: [PATCH RFC v3 13/13] net: implement UDP tunnel features offloading
Posted by Stefano Garzarella 3 months, 4 weeks ago
On Fri, Jul 18, 2025 at 10:52:39AM +0200, Paolo Abeni wrote:
>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 the kernel supported features list, to allow qemu
>transfer to the backend the needed information.
>
>Signed-off-by: Paolo Abeni <pabeni@redhat.com>
>---
>v2 -> v3:
>  - rebased on top of "net: Consolidate vhost feature bits into vhost_net
>    structure"
>  - _array -> _ex
>
>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/virtio-net.c | 34 ++++++++++++++++++++++++++--------
> include/net/net.h   |  2 ++
> net/net.c           |  3 ++-
> net/tap-linux.c     |  6 ++++++
> net/tap.c           |  2 ++
> 5 files changed, 38 insertions(+), 9 deletions(-)
>
>diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
>index 70c85f7f77..46f7efac7d 100644
>--- a/hw/net/virtio-net.c
>+++ b/hw/net/virtio-net.c
>@@ -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 ?
>@@ -803,6 +812,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);
>@@ -824,7 +837,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);
> }
>@@ -937,7 +952,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);
>@@ -3160,13 +3176,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_ex);
>
>     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) {
>@@ -3986,7 +4004,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);
>diff --git a/include/net/net.h b/include/net/net.h
>index 9a9084690d..72b476ee1d 100644
>--- a/include/net/net.h
>+++ b/include/net/net.h
>@@ -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)                            \
>diff --git a/net/net.c b/net/net.c
>index 9536184a0c..27e0d27807 100644
>--- a/net/net.c
>+++ b/net/net.c
>@@ -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);
>diff --git a/net/tap-linux.c b/net/tap-linux.c
>index e2628be798..8e275d2ea4 100644
>--- a/net/tap-linux.c
>+++ b/net/tap-linux.c
>@@ -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) {
>diff --git a/net/tap.c b/net/tap.c
>index 23c6c118e7..2dfa843547 100644
>--- a/net/tap.c
>+++ b/net/tap.c
>@@ -62,6 +62,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,

The *_GSO_CSUM are not supported by vhost-net, right?
(sorry, I don't know the details, it just occurred to me by looking at 
the fetaures we enable in the other patch.)

Thanks,
Stefano

>     VHOST_INVALID_FEATURE_BIT
> };
>
>-- 
>2.50.0
>
Re: [PATCH RFC v3 13/13] net: implement UDP tunnel features offloading
Posted by Paolo Abeni 3 months, 4 weeks ago
On 7/18/25 3:22 PM, Stefano Garzarella wrote:
> On Fri, Jul 18, 2025 at 10:52:39AM +0200, Paolo Abeni wrote:
>> diff --git a/net/tap.c b/net/tap.c
>> index 23c6c118e7..2dfa843547 100644
>> --- a/net/tap.c
>> +++ b/net/tap.c
>> @@ -62,6 +62,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,
> 
> The *_GSO_CSUM are not supported by vhost-net, right?
> (sorry, I don't know the details, it just occurred to me by looking at 
> the fetaures we enable in the other patch.)

Yes, the kernel module supports/exposes only the 2 features above:
vhost-net need only to be aware of the exact virtio_net_header size,
which in turn depends just on them. Enabling/disabling the outer header
csum offload does not change the virtio_net_header struct.

The actual csum offload is implemented by the tun device.

Please LMK if the above solves your doubt.

Thanks,

Paolo
Re: [PATCH RFC v3 13/13] net: implement UDP tunnel features offloading
Posted by Stefano Garzarella 3 months, 4 weeks ago
On Fri, Jul 18, 2025 at 03:44:20PM +0200, Paolo Abeni wrote:
>On 7/18/25 3:22 PM, Stefano Garzarella wrote:
>> On Fri, Jul 18, 2025 at 10:52:39AM +0200, Paolo Abeni wrote:
>>> diff --git a/net/tap.c b/net/tap.c
>>> index 23c6c118e7..2dfa843547 100644
>>> --- a/net/tap.c
>>> +++ b/net/tap.c
>>> @@ -62,6 +62,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,
>>
>> The *_GSO_CSUM are not supported by vhost-net, right?
>> (sorry, I don't know the details, it just occurred to me by looking at
>> the fetaures we enable in the other patch.)
>
>Yes, the kernel module supports/exposes only the 2 features above:
>vhost-net need only to be aware of the exact virtio_net_header size,
>which in turn depends just on them. Enabling/disabling the outer header
>csum offload does not change the virtio_net_header struct.
>
>The actual csum offload is implemented by the tun device.
>
>Please LMK if the above solves your doubt.

Yes, thanks!

Stefano