* [PATCH RFC v4 1/6] virtio-net: move virtio_net_common_hdr to virtio_net.h
2026-10-26 8:29 [PATCH RFC v4 0/6] virtio-net: add receive timestamp support Steffen Trumtrar
@ 2026-10-26 8:29 ` Steffen Trumtrar
2026-10-26 8:29 ` [PATCH RFC v4 2/6] virtio-net: support receive timestamp Steffen Trumtrar
` (4 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Steffen Trumtrar @ 2026-10-26 8:29 UTC (permalink / raw)
To: Michael S. Tsirkin, Xuan Zhuo, David S. Miller, Jakub Kicinski,
Paolo Abeni, Richard Cochran, Willem de Bruijn, Andrew Lunn,
Eugenio Pérez, Jason Wang, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Eric Dumazet
Cc: Steffen Trumtrar, virtualization, netdev, linux-kernel, bpf, kvm
tun and vhost need to know the layout and size of the virtio-net
header as the driver uses it, e.g. to fill in fields behind
virtio_net_hdr_v1_hash_tunnel. Move struct virtio_net_common_hdr to
the kernel-internal header that they include already.
No functional change.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Assisted-by: Claude:claude-opus-5-5
---
drivers/net/virtio_net.c | 9 ---------
include/linux/virtio_net.h | 9 +++++++++
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index daab43ac92ce4..17d3e32eed9d6 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -492,15 +492,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;
- };
-};
-
static struct virtio_net_common_hdr xsk_hdr;
static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf);
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index d6466f96cdd00..a6de308b8f25e 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -10,6 +10,15 @@
#include <uapi/linux/tcp.h>
#include <uapi/linux/virtio_net.h>
+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;
+ };
+};
+
static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
{
switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
--
2.52.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH RFC v4 2/6] virtio-net: support receive timestamp
2026-10-26 8:29 [PATCH RFC v4 0/6] virtio-net: add receive timestamp support Steffen Trumtrar
2026-10-26 8:29 ` [PATCH RFC v4 1/6] virtio-net: move virtio_net_common_hdr to virtio_net.h Steffen Trumtrar
@ 2026-10-26 8:29 ` Steffen Trumtrar
2026-10-26 8:51 ` sashiko-bot
2026-10-26 8:29 ` [PATCH RFC v4 3/6] virtio-net: assert header sizes Steffen Trumtrar
` (3 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Steffen Trumtrar @ 2026-10-26 8:29 UTC (permalink / raw)
To: Michael S. Tsirkin, Xuan Zhuo, David S. Miller, Jakub Kicinski,
Paolo Abeni, Richard Cochran, Willem de Bruijn, Andrew Lunn,
Eugenio Pérez, Jason Wang, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Eric Dumazet
Cc: Steffen Trumtrar, virtualization, netdev, linux-kernel, bpf, kvm
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 <s.trumtrar@pengutronix.de>
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;
+
+ struct kernel_hwtstamp_config tstamp_config;
+
/* Has control virtqueue */
bool has_cvq;
@@ -483,14 +490,15 @@ static_assert(offsetof(struct virtnet_info, rss_trailer.hash_key_data) ==
offsetof(struct virtnet_info, rss_hash_key_data));
struct padded_vnet_hdr {
- struct virtio_net_hdr_v1_hash hdr;
+ struct virtio_net_common_hdr hdr;
/*
* hdr is in a separate sg buffer, and data sg buffer shares same page
* with this header sg. This padding makes next sg 16 byte aligned
* after the header.
*/
- char padding[12];
+ char padding[8];
};
+static_assert(sizeof(struct padded_vnet_hdr) % 16 == 0);
static struct virtio_net_common_hdr xsk_hdr;
@@ -501,7 +509,7 @@ static int virtnet_xdp_handler(struct bpf_prog *xdp_prog, struct xdp_buff *xdp,
unsigned int *xdp_xmit,
struct virtnet_rq_stats *stats);
static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *rq,
- struct sk_buff *skb, u8 flags);
+ struct sk_buff *skb, u8 flags, ktime_t tstamp);
static struct sk_buff *virtnet_skb_append_frag(struct receive_queue *rq,
struct sk_buff *head_skb,
struct sk_buff *curr_skb,
@@ -1274,14 +1282,25 @@ static struct sk_buff *virtnet_receive_xsk_merge(struct net_device *dev, struct
return NULL;
}
+static inline ktime_t virtnet_record_rx_tstamp(const struct virtnet_info *vi,
+ const struct virtio_net_common_hdr *hdr)
+{
+ if (!READ_ONCE(vi->hwts_rx_en))
+ return 0;
+
+ return ns_to_ktime(get_unaligned_le64(hdr->tstamp));
+}
+
static void virtnet_receive_xsk_buf(struct virtnet_info *vi, struct receive_queue *rq,
void *buf, u32 len,
unsigned int *xdp_xmit,
struct virtnet_rq_stats *stats)
{
struct net_device *dev = vi->dev;
+ struct virtio_net_common_hdr *hdr;
struct sk_buff *skb = NULL;
struct xdp_buff *xdp;
+ ktime_t tstamp;
u8 flags;
len -= vi->hdr_len;
@@ -1299,7 +1318,9 @@ static void virtnet_receive_xsk_buf(struct virtnet_info *vi, struct receive_queu
return;
}
- flags = ((struct virtio_net_common_hdr *)(xdp->data - vi->hdr_len))->hdr.flags;
+ hdr = (struct virtio_net_common_hdr *)(xdp->data - vi->hdr_len);
+ flags = hdr->hdr.flags;
+ tstamp = virtnet_record_rx_tstamp(vi, hdr);
if (!vi->mergeable_rx_bufs)
skb = virtnet_receive_xsk_small(dev, vi, rq, xdp, xdp_xmit, stats);
@@ -1307,7 +1328,7 @@ static void virtnet_receive_xsk_buf(struct virtnet_info *vi, struct receive_queu
skb = virtnet_receive_xsk_merge(dev, vi, rq, xdp, xdp_xmit, stats);
if (skb)
- virtnet_receive_done(vi, rq, skb, flags);
+ virtnet_receive_done(vi, rq, skb, flags, tstamp);
}
static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue *rq,
@@ -2489,7 +2510,7 @@ static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash,
}
static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *rq,
- struct sk_buff *skb, u8 flags)
+ struct sk_buff *skb, u8 flags, ktime_t tstamp)
{
struct virtio_net_common_hdr *hdr;
struct net_device *dev = vi->dev;
@@ -2498,6 +2519,9 @@ static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *
if (dev->features & NETIF_F_RXHASH && vi->has_rss_hash_report)
virtio_skb_set_hash(&hdr->hash_v1_hdr, skb);
+ if (tstamp)
+ skb_hwtstamps(skb)->hwtstamp = tstamp;
+
hdr->hdr.flags = flags;
if (virtio_net_handle_csum_offload(skb, &hdr->hdr, vi->rx_tnl_csum)) {
net_warn_ratelimited("%s: bad csum: flags: %x, gso_type: %x rx_tnl_csum %d\n",
@@ -2536,8 +2560,10 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
unsigned int *xdp_xmit,
struct virtnet_rq_stats *stats)
{
+ struct virtio_net_common_hdr *hdr;
struct net_device *dev = vi->dev;
struct sk_buff *skb;
+ ktime_t tstamp;
u8 flags;
if (unlikely(len < vi->hdr_len + ETH_HLEN)) {
@@ -2567,23 +2593,27 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
*/
if (vi->mergeable_rx_bufs) {
- flags = ((struct virtio_net_common_hdr *)buf)->hdr.flags;
+ hdr = buf;
+ flags = hdr->hdr.flags;
+ tstamp = virtnet_record_rx_tstamp(vi, hdr);
skb = receive_mergeable(dev, vi, rq, buf, ctx, len, xdp_xmit,
stats);
} else if (vi->big_packets) {
- void *p = page_address((struct page *)buf);
-
- flags = ((struct virtio_net_common_hdr *)p)->hdr.flags;
+ hdr = page_address((struct page *)buf);
+ flags = hdr->hdr.flags;
+ tstamp = virtnet_record_rx_tstamp(vi, hdr);
skb = receive_big(dev, vi, rq, buf, len, stats);
} else {
- flags = ((struct virtio_net_common_hdr *)buf)->hdr.flags;
+ hdr = buf;
+ flags = hdr->hdr.flags;
+ tstamp = virtnet_record_rx_tstamp(vi, hdr);
skb = receive_small(dev, vi, rq, buf, ctx, len, xdp_xmit, stats);
}
if (unlikely(!skb))
return;
- virtnet_receive_done(vi, rq, skb, flags);
+ virtnet_receive_done(vi, rq, skb, flags, tstamp);
}
static int virtnet_rq_submit(struct receive_queue *rq, char *buf,
@@ -3303,6 +3333,10 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
false, feature_hdrlen))
return -EPROTO;
+ /* Don't leak skb->cb or headroom; the device expects tstamp to be 0 */
+ if (hdr_len > sizeof(*hdr))
+ memset((u8 *)hdr + sizeof(*hdr), 0, hdr_len - sizeof(*hdr));
+
if (vi->mergeable_rx_bufs)
hdr->hash_hdr.hdr.num_buffers = 0;
@@ -5520,6 +5554,27 @@ static int virtnet_get_per_queue_coalesce(struct net_device *dev,
return 0;
}
+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);
+ }
+
+ return 0;
+}
+
static void virtnet_init_settings(struct net_device *dev)
{
struct virtnet_info *vi = netdev_priv(dev);
@@ -5615,7 +5670,7 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
.get_ethtool_stats = virtnet_get_ethtool_stats,
.set_channels = virtnet_set_channels,
.get_channels = virtnet_get_channels,
- .get_ts_info = ethtool_op_get_ts_info,
+ .get_ts_info = virtnet_get_ts_info,
.get_link_ksettings = virtnet_get_link_ksettings,
.set_link_ksettings = virtnet_set_link_ksettings,
.set_coalesce = virtnet_set_coalesce,
@@ -6214,6 +6269,43 @@ static void virtnet_tx_timeout(struct net_device *dev, unsigned int txqueue)
jiffies_to_usecs(jiffies - READ_ONCE(txq->trans_start)));
}
+static int virtnet_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct virtnet_info *vi = netdev_priv(dev);
+
+ if (!vi->has_rx_tstamp)
+ return -EOPNOTSUPP;
+
+ *config = vi->tstamp_config;
+
+ return 0;
+}
+
+static int virtnet_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct virtnet_info *vi = netdev_priv(dev);
+
+ if (!vi->has_rx_tstamp)
+ return -EOPNOTSUPP;
+
+ if (config->tx_type != HWTSTAMP_TX_OFF) {
+ NL_SET_ERR_MSG_MOD(extack, "TX timestamping is not supported");
+ return -ERANGE;
+ }
+
+ /* The driver cannot filter, any filter becomes ALL */
+ if (config->rx_filter != HWTSTAMP_FILTER_NONE)
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+
+ WRITE_ONCE(vi->hwts_rx_en, config->rx_filter == HWTSTAMP_FILTER_ALL);
+ vi->tstamp_config = *config;
+
+ return 0;
+}
+
static int virtnet_init_irq_moder(struct virtnet_info *vi)
{
u8 profile_flags = 0, coal_flags = 0;
@@ -6274,6 +6366,8 @@ static const struct net_device_ops virtnet_netdev = {
.ndo_get_phys_port_name = virtnet_get_phys_port_name,
.ndo_set_features = virtnet_set_features,
.ndo_tx_timeout = virtnet_tx_timeout,
+ .ndo_hwtstamp_set = virtnet_hwtstamp_set,
+ .ndo_hwtstamp_get = virtnet_hwtstamp_get,
};
static void virtnet_config_changed_work(struct work_struct *work)
@@ -6685,6 +6779,10 @@ static int virtnet_validate(struct virtio_device *vdev)
__virtio_clear_bit(vdev, VIRTIO_NET_F_STANDBY);
}
+ /* The fixed header layout with tstamp at offset 32 needs VERSION_1 */
+ if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
+ __virtio_clear_bit(vdev, VIRTIO_NET_F_RX_TSTAMP);
+
return 0;
}
@@ -6880,6 +6978,9 @@ static int virtnet_probe(struct virtio_device *vdev)
if (virtio_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT))
vi->has_rss_hash_report = true;
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_RX_TSTAMP))
+ vi->has_rx_tstamp = true;
+
if (virtio_has_feature(vdev, VIRTIO_NET_F_RSS)) {
vi->has_rss = true;
@@ -6913,8 +7014,10 @@ static int virtnet_probe(struct virtio_device *vdev)
dev->xdp_metadata_ops = &virtnet_xdp_metadata_ops;
}
- if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO) ||
- virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO))
+ if (vi->has_rx_tstamp)
+ vi->hdr_len = offsetofend(struct virtio_net_common_hdr, tstamp);
+ else if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO) ||
+ virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO))
vi->hdr_len = sizeof(struct virtio_net_hdr_v1_hash_tunnel);
else if (vi->has_rss_hash_report)
vi->hdr_len = sizeof(struct virtio_net_hdr_v1_hash);
@@ -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
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
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH RFC v4 2/6] virtio-net: support receive timestamp
2026-10-26 8:29 ` [PATCH RFC v4 2/6] virtio-net: support receive timestamp Steffen Trumtrar
@ 2026-10-26 8:51 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-26 8:51 UTC (permalink / raw)
To: Steffen Trumtrar
Cc: virtualization, bpf, kvm, Michael S. Tsirkin, Eugenio Perez
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] virtio-net: incomplete TX header zeroing in xmit_skb() leaks skb memory
--
commit 33c9a34145febcaa0e50429953d991f89f3ffb19
Author: Steffen Trumtrar <s.trumtrar@pengutronix.de>
virtio-net: support receive timestamp
This commit adds optional hardware RX timestamp offload support via the
VIRTIO_NET_F_RX_TSTAMP feature, extending struct virtio_net_common_hdr to
40 bytes to carry a 64-bit TAI receive timestamp and wiring up the
ethtool and netdev hardware timestamping callbacks.
> 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
[ ... ]
> @@ -3303,6 +3333,10 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
> false, feature_hdrlen))
> return -EPROTO;
>
> + /* Don't leak skb->cb or headroom; the device expects tstamp to be 0 */
> + if (hdr_len > sizeof(*hdr))
> + memset((u8 *)hdr + sizeof(*hdr), 0, hdr_len - sizeof(*hdr));
> +
> if (vi->mergeable_rx_bufs)
> hdr->hash_hdr.hdr.num_buffers = 0;
[Severity: Medium]
Can this leave bytes in the middle of the transmitted header uninitialized
and leak stale skb->cb or headroom memory to the device?
When VIRTIO_NET_F_RX_TSTAMP is negotiated, virtnet_probe() sets vi->hdr_len
to 40 bytes (offsetofend(struct virtio_net_common_hdr, tstamp)), even if
hash reporting, UDP tunnel GSO, or mergeable RX buffers were not
negotiated.
In xmit_skb(), hdr has type struct virtio_net_hdr_v1_hash_tunnel *
(sizeof(*hdr) is 24 bytes) and points either into skb headroom or to
&skb_vnet_common_hdr(skb)->tnl_hdr (aliasing skb->cb). When xmit_skb()
calls virtio_net_hdr_tnl_from_skb() for a packet without UDP tunnel GSO, it
returns early after calling virtio_net_hdr_from_skb():
include/linux/virtio_net.h:virtio_net_hdr_tnl_from_skb() {
tnl_gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM);
if (!tnl_gso_type) {
ret = virtio_net_hdr_from_skb(skb, hdr, little_endian,
has_data_valid, vlan_hlen);
if (ret)
return ret;
if (feature_hdrlen && hdr->hdr_len)
__virtio_net_set_hdrlen(skb, hdr, little_endian);
return ret;
}
}
Because virtio_net_hdr_from_skb() takes a struct virtio_net_hdr * argument,
its memset(hdr, 0, sizeof(*hdr)) only zeroes the first 10 bytes (offsets
0..9).
The new memset((u8 *)hdr + sizeof(*hdr), 0, hdr_len - sizeof(*hdr)) starts
at offset 24 and zeroes offsets 24..39, while hdr->hash_hdr.hdr.num_buffers
= 0 only zeroes offsets 10..11 when vi->mergeable_rx_bufs is set. As a
result, offsets 12..23 (hash_value_lo, hash_value_hi, hash_report, padding,
outer_th_offset, inner_nh_offset) and offsets 10..11 (when
!vi->mergeable_rx_bufs) are never zeroed before all 40 bytes are mapped into
sq->sg and passed to the device.
Additionally, when !can_push, hdr points to the 24-byte tnl_hdr member
sub-object inside struct virtio_net_common_hdr, so writing past sizeof(*hdr)
via hdr writes across the sub-object boundary into sibling fields of
struct virtio_net_common_hdr.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-v6-7-topic-virtio-net-ptp-v4-0-008af03cd43b@pengutronix.de?part=2
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v4 3/6] virtio-net: assert header sizes
2026-10-26 8:29 [PATCH RFC v4 0/6] virtio-net: add receive timestamp support Steffen Trumtrar
2026-10-26 8:29 ` [PATCH RFC v4 1/6] virtio-net: move virtio_net_common_hdr to virtio_net.h Steffen Trumtrar
2026-10-26 8:29 ` [PATCH RFC v4 2/6] virtio-net: support receive timestamp Steffen Trumtrar
@ 2026-10-26 8:29 ` Steffen Trumtrar
2026-10-26 8:29 ` [PATCH RFC v4 4/6] vhost-net: support VIRTIO_NET_F_RX_TSTAMP Steffen Trumtrar
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Steffen Trumtrar @ 2026-10-26 8:29 UTC (permalink / raw)
To: Michael S. Tsirkin, Xuan Zhuo, David S. Miller, Jakub Kicinski,
Paolo Abeni, Richard Cochran, Willem de Bruijn, Andrew Lunn,
Eugenio Pérez, Jason Wang, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Eric Dumazet
Cc: Steffen Trumtrar, virtualization, netdev, linux-kernel, bpf, kvm
The header is cast from skb->cb and from device buffers, so its layout
must not change silently. Add static_asserts for the offset of tstamp,
the total size, the alignment, and that the header fits into skb->cb.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Assisted-by: Claude:claude-opus-5-5
---
include/linux/virtio_net.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 2abd8a614a23f..e7776a37a486b 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -29,6 +29,17 @@ struct virtio_net_common_hdr {
__le16 tstamp[4]; /* le64 TAI receive time in ns, 0 if none */
};
+/*
+ * The header is cast from skb->cb and from device buffers, the layout must not
+ * change silently.
+ */
+static_assert(offsetof(struct virtio_net_common_hdr, tstamp) == 32);
+static_assert(sizeof(struct virtio_net_common_hdr) ==
+ offsetofend(struct virtio_net_common_hdr, tstamp));
+static_assert(__alignof__(struct virtio_net_common_hdr) == 2);
+/* virtio_net_common_hdr must fit into skb->cb */
+static_assert(sizeof(struct virtio_net_common_hdr) <= sizeof_field(struct sk_buff, cb));
+
static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
{
switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
--
2.52.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH RFC v4 4/6] vhost-net: support VIRTIO_NET_F_RX_TSTAMP
2026-10-26 8:29 [PATCH RFC v4 0/6] virtio-net: add receive timestamp support Steffen Trumtrar
` (2 preceding siblings ...)
2026-10-26 8:29 ` [PATCH RFC v4 3/6] virtio-net: assert header sizes Steffen Trumtrar
@ 2026-10-26 8:29 ` Steffen Trumtrar
2026-10-26 8:51 ` sashiko-bot
2026-10-26 8:29 ` [PATCH RFC v4 5/6] virtio-net: add XDP rx timestamp metadata Steffen Trumtrar
2026-10-26 8:29 ` [PATCH RFC v4 6/6] tun: add rx timestamp to the vnet header Steffen Trumtrar
5 siblings, 1 reply; 10+ messages in thread
From: Steffen Trumtrar @ 2026-10-26 8:29 UTC (permalink / raw)
To: Michael S. Tsirkin, Xuan Zhuo, David S. Miller, Jakub Kicinski,
Paolo Abeni, Richard Cochran, Willem de Bruijn, Andrew Lunn,
Eugenio Pérez, Jason Wang, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Eric Dumazet
Cc: Steffen Trumtrar, virtualization, netdev, linux-kernel, bpf, kvm
Advertise VIRTIO_NET_F_RX_TSTAMP to userspace and size the vnet header
for it, so a guest can negotiate the feature with a vhost-net backed
tap device.
The header is 40 bytes if the feature is negotiated. Whoever writes
the header (tun, or vhost itself with VHOST_NET_F_VIRTIO_NET_HDR) has
to fill in the timestamp; vhost only reserves the room. If vhost
writes the header, it zeroes everything behind num_buffers, so the
timestamp is 0, which the guest ignores.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Assisted-by: Claude:claude-opus-5-5
---
drivers/vhost/net.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 2cc730729e08a..2a38ad0ea1af2 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -77,7 +77,8 @@ static const int vhost_net_bits[] = {
VIRTIO_F_RING_RESET,
VIRTIO_F_IN_ORDER,
VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO,
- VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO
+ VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO,
+ VIRTIO_NET_F_RX_TSTAMP
};
enum {
@@ -1282,6 +1283,16 @@ static void handle_rx(struct vhost_net *net)
vhost_discard_vq_desc(vq, headcount, ndesc);
goto out;
}
+ /* Zero the rest of a header supplied by vhost, e.g. tstamp */
+ if (unlikely(vhost_hlen > sizeof(hdr) + sizeof(num_buffers))) {
+ size_t rest = vhost_hlen - sizeof(hdr) - sizeof(num_buffers);
+
+ if (iov_iter_zero(rest, &fixup) != rest) {
+ vq_err(vq, "Failed vnet_hdr zeroing");
+ vhost_discard_vq_desc(vq, headcount, ndesc);
+ goto out;
+ }
+ }
nvq->done_idx += headcount;
count += in_order ? 1 : headcount;
if (nvq->done_idx > VHOST_NET_BATCH) {
@@ -1688,6 +1699,10 @@ static int vhost_net_set_features(struct vhost_net *n, const u64 *features)
VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO))
hdr_len = sizeof(struct virtio_net_hdr_v1_hash_tunnel);
+ /* The header layout is cumulative, tstamp sits behind all other fields. */
+ if (virtio_features_test_bit(features, VIRTIO_NET_F_RX_TSTAMP))
+ hdr_len = offsetofend(struct virtio_net_common_hdr, tstamp);
+
if (virtio_features_test_bit(features, VHOST_NET_F_VIRTIO_NET_HDR)) {
/* vhost provides vnet_hdr */
vhost_hlen = hdr_len;
--
2.52.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH RFC v4 4/6] vhost-net: support VIRTIO_NET_F_RX_TSTAMP
2026-10-26 8:29 ` [PATCH RFC v4 4/6] vhost-net: support VIRTIO_NET_F_RX_TSTAMP Steffen Trumtrar
@ 2026-10-26 8:51 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-26 8:51 UTC (permalink / raw)
To: Steffen Trumtrar
Cc: virtualization, bpf, kvm, Michael S. Tsirkin, Eugenio Perez
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] vhost-net: incomplete header zeroing in handle_rx() without num_buffers
--
commit a70532fad1c69345268d35c3f489b52f8abb6392
Author: Steffen Trumtrar <s.trumtrar@pengutronix.de>
vhost-net: support VIRTIO_NET_F_RX_TSTAMP
This commit advertises VIRTIO_NET_F_RX_TSTAMP in vhost-net, sizes the vnet
header to include the timestamp field when negotiated, and zeroes the extra
header bytes in handle_rx() when vhost supplies the header itself.
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index 2cc730729e08a..2a38ad0ea1af2 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
[ ... ]
> @@ -1282,6 +1283,16 @@ static void handle_rx(struct vhost_net *net)
> vhost_discard_vq_desc(vq, headcount, ndesc);
> goto out;
> }
> + /* Zero the rest of a header supplied by vhost, e.g. tstamp */
> + if (unlikely(vhost_hlen > sizeof(hdr) + sizeof(num_buffers))) {
> + size_t rest = vhost_hlen - sizeof(hdr) - sizeof(num_buffers);
> +
> + if (iov_iter_zero(rest, &fixup) != rest) {
> + vq_err(vq, "Failed vnet_hdr zeroing");
> + vhost_discard_vq_desc(vq, headcount, ndesc);
> + goto out;
> + }
> + }
[Severity: Medium]
Can this leave the final 2 bytes of the vhost-supplied header unzeroed in the
guest RX buffer when neither VIRTIO_F_VERSION_1 nor VIRTIO_NET_F_MRG_RXBUF is
negotiated?
In vhost_net_set_features(), VIRTIO_NET_F_RX_TSTAMP (or UDP tunnel GSO) can
be configured alongside VHOST_NET_F_VIRTIO_NET_HDR without requiring
VIRTIO_F_VERSION_1 or VIRTIO_NET_F_MRG_RXBUF:
drivers/vhost/net.c:vhost_net_set_features() {
...
if (virtio_features_test_bit(features, VIRTIO_NET_F_RX_TSTAMP))
hdr_len = offsetofend(struct virtio_net_common_hdr, tstamp);
if (virtio_features_test_bit(features, VHOST_NET_F_VIRTIO_NET_HDR)) {
/* vhost provides vnet_hdr */
vhost_hlen = hdr_len;
...
}
When handle_rx() runs with this configuration, set_num_buffers is false and
writing num_buffers to fixup is skipped:
drivers/vhost/net.c:handle_rx() {
...
mergeable = vhost_has_feature(vq, VIRTIO_NET_F_MRG_RXBUF);
set_num_buffers = mergeable ||
vhost_has_feature(vq, VIRTIO_F_VERSION_1);
...
if (likely(set_num_buffers) &&
copy_to_iter(&num_buffers, sizeof num_buffers,
&fixup) != sizeof num_buffers) {
...
}
Because msg.msg_iter was advanced by vhost_hlen before recvmsg(), the packet
payload starts at offset vhost_hlen, while fixup only advanced by sizeof(hdr)
(10 bytes). Subtracting sizeof(num_buffers) from rest unconditionally causes
iov_iter_zero() to stop 2 bytes short of the payload, leaving the upper 16
bits of tstamp in the guest buffer uninitialized.
Should rest only subtract sizeof(num_buffers) when set_num_buffers is true
(or should vhost_net_set_features() require VIRTIO_F_VERSION_1 for these
extended header features)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-v6-7-topic-virtio-net-ptp-v4-0-008af03cd43b@pengutronix.de?part=4
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v4 5/6] virtio-net: add XDP rx timestamp metadata
2026-10-26 8:29 [PATCH RFC v4 0/6] virtio-net: add receive timestamp support Steffen Trumtrar
` (3 preceding siblings ...)
2026-10-26 8:29 ` [PATCH RFC v4 4/6] vhost-net: support VIRTIO_NET_F_RX_TSTAMP Steffen Trumtrar
@ 2026-10-26 8:29 ` Steffen Trumtrar
2026-10-26 9:04 ` sashiko-bot
2026-10-26 8:29 ` [PATCH RFC v4 6/6] tun: add rx timestamp to the vnet header Steffen Trumtrar
5 siblings, 1 reply; 10+ messages in thread
From: Steffen Trumtrar @ 2026-10-26 8:29 UTC (permalink / raw)
To: Michael S. Tsirkin, Xuan Zhuo, David S. Miller, Jakub Kicinski,
Paolo Abeni, Richard Cochran, Willem de Bruijn, Andrew Lunn,
Eugenio Pérez, Jason Wang, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Eric Dumazet
Cc: Steffen Trumtrar, virtualization, netdev, linux-kernel, bpf, kvm
For the skb, the timestamp is read from the header before the XDP
program runs, so XDP_PASS keeps it even if the program changes the
packet. XDP therefore works with rx timestamping enabled.
Add xmo_rx_timestamp so XDP programs can read the timestamp with
bpf_xdp_metadata_rx_timestamp(). It returns -ENODATA if timestamping
is not enabled or the device did not timestamp the packet. Install the
metadata ops also if only VIRTIO_NET_F_RX_TSTAMP is negotiated;
rx_hash returns -ENODATA then.
Like virtnet_xdp_rx_hash(), the callback finds the header relative to
xdp->data, so it has to be called before bpf_xdp_adjust_head().
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Assisted-by: Claude:claude-opus-5-5
---
drivers/net/virtio_net.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 23af10c613b48..e79b65653fad7 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -6848,8 +6848,30 @@ static int virtnet_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash,
return 0;
}
+static int virtnet_xdp_rx_timestamp(const struct xdp_md *_ctx, u64 *timestamp)
+{
+ const struct xdp_buff *xdp = (void *)_ctx;
+ const struct virtio_net_common_hdr *hdr;
+ struct virtnet_info *vi;
+ u64 tstamp;
+
+ vi = netdev_priv(xdp->rxq->dev);
+ if (!READ_ONCE(vi->hwts_rx_en))
+ return -ENODATA;
+
+ hdr = (const struct virtio_net_common_hdr *)(xdp->data - vi->hdr_len);
+ tstamp = get_unaligned_le64(hdr->tstamp);
+ /* 0: the device did not timestamp this packet */
+ if (!tstamp)
+ return -ENODATA;
+
+ *timestamp = tstamp;
+ return 0;
+}
+
static const struct xdp_metadata_ops virtnet_xdp_metadata_ops = {
.xmo_rx_hash = virtnet_xdp_rx_hash,
+ .xmo_rx_timestamp = virtnet_xdp_rx_timestamp,
};
static int virtnet_probe(struct virtio_device *vdev)
@@ -7014,6 +7036,10 @@ static int virtnet_probe(struct virtio_device *vdev)
dev->xdp_metadata_ops = &virtnet_xdp_metadata_ops;
}
+ /* rx_hash returns -ENODATA without NETIF_F_RXHASH */
+ if (vi->has_rx_tstamp)
+ dev->xdp_metadata_ops = &virtnet_xdp_metadata_ops;
+
if (vi->has_rx_tstamp)
vi->hdr_len = offsetofend(struct virtio_net_common_hdr, tstamp);
else if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO) ||
--
2.52.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH RFC v4 5/6] virtio-net: add XDP rx timestamp metadata
2026-10-26 8:29 ` [PATCH RFC v4 5/6] virtio-net: add XDP rx timestamp metadata Steffen Trumtrar
@ 2026-10-26 9:04 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-26 9:04 UTC (permalink / raw)
To: Steffen Trumtrar
Cc: virtualization, bpf, kvm, Michael S. Tsirkin, Eugenio Perez
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] virtio-net: virtnet_xdp_rx_timestamp() reads header overlapping XDP data_meta
--
commit 232ef11034e0a6c3f2481ceaa1291cc9268f328c
Author: Steffen Trumtrar <s.trumtrar@pengutronix.de>
virtio-net: add XDP rx timestamp metadata
This commit implements the xmo_rx_timestamp XDP metadata callback in
virtio_net so XDP programs can read hardware receive timestamps via
bpf_xdp_metadata_rx_timestamp(). It also installs virtnet_xdp_metadata_ops
when VIRTIO_NET_F_RX_TSTAMP is negotiated.
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 23af10c613b48..e79b65653fad7 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -6848,8 +6848,30 @@ static int virtnet_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash,
> return 0;
> }
>
> +static int virtnet_xdp_rx_timestamp(const struct xdp_md *_ctx, u64 *timestamp)
> +{
> + const struct xdp_buff *xdp = (void *)_ctx;
> + const struct virtio_net_common_hdr *hdr;
> + struct virtnet_info *vi;
> + u64 tstamp;
> +
> + vi = netdev_priv(xdp->rxq->dev);
> + if (!READ_ONCE(vi->hwts_rx_en))
> + return -ENODATA;
> +
> + hdr = (const struct virtio_net_common_hdr *)(xdp->data - vi->hdr_len);
> + tstamp = get_unaligned_le64(hdr->tstamp);
[Severity: High]
Can reading hdr->tstamp in place from xdp->data - vi->hdr_len collide with
the XDP data_meta region when an XDP program uses bpf_xdp_adjust_meta()?
When VIRTIO_NET_F_RX_TSTAMP is negotiated and hardware rx timestamping is
enabled via virtnet_hwtstamp_set(), virtnet_probe() sets vi->hdr_len to 40
bytes:
drivers/net/virtio_net.c:virtnet_probe() {
if (vi->has_rx_tstamp)
vi->hdr_len = offsetofend(struct virtio_net_common_hdr, tstamp);
}
In the XDP receive paths (receive_small_xdp(), receive_mergeable_xdp() ->
virtnet_build_xdp_buff_mrg(), and virtnet_receive_xsk_buf()), the
virtio_net_common_hdr remains in the headroom immediately preceding
xdp->data, spanning [xdp->data - 40, xdp->data). Within that header,
hdr->tstamp sits at [xdp->data - 8, xdp->data) and hdr->hash_v1_hdr sits at
[xdp->data - 28, xdp->data - 20).
Documentation/networking/xdp-rx-metadata.rst notes that drivers must move
device-reserved metadata out of the [xdp->data_meta, xdp->data) area before
running the XDP program because XDP programs call bpf_xdp_adjust_meta()
first and then populate their custom metadata structure in [xdp->data_meta,
xdp->data) using the xdp_metadata kfuncs.
For example, in tools/testing/selftests/bpf/progs/xdp_hw_metadata.c:rx(),
where struct xdp_meta is 32 bytes:
err = bpf_xdp_adjust_meta(ctx, -(int)sizeof(struct xdp_meta));
if (err)
return XDP_PASS;
data = (void *)(long)ctx->data;
meta = (void *)(long)ctx->data_meta;
if (meta + 1 > data)
return XDP_PASS;
meta->hint_valid = 0;
meta->xdp_timestamp = bpf_ktime_get_tai_ns();
err = bpf_xdp_metadata_rx_timestamp(ctx, &meta->rx_timestamp);
if (err)
meta->rx_timestamp_err = err;
else
meta->hint_valid |= XDP_META_FIELD_TS;
err = bpf_xdp_metadata_rx_hash(ctx, &meta->rx_hash,
&meta->rx_hash_type);
Here, initializing meta->hint_valid = 0 writes to [xdp->data - 4,
xdp->data), which overwrites the upper 32 bits of hdr->tstamp before
bpf_xdp_metadata_rx_timestamp() calls virtnet_xdp_rx_timestamp(). Then,
writing meta->xdp_timestamp and meta->rx_timestamp at [xdp->data - 32,
xdp->data - 16) overwrites hdr->hash_v1_hdr at [xdp->data - 28,
xdp->data - 22) before bpf_xdp_metadata_rx_hash() calls
virtnet_xdp_rx_hash().
Unlike the skb paths (receive_buf() and virtnet_receive_xsk_buf()), which
save the timestamp via virtnet_record_rx_tstamp() before invoking the XDP
program, virtnet_xdp_rx_timestamp() reads from the live buffer during XDP
execution. Should the driver extract or preserve the rx metadata outside
the [xdp->data_meta, xdp->data) region before running the XDP program?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-v6-7-topic-virtio-net-ptp-v4-0-008af03cd43b@pengutronix.de?part=5
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v4 6/6] tun: add rx timestamp to the vnet header
2026-10-26 8:29 [PATCH RFC v4 0/6] virtio-net: add receive timestamp support Steffen Trumtrar
` (4 preceding siblings ...)
2026-10-26 8:29 ` [PATCH RFC v4 5/6] virtio-net: add XDP rx timestamp metadata Steffen Trumtrar
@ 2026-10-26 8:29 ` Steffen Trumtrar
5 siblings, 0 replies; 10+ messages in thread
From: Steffen Trumtrar @ 2026-10-26 8:29 UTC (permalink / raw)
To: Michael S. Tsirkin, Xuan Zhuo, David S. Miller, Jakub Kicinski,
Paolo Abeni, Richard Cochran, Willem de Bruijn, Andrew Lunn,
Eugenio Pérez, Jason Wang, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Eric Dumazet
Cc: Steffen Trumtrar, virtualization, netdev, linux-kernel, bpf, kvm
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 <s.trumtrar@pengutronix.de>
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
^ permalink raw reply related [flat|nested] 10+ messages in thread