Reference implementation of the device side of VIRTIO_NET_F_RX_TSTAMP: if
the configured vnet_hdr_sz covers the tstamp field, write the current
TAI time into the header when a packet is handed to userspace or vhost.
__tun_vnet_hdr_put() now takes the full struct virtio_net_common_hdr
and copies as much of it as vnet_hdr_sz holds.

This is not intended to be merged. A full test needs a QEMU that knows
the feature and sets the matching vnet_hdr_sz in
virtio_net_set_mrg_rx_bufs(); see
https://github.com/strumtrar/qemu/tree/v11.0.1/virtio-rx-stamps

Signed-off-by: Steffen Trumtrar <[email protected]>
Assisted-by: Claude:claude-opus-5-5
---
 drivers/net/tun.c      | 14 +++++++++-----
 drivers/net/tun_vnet.h | 18 ++++++++++++------
 2 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 242899f7fd071..67699f9fc3d3d 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -57,6 +57,7 @@
 #include <linux/math.h>
 #include <linux/nsproxy.h>
 #include <linux/virtio_net.h>
+#include <linux/unaligned.h>
 #include <linux/rcupdate.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
@@ -2160,12 +2161,16 @@ 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));
+
+               /* The configured header covers the tstamp field */
+               if (vnet_hdr_sz >= offsetofend(struct virtio_net_common_hdr, 
tstamp))
+                       put_unaligned_le64(ktime_get_clocktai_ns(), hdr.tstamp);
+
                ret = tun_vnet_hdr_tnl_from_skb(tun->flags, tun->dev, skb,
-                                               &hdr);
+                                               &hdr.tnl_hdr);
                if (ret)
                        return ret;
 
@@ -2173,9 +2178,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..d4cc56dac98d6 100644
--- a/drivers/net/tun_vnet.h
+++ b/drivers/net/tun_vnet.h
@@ -153,19 +153,21 @@ static inline int tun_vnet_hdr_get(int sz, unsigned int 
flags,
        return __tun_vnet_hdr_get(sz, flags, 0, from, hdr);
 }
 
+/* hdr is fully initialized, copy as much of it as the configured size holds */
 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 put_size = min_t(unsigned int, sizeof(*hdr), sz);
 
-       if (unlikely(iov_iter_count(iter) < sz))
+       if (unlikely(iov_iter_count(iter) < sz ||
+                    sz < tun_vnet_parse_size(features)))
                return -EINVAL;
 
-       if (unlikely(copy_to_iter(hdr, parsed_size, iter) != parsed_size))
+       if (unlikely(copy_to_iter(hdr, put_size, iter) != put_size))
                return -EFAULT;
 
-       if (iov_iter_zero(sz - parsed_size, iter) != sz - parsed_size)
+       if (iov_iter_zero(sz - put_size, iter) != sz - put_size)
                return -EFAULT;
 
        return 0;
@@ -174,7 +176,11 @@ 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);
+       struct virtio_net_common_hdr common_hdr = {};
+
+       common_hdr.hdr = *hdr;
+
+       return __tun_vnet_hdr_put(sz, 0, iter, &common_hdr);
 }
 
 static inline int tun_vnet_hdr_to_skb(unsigned int flags, struct sk_buff *skb,

-- 
2.52.0


Reply via email to