virtualization.lists.linux-foundation.org archive mirror
 help / color / mirror / Atom feed
* [PATCH vhost] virtio_net: update the hdr_len calculation for xmit
@ 2024-03-20 10:20 Xuan Zhuo
  2024-03-21  4:17 ` Jason Wang
  0 siblings, 1 reply; 9+ messages in thread
From: Xuan Zhuo @ 2024-03-20 10:20 UTC (permalink / raw)
  To: virtualization; +Cc: Michael S. Tsirkin, Jason Wang, Xuan Zhuo, Spike Du

The virtio spec says:
    If one of the VIRTIO_NET_F_HOST_TSO4, TSO6, USO or UFO options have
    been negotiated:
        If the VIRTIO_NET_F_GUEST_HDRLEN feature has been negotiated,
        and gso_type differs from VIRTIO_NET_HDR_GSO_NONE, the driver
        MUST set hdr_len to a value equal to the length of the headers,
        including the transport header.

        If the VIRTIO_NET_F_GUEST_HDRLEN feature has not been negotiated,
        or gso_type is VIRTIO_NET_HDR_GSO_NONE, the driver SHOULD set
        hdr_len to a value not less than the length of the headers,
        including the transport header.

So if the VIRTIO_NET_F_GUEST_HDRLEN feature has been negotiated, the
hdr->hdr_len should be eth header + ip header + tcp/udp header.

But now:
    hdr->hdr_len = __cpu_to_virtio15(little_endian, skb_headlen(skb));

The skb_headlen() returns the linear space of the skb, not the header
size that only match the case the VIRTIO_NET_F_GUEST_HDRLEN feature has
not been negotiated, or gso_type is VIRTIO_NET_HDR_GSO_NONE.

We do not check the feature of the device. This function is a common
function used by many places. So we do more stricter work whatever
the features is negotiated.

For the case skb_is_gso(skb) is false, if none of the
VIRTIO_NET_F_HOST_TSO4, TSO6, USO or UFO options have been negotiated,
the spec not define the action of setting hdr_len. Here I set it to
skb_headlen(). If one of the above features have been negotiated, we
should set a value not less than the length of "eth header + ip header +
tcp/udp header". So the skb_headlen() also is a valid value.

For the case skb_is_gso(skb) is true, it implies that one of
VIRTIO_NET_F_HOST_TSO4, TSO6, USO or UFO options MUST have been
negotiated. If the VIRTIO_NET_F_GUEST_HDRLEN is negotiated, we MUST set
it to the length of "eth header + ip header + tcp/udp header".
If the VIRTIO_NET_F_GUEST_HDRLEN is not negotiated, that still be a
valid value.

Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
Reported-by: Spike Du <spiked@nvidia.com>
---
 include/linux/virtio_net.h | 41 ++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 4dfa9b69ca8d..51d93f9762d7 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -201,24 +201,45 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
 
 	if (skb_is_gso(skb)) {
 		struct skb_shared_info *sinfo = skb_shinfo(skb);
+		u32 hdrlen;
 
-		/* This is a hint as to how much should be linear. */
-		hdr->hdr_len = __cpu_to_virtio16(little_endian,
-						 skb_headlen(skb));
-		hdr->gso_size = __cpu_to_virtio16(little_endian,
-						  sinfo->gso_size);
-		if (sinfo->gso_type & SKB_GSO_TCPV4)
+		if (sinfo->gso_type & SKB_GSO_TCPV4) {
 			hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
-		else if (sinfo->gso_type & SKB_GSO_TCPV6)
+			hdrlen = tcp_hdrlen(skb) + skb_transport_offset(skb);
+
+		} else if (sinfo->gso_type & SKB_GSO_TCPV6) {
 			hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
-		else if (sinfo->gso_type & SKB_GSO_UDP_L4)
+			hdrlen = tcp_hdrlen(skb) + skb_transport_offset(skb);
+
+		} else if (sinfo->gso_type & SKB_GSO_UDP_L4) {
 			hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4;
-		else
+			hdrlen = sizeof(struct udphdr) + skb_transport_offset(skb);
+
+		} else {
 			return -EINVAL;
+		}
+
+		/* One of VIRTIO_NET_F_HOST_TSO4, TSO6, USO or UFO options MUST
+		 * have been negotiated. If the VIRTIO_NET_F_GUEST_HDRLEN is
+		 * negotiated, we MUST set it to the length of "eth header + ip
+		 * header + tcp/udp header".  If the VIRTIO_NET_F_GUEST_HDRLEN
+		 * is not negotiated, that still be a valid value.
+		 */
+		hdr->hdr_len = __cpu_to_virtio16(little_endian, hdrlen);
+		hdr->gso_size = __cpu_to_virtio16(little_endian,
+						  sinfo->gso_size);
+
 		if (sinfo->gso_type & SKB_GSO_TCP_ECN)
 			hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
-	} else
+	} else {
+		/* If one of the VIRTIO_NET_F_HOST_TSO4, TSO6, USO or UFO
+		 * options have been negotiated, we should set hdr_len to a
+		 * value not less than the length of "eth header + ip header +
+		 * tcp/udp header".
+		 */
+		hdr->hdr_len = __cpu_to_virtio16(little_endian, skb_headlen(skb));
 		hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
+	}
 
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
 		hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
-- 
2.32.0.3.g01195cf9f


^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-04-23  5:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-03-20 10:20 [PATCH vhost] virtio_net: update the hdr_len calculation for xmit Xuan Zhuo
2024-03-21  4:17 ` Jason Wang
2024-03-22  2:27   ` Xuan Zhuo
2024-03-22  5:04     ` Jason Wang
2024-03-22  5:45       ` Xuan Zhuo
2024-03-22  5:57         ` Jason Wang
2024-03-22  6:08           ` Xuan Zhuo
2024-04-22 20:31             ` Michael S. Tsirkin
2024-04-23  5:53               ` Xuan Zhuo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).