Demonstrate support for new virtio-net features

VIRTIO_NET_HDR_F_TSTAMP

This is not intended to be merged.

A full feature test also requires a patched qemu binary that knows
these features and negotiates correct vnet_hdr_sz in
virtio_net_set_mrg_rx_bufs. See
https://github.com/strumtrar/qemu/tree/v11.0.1/virtio-rx-stamps

Not-yet-signed-off-by: Steffen Trumtrar <[email protected]>

---
 Changes since v2:
 - use tstamp from virtio_net_common_hdr
 - use the negotiated vnet_hdr_sz in __tun_vnet_hdr_get

Signed-off-by: Steffen Trumtrar <[email protected]>
---
 drivers/net/tun.c               | 20 +++++++++++++++-----
 drivers/net/tun_vnet.h          | 27 ++++++++++++++++-----------
 drivers/net/virtio_net.c        | 11 -----------
 include/uapi/linux/virtio_net.h | 11 +++++++++++
 4 files changed, 42 insertions(+), 27 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 5a302709a68aa..924784771b6b7 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -2157,12 +2157,23 @@ static ssize_t tun_put_user(struct tun_struct *tun,
        }
 
        if (vnet_hdr_sz) {
-               struct virtio_net_hdr_v1_hash_tunnel hdr;
-               struct virtio_net_hdr *gso;
+               struct virtio_net_common_hdr hdr;
 
                memset(&hdr, 0, sizeof(hdr));
+
+               /* hdr has at least the size up to and including the tstamp 
field. */
+               if (vnet_hdr_sz >= offsetof(struct virtio_net_common_hdr, 
tstamp) +
+                                  sizeof_field(struct virtio_net_common_hdr, 
tstamp)) {
+                       u64 tstamp = ktime_get_clocktai_ns();
+
+                       hdr.tstamp[0] = (tstamp & 0x000000000000ffffULL) >> 0;
+                       hdr.tstamp[1] = (tstamp & 0x00000000ffff0000ULL) >> 16;
+                       hdr.tstamp[2] = (tstamp & 0x0000ffff00000000ULL) >> 32;
+                       hdr.tstamp[3] = (tstamp & 0xffff000000000000ULL) >> 48;
+               }
+
                ret = tun_vnet_hdr_tnl_from_skb(tun->flags, tun->dev, skb,
-                                               &hdr);
+                                               (struct 
virtio_net_hdr_v1_hash_tunnel *)&hdr);
                if (ret)
                        return ret;
 
@@ -2170,9 +2181,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
                 * Drop the packet if the configured header size is too small
                 * WRT the enabled offloads.
                 */
-               gso = (struct virtio_net_hdr *)&hdr;
                ret = __tun_vnet_hdr_put(vnet_hdr_sz, tun->dev->features,
-                                        iter, gso);
+                                        iter, &hdr);
                if (ret)
                        return ret;
        }
diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h
index f4c652b1fa44d..0a7fe1ce889b5 100644
--- a/drivers/net/tun_vnet.h
+++ b/drivers/net/tun_vnet.h
@@ -109,12 +109,9 @@ static inline long tun_vnet_ioctl(int *vnet_hdr_sz, 
unsigned int *flags,
        }
 }
 
-static inline unsigned int tun_vnet_parse_size(netdev_features_t features)
+static inline unsigned int tun_vnet_parse_size(int vnet_hdr_sz)
 {
-       if (!(features & NETIF_F_GSO_UDP_TUNNEL))
-               return sizeof(struct virtio_net_hdr);
-
-       return TUN_VNET_TNL_SIZE;
+       return min_t(unsigned int, sizeof(struct virtio_net_common_hdr), 
vnet_hdr_sz);
 }
 
 static inline int __tun_vnet_hdr_get(int sz, unsigned int flags,
@@ -122,15 +119,20 @@ static inline int __tun_vnet_hdr_get(int sz, unsigned int 
flags,
                                     struct iov_iter *from,
                                     struct virtio_net_hdr *hdr)
 {
-       unsigned int parsed_size = tun_vnet_parse_size(features);
+       unsigned int parsed_size = tun_vnet_parse_size(sz);
+       u8 tmp[sizeof(struct virtio_net_common_hdr)]; // temp buffer with known 
size
        u16 hdr_len;
 
        if (iov_iter_count(from) < sz)
                return -EINVAL;
 
-       if (!copy_from_iter_full(hdr, parsed_size, from))
+       /* copy parsed size data to the tmp buffer, otherwise compiler will 
complain */
+       if (!copy_from_iter_full(tmp, parsed_size, from))
                return -EFAULT;
 
+       /* now copy the relevant data from tmp to hdr */
+       memcpy(hdr, tmp, min(parsed_size, sizeof(*hdr)));
+
        hdr_len = tun_vnet16_to_cpu(flags, hdr->hdr_len);
 
        if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
@@ -155,14 +157,17 @@ static inline int tun_vnet_hdr_get(int sz, unsigned int 
flags,
 
 static inline int __tun_vnet_hdr_put(int sz, netdev_features_t features,
                                     struct iov_iter *iter,
-                                    const struct virtio_net_hdr *hdr)
+                                    const struct virtio_net_common_hdr *hdr)
 {
-       unsigned int parsed_size = tun_vnet_parse_size(features);
+       unsigned int parsed_size = tun_vnet_parse_size(sz);
+       u8 buf[sizeof(*hdr)];
 
        if (unlikely(iov_iter_count(iter) < sz))
                return -EINVAL;
 
-       if (unlikely(copy_to_iter(hdr, parsed_size, iter) != parsed_size))
+       memcpy(buf, hdr, parsed_size);
+
+       if (unlikely(copy_to_iter(buf, parsed_size, iter) != parsed_size))
                return -EFAULT;
 
        if (iov_iter_zero(sz - parsed_size, iter) != sz - parsed_size)
@@ -174,7 +179,7 @@ static inline int __tun_vnet_hdr_put(int sz, 
netdev_features_t features,
 static inline int tun_vnet_hdr_put(int sz, struct iov_iter *iter,
                                   const struct virtio_net_hdr *hdr)
 {
-       return __tun_vnet_hdr_put(sz, 0, iter, hdr);
+       return __tun_vnet_hdr_put(sz, 0, iter, (struct virtio_net_common_hdr 
*)hdr);
 }
 
 static inline int tun_vnet_hdr_to_skb(unsigned int flags, struct sk_buff *skb,
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index ef6238cb336b9..60ae9392f9876 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -498,17 +498,6 @@ struct padded_vnet_hdr {
        char padding[12];
 };
 
-struct virtio_net_common_hdr {
-       union {
-               struct virtio_net_hdr hdr;
-               struct virtio_net_hdr_mrg_rxbuf mrg_hdr;
-               struct virtio_net_hdr_v1_hash hash_v1_hdr;
-               struct virtio_net_hdr_v1_hash_tunnel tnl_hdr;
-       };
-
-       __le16 tstamp[4];       /* 64-bit timestamp, 2-byte aligned */
-};
-
 static struct virtio_net_common_hdr xsk_hdr;
 
 static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf);
diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
index 39977765b72aa..8fed76755dcb1 100644
--- a/include/uapi/linux/virtio_net.h
+++ b/include/uapi/linux/virtio_net.h
@@ -238,6 +238,17 @@ struct virtio_net_hdr_mrg_rxbuf {
        struct virtio_net_hdr hdr;
        __virtio16 num_buffers; /* Number of merged rx buffers */
 };
+
+struct virtio_net_common_hdr {
+       union {
+               struct virtio_net_hdr hdr;
+               struct virtio_net_hdr_mrg_rxbuf mrg_hdr;
+               struct virtio_net_hdr_v1_hash hash_v1_hdr;
+               struct virtio_net_hdr_v1_hash_tunnel tnl_hdr;
+       };
+
+       __virtio16 tstamp[4];   /* 64-bit timestamp, 2-byte aligned */
+};
 #endif /* ...VIRTIO_NET_NO_LEGACY */
 
 /*

-- 
2.52.0


Reply via email to