* [PATCH net v5 1/7] net: account for ingress-resolved virtio net hdr state
[not found] <cover.v5.git.mst@redhat.com>
@ 2026-10-22 12:57 ` Michael S. Tsirkin
2026-10-22 14:37 ` Willem de Bruijn
2026-10-22 12:58 ` [PATCH net v5 2/7] um: vector: trim skbs before verifying the header Michael S. Tsirkin
1 sibling, 1 reply; 3+ messages in thread
From: Michael S. Tsirkin @ 2026-10-22 12:57 UTC (permalink / raw)
To: linux-kernel
Cc: Willem de Bruijn, Chris Arges, Björn Töpel, Phil Sutter,
Jason Wang, Eugenio Pérez, Xuan Zhuo, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, virtualization, Max Krasnyansky, Rusty Russell,
Richard Weinberger, Anton Ivanov, Johannes Berg, Simon Horman,
linux-um
The following patch will add ingress resolution of uncommon virtio net
hdr checksum metadata (software checksum completion at ingress) which
adds performance overhead to the packet processing.
We should make it visible, so a hypothetical workload relying on
uncommon metadata can be distinguished from none at all. This way issues
in the parsing will be reported and not quietly pay the host-CPU
checksum overhead instead of offload.
Flip every call to virtio_net_hdr_to_skb to test for negative errno
only. The um vector raw transport even ignored the return value
altogether: rejected packets went on into the stack with their
metadata unvalidated, so check it there as well.
Have tun count these events per device as rx_csum_sw, exposed via
ethtool -S.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
Notes (changelog):
v4->v5:
count on tun only: dropped the tap, macvtap, ipvtap and
virtio_net counters (Willem de Bruijn)
arch/um/drivers/vector_transports.c | 4 ++-
drivers/net/tap.c | 4 +--
drivers/net/tun.c | 41 +++++++++++++++++++++++++++--
drivers/net/virtio_net.c | 2 +-
net/packet/af_packet.c | 4 +--
5 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/arch/um/drivers/vector_transports.c b/arch/um/drivers/vector_transports.c
index e1fb2a76fdff..8b7548458663 100644
--- a/arch/um/drivers/vector_transports.c
+++ b/arch/um/drivers/vector_transports.c
@@ -210,7 +210,9 @@ static int raw_verify_header(
return 1;
skb_set_network_header(skb, ETH_HLEN);
- virtio_net_hdr_to_skb(skb, vheader, virtio_legacy_is_little_endian());
+ if (virtio_net_hdr_to_skb(skb, vheader,
+ virtio_legacy_is_little_endian()) < 0)
+ return -1;
return 0;
}
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index ff67d99deb39..73e883394446 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -649,7 +649,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
if (vnet_hdr_len) {
err = tun_vnet_hdr_to_skb(q->flags, skb, &vnet_hdr);
- if (err) {
+ if (err < 0) {
rcu_read_unlock();
drop_reason = SKB_DROP_REASON_DEV_HDR;
goto err_kfree;
@@ -1084,7 +1084,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
if (vnet_hdr_len) {
err = tun_vnet_hdr_to_skb(q->flags, skb, gso);
- if (err) {
+ if (err < 0) {
rcu_read_unlock();
goto err_kfree;
}
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 242899f7fd07..197fb8726f8d 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -210,6 +210,7 @@ struct tun_struct {
u32 flow_count;
u32 rx_batched;
atomic_long_t rx_frame_errors;
+ atomic64_t rx_csum_sw;
struct bpf_prog __rcu *xdp_prog;
struct tun_prog __rcu *steering_prog;
struct tun_prog __rcu *filter_prog;
@@ -1935,10 +1936,14 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
break;
}
- if (tun_vnet_hdr_tnl_to_skb(tun->flags, features, skb, &hdr)) {
+ err = tun_vnet_hdr_tnl_to_skb(tun->flags, features, skb, &hdr);
+ if (err < 0) {
atomic_long_inc(&tun->rx_frame_errors);
err = -EINVAL;
goto free_skb;
+ } else if (err > 0) {
+ atomic64_inc(&tun->rx_csum_sw);
+ err = 0;
}
if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP)
@@ -2605,11 +2610,15 @@ static int tun_xdp_one(struct tun_struct *tun,
tnl_hdr = (struct virtio_net_hdr_v1_hash_tunnel *)gso;
skb->dev = tun->dev;
skb_set_network_header(skb, ETH_HLEN);
- if (tun_vnet_hdr_tnl_to_skb(tun->flags, features, skb, tnl_hdr)) {
+ ret = tun_vnet_hdr_tnl_to_skb(tun->flags, features, skb, tnl_hdr);
+ if (ret < 0) {
atomic_long_inc(&tun->rx_frame_errors);
kfree_skb(skb);
ret = -EINVAL;
goto out;
+ } else if (ret > 0) {
+ atomic64_inc(&tun->rx_csum_sw);
+ ret = 0;
}
skb->protocol = eth_type_trans(skb, tun->dev);
@@ -3739,7 +3748,35 @@ static void tun_get_channels(struct net_device *dev,
channels->max_combined = tun->flags & IFF_MULTI_QUEUE ? MAX_TAP_QUEUES : 1;
}
+#define TUN_STATS_LEN 1
+static const char tun_ethtool_strings[TUN_STATS_LEN][ETH_GSTRING_LEN] = {
+ "rx_csum_sw",
+};
+
+static int tun_get_sset_count(struct net_device *dev, int sset)
+{
+ return sset == ETH_SS_STATS ? TUN_STATS_LEN : -EOPNOTSUPP;
+}
+
+static void tun_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ if (stringset == ETH_SS_STATS)
+ memcpy(data, tun_ethtool_strings,
+ sizeof(tun_ethtool_strings));
+}
+
+static void tun_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct tun_struct *tun = netdev_priv(dev);
+
+ data[0] = atomic64_read(&tun->rx_csum_sw);
+}
+
static const struct ethtool_ops tun_ethtool_ops = {
+ .get_sset_count = tun_get_sset_count,
+ .get_strings = tun_get_strings,
+ .get_ethtool_stats = tun_get_ethtool_stats,
.supported_coalesce_params = ETHTOOL_COALESCE_RX_MAX_FRAMES,
.get_drvinfo = tun_get_drvinfo,
.get_msglevel = tun_get_msglevel,
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index daab43ac92ce..2e6fd81df9c4 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -2519,7 +2519,7 @@ static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *
skb_set_network_header(skb, ETH_HLEN);
if (virtio_net_hdr_tnl_to_skb(skb, &hdr->tnl_hdr, vi->rx_tnl,
vi->rx_tnl_csum,
- virtio_is_little_endian(vi->vdev))) {
+ virtio_is_little_endian(vi->vdev)) < 0) {
net_warn_ratelimited("%s: bad gso: type: %x, size: %u, flags %x tunnel %d tnl csum %d\n",
dev->name, hdr->hdr.gso_type,
hdr->hdr.gso_size, hdr->hdr.flags,
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 52ae3d875e97..e6aec9ecfa99 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2913,7 +2913,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
}
if (has_vnet_hdr) {
- if (virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le())) {
+ if (virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le()) < 0) {
tp_len = -EINVAL;
goto tpacket_error;
}
@@ -3136,7 +3136,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
if (vnet_hdr_sz) {
err = virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le());
- if (err)
+ if (err < 0)
goto out_free;
len += vnet_hdr_sz;
}
--
MST
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH net v5 2/7] um: vector: trim skbs before verifying the header
[not found] <cover.v5.git.mst@redhat.com>
2026-10-22 12:57 ` [PATCH net v5 1/7] net: account for ingress-resolved virtio net hdr state Michael S. Tsirkin
@ 2026-10-22 12:58 ` Michael S. Tsirkin
1 sibling, 0 replies; 3+ messages in thread
From: Michael S. Tsirkin @ 2026-10-22 12:58 UTC (permalink / raw)
To: linux-kernel
Cc: Willem de Bruijn, Chris Arges, Björn Töpel, Phil Sutter,
Jason Wang, Eugenio Pérez, Xuan Zhuo, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, virtualization, Max Krasnyansky, Rusty Russell,
Willem de Bruijn, Richard Weinberger, Anton Ivanov, Johannes Berg,
linux-um
The vector receive path over-allocates the skb and only trims it
to the received length after the transport header is verified.
A follow up patch will complete the packet checksum in software within
virtio_net_hdr_to_skb so we need the packet trimmed (otherwise the
checksum would be computed incorrectly over the unreceived tail of the
buffer).
Trim first, so the import sees the packet as it was received.
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
arch/um/drivers/vector_kern.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
index 8a70b3a62553..1afbef78df72 100644
--- a/arch/um/drivers/vector_kern.c
+++ b/arch/um/drivers/vector_kern.c
@@ -865,6 +865,7 @@ static int vector_legacy_rx(struct vector_private *vp)
if (skb != NULL) {
if (pkt_len > vp->header_size) {
+ pskb_trim(skb, pkt_len - vp->rx_header_size);
if (vp->header_size > 0) {
header_check = vp->verify_header(
vp->header_rxbuffer, skb, vp);
@@ -879,7 +880,6 @@ static int vector_legacy_rx(struct vector_private *vp)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
}
- pskb_trim(skb, pkt_len - vp->rx_header_size);
skb->protocol = eth_type_trans(skb, skb->dev);
vp->dev->stats.rx_bytes += skb->len;
vp->dev->stats.rx_packets++;
@@ -981,6 +981,8 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
for (i = 0; i < packet_count; i++) {
skb = (*skbuff_vector);
if (mmsg_vector->msg_len > vp->header_size) {
+ pskb_trim(skb,
+ mmsg_vector->msg_len - vp->rx_header_size);
if (vp->header_size > 0) {
header_check = vp->verify_header(
mmsg_vector->msg_hdr.msg_iov->iov_base,
@@ -1005,8 +1007,6 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
}
- pskb_trim(skb,
- mmsg_vector->msg_len - vp->rx_header_size);
skb->protocol = eth_type_trans(skb, skb->dev);
/*
* We do not need to lock on updating stats here
--
MST
^ permalink raw reply related [flat|nested] 3+ messages in thread