]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
virtio_net: fix alignment for virtio_net_hdr_v1_hash
authorMichael S. Tsirkin <mst@redhat.com>
Fri, 31 Oct 2025 06:05:51 +0000 (14:05 +0800)
committerJakub Kicinski <kuba@kernel.org>
Wed, 5 Nov 2025 01:14:07 +0000 (17:14 -0800)
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>
Tested-by: Lei Yang <leiyang@redhat.com>
Link: https://patch.msgid.link/20251031060551.126-1-jasowang@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/virtio_net.c
include/linux/virtio_net.h
include/uapi/linux/virtio_net.h

index 8e8a179aaa491529ffeda99661bc6be4accbc7ed..e6e650bc3bc32ae89c985d241df1c3a020eb7602 100644 (file)
@@ -2539,6 +2539,13 @@ err_buf:
        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;
 }
 
index 4d1780848d0e04908bd627b02928c2d740693e89..b673c31569f32048b3e4c3647fadc08456248c4d 100644 (file)
@@ -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;
 
index 8bf27ab8bcb4dd9c13af177e4531f7f10d921a39..1db45b01532b5824e2000b82915be0444dee94b8 100644 (file)
@@ -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