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
