Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

virtio_net: fix alignment for virtio_net_hdr_v1_hash

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>

authored by

Michael S. Tsirkin and committed by
Jakub Kicinski
c3838262 e120f467

+17 -4
+13 -2
drivers/net/virtio_net.c
··· 2539 2539 return NULL; 2540 2540 } 2541 2541 2542 + static inline u32 2543 + virtio_net_hash_value(const struct virtio_net_hdr_v1_hash *hdr_hash) 2544 + { 2545 + return __le16_to_cpu(hdr_hash->hash_value_lo) | 2546 + (__le16_to_cpu(hdr_hash->hash_value_hi) << 16); 2547 + } 2548 + 2542 2549 static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash, 2543 2550 struct sk_buff *skb) 2544 2551 { ··· 2572 2565 default: 2573 2566 rss_hash_type = PKT_HASH_TYPE_NONE; 2574 2567 } 2575 - skb_set_hash(skb, __le32_to_cpu(hdr_hash->hash_value), rss_hash_type); 2568 + skb_set_hash(skb, virtio_net_hash_value(hdr_hash), rss_hash_type); 2576 2569 } 2577 2570 2578 2571 static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *rq, ··· 3317 3310 bool can_push; 3318 3311 3319 3312 pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest); 3313 + 3314 + /* Make sure it's safe to cast between formats */ 3315 + BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr)); 3316 + BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr.hdr)); 3320 3317 3321 3318 can_push = vi->any_header_sg && 3322 3319 !((unsigned long)skb->data & (__alignof__(*hdr) - 1)) && ··· 6761 6750 hash_report = VIRTIO_NET_HASH_REPORT_NONE; 6762 6751 6763 6752 *rss_type = virtnet_xdp_rss_type[hash_report]; 6764 - *hash = __le32_to_cpu(hdr_hash->hash_value); 6753 + *hash = virtio_net_hash_value(hdr_hash); 6765 6754 return 0; 6766 6755 } 6767 6756
+2 -1
include/linux/virtio_net.h
··· 401 401 if (!tnl_hdr_negotiated) 402 402 return -EINVAL; 403 403 404 - vhdr->hash_hdr.hash_value = 0; 404 + vhdr->hash_hdr.hash_value_lo = 0; 405 + vhdr->hash_hdr.hash_value_hi = 0; 405 406 vhdr->hash_hdr.hash_report = 0; 406 407 vhdr->hash_hdr.padding = 0; 407 408
+2 -1
include/uapi/linux/virtio_net.h
··· 193 193 194 194 struct virtio_net_hdr_v1_hash { 195 195 struct virtio_net_hdr_v1 hdr; 196 - __le32 hash_value; 196 + __le16 hash_value_lo; 197 + __le16 hash_value_hi; 197 198 #define VIRTIO_NET_HASH_REPORT_NONE 0 198 199 #define VIRTIO_NET_HASH_REPORT_IPv4 1 199 200 #define VIRTIO_NET_HASH_REPORT_TCPv4 2