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

