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

virtio-net: avoid unnecessary checksum calculation on guest RX

Commit a2fb4bc4e2a6 ("net: implement virtio helpers to handle UDP
GSO tunneling.") inadvertently altered checksum offload behavior
for guests not using UDP GSO tunneling.

Before, tun_put_user called tun_vnet_hdr_from_skb, which passed
has_data_valid = true to virtio_net_hdr_from_skb.

After, tun_put_user began calling tun_vnet_hdr_tnl_from_skb instead,
which passes has_data_valid = false into both call sites.

This caused virtio hdr flags to not include VIRTIO_NET_HDR_F_DATA_VALID
for SKBs where skb->ip_summed == CHECKSUM_UNNECESSARY. As a result,
guests are forced to recalculate checksums unnecessarily.

Restore the previous behavior by ensuring has_data_valid = true is
passed in the !tnl_gso_type case, but only from tun side, as
virtio_net_hdr_tnl_from_skb() is used also by the virtio_net driver,
which in turn must not use VIRTIO_NET_HDR_F_DATA_VALID on tx.

cc: stable@vger.kernel.org
Fixes: a2fb4bc4e2a6 ("net: implement virtio helpers to handle UDP GSO tunneling.")
Signed-off-by: Jon Kohler <jon@nutanix.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Link: https://patch.msgid.link/20251125222754.1737443-1-jon@nutanix.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Jon Kohler and committed by
Jakub Kicinski
1cd1c472 6d66e093

+7 -5
+1 -1
drivers/net/tun_vnet.h
··· 244 244 245 245 if (virtio_net_hdr_tnl_from_skb(skb, tnl_hdr, has_tnl_offload, 246 246 tun_vnet_is_little_endian(flags), 247 - vlan_hlen)) { 247 + vlan_hlen, true)) { 248 248 struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr; 249 249 struct skb_shared_info *sinfo = skb_shinfo(skb); 250 250
+2 -1
drivers/net/virtio_net.c
··· 3339 3339 hdr = &skb_vnet_common_hdr(skb)->tnl_hdr; 3340 3340 3341 3341 if (virtio_net_hdr_tnl_from_skb(skb, hdr, vi->tx_tnl, 3342 - virtio_is_little_endian(vi->vdev), 0)) 3342 + virtio_is_little_endian(vi->vdev), 0, 3343 + false)) 3343 3344 return -EPROTO; 3344 3345 3345 3346 if (vi->mergeable_rx_bufs)
+4 -3
include/linux/virtio_net.h
··· 384 384 struct virtio_net_hdr_v1_hash_tunnel *vhdr, 385 385 bool tnl_hdr_negotiated, 386 386 bool little_endian, 387 - int vlan_hlen) 387 + int vlan_hlen, 388 + bool has_data_valid) 388 389 { 389 390 struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)vhdr; 390 391 unsigned int inner_nh, outer_th; ··· 395 394 tnl_gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL | 396 395 SKB_GSO_UDP_TUNNEL_CSUM); 397 396 if (!tnl_gso_type) 398 - return virtio_net_hdr_from_skb(skb, hdr, little_endian, false, 399 - vlan_hlen); 397 + return virtio_net_hdr_from_skb(skb, hdr, little_endian, 398 + has_data_valid, vlan_hlen); 400 399 401 400 /* Tunnel support not negotiated but skb ask for it. */ 402 401 if (!tnl_hdr_negotiated)