Steffen Trumtrar wrote:
> Add optional hardware rx timestamp offload for virtio-net.
>
> Introduce the virtio feature VIRTIO_NET_F_RX_TSTAMP. If negotiated, the
> virtio-net header grows by room for a 64 bit TAI timestamp (header
> size 40 bytes), stored in struct virtio_net_common_hdr. The layout is
> cumulative: the field is at the same offset whether or not the
> features before it were negotiated. The fixed layout needs
> VIRTIO_F_VERSION_1, so the feature is cleared without it.
>
> Implement ndo_hwtstamp_get/set so the timestamping can be enabled
> and disabled at runtime with SIOCSHWTSTAMP. The driver cannot filter
> what the device timestamps, so any rx filter other than NONE becomes
> ALL. The timestamp is read before the packet is handed to the XDP
> program or the stack, and attached to the skb with skb_hwtstamps().
> It is a raw TAI value; there is no PHC.
>
> The header size is negotiated per device and also applies to
> transmitted packets. On transmit, zero the header behind the fields
> the driver fills in, so the device sees a zero tstamp and no stale
> skb->cb or headroom bytes.
>
> On receive in big packet mode, the header is placed in front of the
> data in the first page. Grow padded_vnet_hdr to 48 bytes so the
> 40 byte header still fits there.
>
> Tested in a guest with a patched QEMU:
> guest: ./timestamping eth0 \
> SOF_TIMESTAMPING_RAW_HARDWARE \
> SOF_TIMESTAMPING_RX_HARDWARE
> host: nc -4 -u 192.168.1.1 319
>
> Based on work by Willem de Bruijn.
>
> Signed-off-by: Steffen Trumtrar <[email protected]>
> Assisted-by: Claude:claude-opus-5-5
> ---
> drivers/net/virtio_net.c | 136
> +++++++++++++++++++++++++++++++++++-----
> include/linux/virtio_net.h | 10 +++
> include/uapi/linux/virtio_net.h | 1 +
> 3 files changed, 131 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 17d3e32eed9d6..23af10c613b48 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -20,6 +20,7 @@
> #include <linux/filter.h>
> #include <linux/kernel.h>
> #include <linux/dim.h>
> +#include <linux/unaligned.h>
> #include <net/route.h>
> #include <net/xdp.h>
> #include <net/net_failover.h>
> @@ -416,6 +417,12 @@ struct virtnet_info {
> u32 rss_hash_types_supported;
> u32 rss_hash_types_saved;
>
> + /* Device passes time stamps to the driver */
> + bool has_rx_tstamp;
> + bool hwts_rx_en;
nit: cryptic name. rx_tstamp_supported and rx_tstamp_enabled or so?
> +
> + struct kernel_hwtstamp_config tstamp_config;
> +
> /* Has control virtqueue */
> bool has_cvq;
> +static int virtnet_get_ts_info(struct net_device *dev,
> + struct kernel_ethtool_ts_info *info)
> +{
> + struct virtnet_info *vi = netdev_priv(dev);
> +
> + /* setup default software timestamp */
> + ethtool_op_get_ts_info(dev, info);
> +
> + if (vi->has_rx_tstamp) {
> + info->so_timestamping |=
> + SOF_TIMESTAMPING_RX_HARDWARE |
> + SOF_TIMESTAMPING_RAW_HARDWARE;
> +
> + info->tx_types = BIT(HWTSTAMP_TX_OFF);
> + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
> + BIT(HWTSTAMP_FILTER_ALL);
> + }
`ethtool_op_get_ts_info()` leaves `info->phc_index = -1`, which breaks
Ethtool
Netlink `ETHTOOL_MSG_TSCONFIG_GET` and `ETHTOOL_MSG_TSCONFIG_SET`
(`ethtool --show-hwtstamp-cfg` / `--set-hwtstamp-cfg`):
`tsconfig_prepare_data()`
in `net/ethtool/tsconfig.c` rejects `ts_info.phc_index == -1` with
`-ENODEV`.
Either `net/ethtool/tsconfig.c` must be taught to allow PHC-less
`ndo_hwtstamp_*`
drivers, or the device needs an associated PHC (e.g. `virtio_rtc`).
> @@ -7250,7 +7353,8 @@ static struct virtio_device_id id_table[] = {
> VIRTIO_NET_F_SPEED_DUPLEX, VIRTIO_NET_F_STANDBY, \
> VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL, \
> VIRTIO_NET_F_VQ_NOTF_COAL, \
> - VIRTIO_NET_F_GUEST_HDRLEN, VIRTIO_NET_F_DEVICE_STATS
> + VIRTIO_NET_F_GUEST_HDRLEN, VIRTIO_NET_F_DEVICE_STATS, \
> + VIRTIO_NET_F_RX_TSTAMP
`VIRTNET_FEATURES` expands into `features_legacy[]` as well as `features[]`.
Legacy virtio only supports 32 feature bits (`0..31`) and never has
`VIRTIO_F_VERSION_1`. Move `VIRTIO_NET_F_RX_TSTAMP` (bit 49) into
`features[]`
alongside `VIRTIO_NET_F_*_UDP_TUNNEL_GSO*`.
>
> static unsigned int features[] = {
> VIRTNET_FEATURES,
> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index a6de308b8f25e..2abd8a614a23f 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h
> @@ -17,6 +17,16 @@ struct virtio_net_common_hdr {
> struct virtio_net_hdr_v1_hash hash_v1_hdr;
> struct virtio_net_hdr_v1_hash_tunnel tnl_hdr;
> };
> + /*
> + * The wire layout is fixed and cumulative: a field sits at the same
> + * offset whether or not the features before it were negotiated; unused
> + * bytes are padding. tstamp is thus always at offset 32 and only
> + * present (header size 40) if VIRTIO_NET_F_RX_TSTAMP was negotiated.
> + * The negotiated header size tells how much of this struct is
> + * on the wire. Only ever append new fields at the end.
> + */
> + __u8 reserved[8]; /* outer_nh_offset, ipsec_resource_hdr (not
> implemented) */
> + __le16 tstamp[4]; /* le64 TAI receive time in ns, 0 if none */
> };
>
> static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
> diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
> index 1db45b01532b5..15e8e408d50fb 100644
> --- a/include/uapi/linux/virtio_net.h
> +++ b/include/uapi/linux/virtio_net.h
> @@ -56,6 +56,7 @@
> #define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow
> * Steering */
> #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */
> +#define VIRTIO_NET_F_RX_TSTAMP 49 /* Device sends TAI receive
> time */
> #define VIRTIO_NET_F_DEVICE_STATS 50 /* Device can provide device-level
> statistics. */
> #define VIRTIO_NET_F_VQ_NOTF_COAL 52 /* Device supports virtqueue
> notification coalescing */
> #define VIRTIO_NET_F_NOTF_COAL 53 /* Device supports
> notifications coalescing */
>
> --
> 2.52.0
>