drivers/net/virtio_net.c | 15 +++++++++++++-- include/linux/virtio_net.h | 3 ++- include/uapi/linux/virtio_net.h | 3 ++- 3 files changed, 17 insertions(+), 4 deletions(-)
From: "Michael S. Tsirkin" <mst@redhat.com>
Changing alignment of header would mean it's no longer safe to cast a
2 byte aligned pointer between formats. Use two 16 bit fields to make
it 2 byte aligned as previously.
This fixes the performance regression since
commit ("virtio_net: enable gso over UDP tunnel support.") as it uses
virtio_net_hdr_v1_hash_tunnel which embeds
virtio_net_hdr_v1_hash. Pktgen in guest + XDP_DROP on TAP + vhost_net
shows the TX PPS is recovered from 2.4Mpps to 4.45Mpps.
Fixes: 56a06bd40fab ("virtio_net: enable gso over UDP tunnel support.")
Cc: stable@vger.kernel.org
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
Changes since V1:
- Fix build issues of virtio_net_hdr_tnl_from_skb()
---
drivers/net/virtio_net.c | 15 +++++++++++++--
include/linux/virtio_net.h | 3 ++-
include/uapi/linux/virtio_net.h | 3 ++-
3 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 8e8a179aaa49..e6e650bc3bc3 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -2539,6 +2539,13 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
return NULL;
}
+static inline u32
+virtio_net_hash_value(const struct virtio_net_hdr_v1_hash *hdr_hash)
+{
+ return __le16_to_cpu(hdr_hash->hash_value_lo) |
+ (__le16_to_cpu(hdr_hash->hash_value_hi) << 16);
+}
+
static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash,
struct sk_buff *skb)
{
@@ -2565,7 +2572,7 @@ static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash,
default:
rss_hash_type = PKT_HASH_TYPE_NONE;
}
- skb_set_hash(skb, __le32_to_cpu(hdr_hash->hash_value), rss_hash_type);
+ skb_set_hash(skb, virtio_net_hash_value(hdr_hash), rss_hash_type);
}
static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *rq,
@@ -3311,6 +3318,10 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);
+ /* Make sure it's safe to cast between formats */
+ BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr));
+ BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr.hdr));
+
can_push = vi->any_header_sg &&
!((unsigned long)skb->data & (__alignof__(*hdr) - 1)) &&
!skb_header_cloned(skb) && skb_headroom(skb) >= hdr_len;
@@ -6750,7 +6761,7 @@ static int virtnet_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash,
hash_report = VIRTIO_NET_HASH_REPORT_NONE;
*rss_type = virtnet_xdp_rss_type[hash_report];
- *hash = __le32_to_cpu(hdr_hash->hash_value);
+ *hash = virtio_net_hash_value(hdr_hash);
return 0;
}
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 4d1780848d0e..b673c31569f3 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -401,7 +401,8 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
if (!tnl_hdr_negotiated)
return -EINVAL;
- vhdr->hash_hdr.hash_value = 0;
+ vhdr->hash_hdr.hash_value_lo = 0;
+ vhdr->hash_hdr.hash_value_hi = 0;
vhdr->hash_hdr.hash_report = 0;
vhdr->hash_hdr.padding = 0;
diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
index 8bf27ab8bcb4..1db45b01532b 100644
--- a/include/uapi/linux/virtio_net.h
+++ b/include/uapi/linux/virtio_net.h
@@ -193,7 +193,8 @@ struct virtio_net_hdr_v1 {
struct virtio_net_hdr_v1_hash {
struct virtio_net_hdr_v1 hdr;
- __le32 hash_value;
+ __le16 hash_value_lo;
+ __le16 hash_value_hi;
#define VIRTIO_NET_HASH_REPORT_NONE 0
#define VIRTIO_NET_HASH_REPORT_IPv4 1
#define VIRTIO_NET_HASH_REPORT_TCPv4 2
--
2.31.1
On Fri, Oct 31, 2025 at 02:05:51PM +0800, Jason Wang wrote: > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h > index 4d1780848d0e..b673c31569f3 100644 > --- a/include/linux/virtio_net.h > +++ b/include/linux/virtio_net.h > @@ -401,7 +401,8 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb, > if (!tnl_hdr_negotiated) > return -EINVAL; > > - vhdr->hash_hdr.hash_value = 0; > + vhdr->hash_hdr.hash_value_lo = 0; > + vhdr->hash_hdr.hash_value_hi = 0; > vhdr->hash_hdr.hash_report = 0; > vhdr->hash_hdr.padding = 0; > BTW is it just me or is old code space-indented here? We should probably switch it to tabs. -- MST
Tested this patch with virtio-net regression tests, everything works fine.
Tested-by: Lei Yang <leiyang@redhat.com>
On Fri, Oct 31, 2025 at 2:06 PM Jason Wang <jasowang@redhat.com> wrote:
>
> From: "Michael S. Tsirkin" <mst@redhat.com>
>
> Changing alignment of header would mean it's no longer safe to cast a
> 2 byte aligned pointer between formats. Use two 16 bit fields to make
> it 2 byte aligned as previously.
>
> This fixes the performance regression since
> commit ("virtio_net: enable gso over UDP tunnel support.") as it uses
> virtio_net_hdr_v1_hash_tunnel which embeds
> virtio_net_hdr_v1_hash. Pktgen in guest + XDP_DROP on TAP + vhost_net
> shows the TX PPS is recovered from 2.4Mpps to 4.45Mpps.
>
> Fixes: 56a06bd40fab ("virtio_net: enable gso over UDP tunnel support.")
> Cc: stable@vger.kernel.org
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> ---
> Changes since V1:
> - Fix build issues of virtio_net_hdr_tnl_from_skb()
> ---
> drivers/net/virtio_net.c | 15 +++++++++++++--
> include/linux/virtio_net.h | 3 ++-
> include/uapi/linux/virtio_net.h | 3 ++-
> 3 files changed, 17 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 8e8a179aaa49..e6e650bc3bc3 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -2539,6 +2539,13 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
> return NULL;
> }
>
> +static inline u32
> +virtio_net_hash_value(const struct virtio_net_hdr_v1_hash *hdr_hash)
> +{
> + return __le16_to_cpu(hdr_hash->hash_value_lo) |
> + (__le16_to_cpu(hdr_hash->hash_value_hi) << 16);
> +}
> +
> static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash,
> struct sk_buff *skb)
> {
> @@ -2565,7 +2572,7 @@ static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash,
> default:
> rss_hash_type = PKT_HASH_TYPE_NONE;
> }
> - skb_set_hash(skb, __le32_to_cpu(hdr_hash->hash_value), rss_hash_type);
> + skb_set_hash(skb, virtio_net_hash_value(hdr_hash), rss_hash_type);
> }
>
> static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *rq,
> @@ -3311,6 +3318,10 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
>
> pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);
>
> + /* Make sure it's safe to cast between formats */
> + BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr));
> + BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr.hdr));
> +
> can_push = vi->any_header_sg &&
> !((unsigned long)skb->data & (__alignof__(*hdr) - 1)) &&
> !skb_header_cloned(skb) && skb_headroom(skb) >= hdr_len;
> @@ -6750,7 +6761,7 @@ static int virtnet_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash,
> hash_report = VIRTIO_NET_HASH_REPORT_NONE;
>
> *rss_type = virtnet_xdp_rss_type[hash_report];
> - *hash = __le32_to_cpu(hdr_hash->hash_value);
> + *hash = virtio_net_hash_value(hdr_hash);
> return 0;
> }
>
> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index 4d1780848d0e..b673c31569f3 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h
> @@ -401,7 +401,8 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
> if (!tnl_hdr_negotiated)
> return -EINVAL;
>
> - vhdr->hash_hdr.hash_value = 0;
> + vhdr->hash_hdr.hash_value_lo = 0;
> + vhdr->hash_hdr.hash_value_hi = 0;
> vhdr->hash_hdr.hash_report = 0;
> vhdr->hash_hdr.padding = 0;
>
> diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
> index 8bf27ab8bcb4..1db45b01532b 100644
> --- a/include/uapi/linux/virtio_net.h
> +++ b/include/uapi/linux/virtio_net.h
> @@ -193,7 +193,8 @@ struct virtio_net_hdr_v1 {
>
> struct virtio_net_hdr_v1_hash {
> struct virtio_net_hdr_v1 hdr;
> - __le32 hash_value;
> + __le16 hash_value_lo;
> + __le16 hash_value_hi;
> #define VIRTIO_NET_HASH_REPORT_NONE 0
> #define VIRTIO_NET_HASH_REPORT_IPv4 1
> #define VIRTIO_NET_HASH_REPORT_TCPv4 2
> --
> 2.31.1
>
>
© 2016 - 2026 Red Hat, Inc.