[ Upstream commit 34d21de99cea9cb17967874313e5b0262527833c ] [ Note: Simplified vrf bits to reduce patch given unrelated to the fix ]
Move {l,t,d}stats allocation to the core and let netdevs pick the stats type they need. That way the driver doesn't have to bother with error handling (allocation failure checking, making sure free happens in the right spot, etc) - all happening in the core.
Co-developed-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Daniel Borkmann daniel@iogearbox.net Reviewed-by: Nikolay Aleksandrov razor@blackwall.org Cc: David Ahern dsahern@kernel.org Link: https://lore.kernel.org/r/20231114004220.6495-3-daniel@iogearbox.net Signed-off-by: Martin KaFai Lau martin.lau@kernel.org Stable-dep-of: 024ee930cb3c ("bpf: Fix dev's rx stats for bpf_redirect_peer traffic") Signed-off-by: Daniel Borkmann daniel@iogearbox.net --- drivers/net/veth.c | 16 ++---------- drivers/net/vrf.c | 24 ++++++------------ include/linux/netdevice.h | 30 +++++++++++++++++++--- net/core/dev.c | 53 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 85 insertions(+), 38 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 8dcd3b6e143b..0a8154611d7f 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1381,25 +1381,12 @@ static void veth_free_queues(struct net_device *dev)
static int veth_dev_init(struct net_device *dev) { - int err; - - dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); - if (!dev->lstats) - return -ENOMEM; - - err = veth_alloc_queues(dev); - if (err) { - free_percpu(dev->lstats); - return err; - } - - return 0; + return veth_alloc_queues(dev); }
static void veth_dev_free(struct net_device *dev) { veth_free_queues(dev); - free_percpu(dev->lstats); }
#ifdef CONFIG_NET_POLL_CONTROLLER @@ -1625,6 +1612,7 @@ static void veth_setup(struct net_device *dev) NETIF_F_HW_VLAN_STAG_RX); dev->needs_free_netdev = true; dev->priv_destructor = veth_dev_free; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_LSTATS; dev->max_mtu = ETH_MAX_MTU;
dev->hw_features = VETH_FEATURES; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 208df4d41939..c8a1009d659e 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -121,22 +121,12 @@ struct net_vrf { int ifindex; };
-struct pcpu_dstats { - u64 tx_pkts; - u64 tx_bytes; - u64 tx_drps; - u64 rx_pkts; - u64 rx_bytes; - u64 rx_drps; - struct u64_stats_sync syncp; -}; - static void vrf_rx_stats(struct net_device *dev, int len) { struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
u64_stats_update_begin(&dstats->syncp); - dstats->rx_pkts++; + dstats->rx_packets++; dstats->rx_bytes += len; u64_stats_update_end(&dstats->syncp); } @@ -161,10 +151,10 @@ static void vrf_get_stats64(struct net_device *dev, do { start = u64_stats_fetch_begin_irq(&dstats->syncp); tbytes = dstats->tx_bytes; - tpkts = dstats->tx_pkts; - tdrops = dstats->tx_drps; + tpkts = dstats->tx_packets; + tdrops = dstats->tx_drops; rbytes = dstats->rx_bytes; - rpkts = dstats->rx_pkts; + rpkts = dstats->rx_packets; } while (u64_stats_fetch_retry_irq(&dstats->syncp, start)); stats->tx_bytes += tbytes; stats->tx_packets += tpkts; @@ -421,7 +411,7 @@ static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev, if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) vrf_rx_stats(dev, len); else - this_cpu_inc(dev->dstats->rx_drps); + this_cpu_inc(dev->dstats->rx_drops);
return NETDEV_TX_OK; } @@ -616,11 +606,11 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
u64_stats_update_begin(&dstats->syncp); - dstats->tx_pkts++; + dstats->tx_packets++; dstats->tx_bytes += len; u64_stats_update_end(&dstats->syncp); } else { - this_cpu_inc(dev->dstats->tx_drps); + this_cpu_inc(dev->dstats->tx_drops); }
return ret; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fbbd0df1106b..662183994e88 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1747,6 +1747,13 @@ enum netdev_ml_priv_type { ML_PRIV_CAN, };
+enum netdev_stat_type { + NETDEV_PCPU_STAT_NONE, + NETDEV_PCPU_STAT_LSTATS, /* struct pcpu_lstats */ + NETDEV_PCPU_STAT_TSTATS, /* struct pcpu_sw_netstats */ + NETDEV_PCPU_STAT_DSTATS, /* struct pcpu_dstats */ +}; + /** * struct net_device - The DEVICE structure. * @@ -1941,10 +1948,14 @@ enum netdev_ml_priv_type { * * @ml_priv: Mid-layer private * @ml_priv_type: Mid-layer private type - * @lstats: Loopback statistics - * @tstats: Tunnel statistics - * @dstats: Dummy statistics - * @vstats: Virtual ethernet statistics + * + * @pcpu_stat_type: Type of device statistics which the core should + * allocate/free: none, lstats, tstats, dstats. none + * means the driver is handling statistics allocation/ + * freeing internally. + * @lstats: Loopback statistics: packets, bytes + * @tstats: Tunnel statistics: RX/TX packets, RX/TX bytes + * @dstats: Dummy statistics: RX/TX/drop packets, RX/TX bytes * * @garp_port: GARP * @mrp_port: MRP @@ -2287,6 +2298,7 @@ struct net_device { void *ml_priv; enum netdev_ml_priv_type ml_priv_type;
+ enum netdev_stat_type pcpu_stat_type:8; union { struct pcpu_lstats __percpu *lstats; struct pcpu_sw_netstats __percpu *tstats; @@ -2670,6 +2682,16 @@ struct pcpu_sw_netstats { struct u64_stats_sync syncp; } __aligned(4 * sizeof(u64));
+struct pcpu_dstats { + u64 rx_packets; + u64 rx_bytes; + u64 rx_drops; + u64 tx_packets; + u64 tx_bytes; + u64 tx_drops; + struct u64_stats_sync syncp; +} __aligned(8 * sizeof(u64)); + struct pcpu_lstats { u64_stats_t packets; u64_stats_t bytes; diff --git a/net/core/dev.c b/net/core/dev.c index 42c16b3e86b9..5151f69dd724 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -9991,6 +9991,46 @@ void netif_tx_stop_all_queues(struct net_device *dev) } EXPORT_SYMBOL(netif_tx_stop_all_queues);
+static int netdev_do_alloc_pcpu_stats(struct net_device *dev) +{ + void __percpu *v; + + switch (dev->pcpu_stat_type) { + case NETDEV_PCPU_STAT_NONE: + return 0; + case NETDEV_PCPU_STAT_LSTATS: + v = dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); + break; + case NETDEV_PCPU_STAT_TSTATS: + v = dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + break; + case NETDEV_PCPU_STAT_DSTATS: + v = dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats); + break; + default: + return -EINVAL; + } + + return v ? 0 : -ENOMEM; +} + +static void netdev_do_free_pcpu_stats(struct net_device *dev) +{ + switch (dev->pcpu_stat_type) { + case NETDEV_PCPU_STAT_NONE: + return; + case NETDEV_PCPU_STAT_LSTATS: + free_percpu(dev->lstats); + break; + case NETDEV_PCPU_STAT_TSTATS: + free_percpu(dev->tstats); + break; + case NETDEV_PCPU_STAT_DSTATS: + free_percpu(dev->dstats); + break; + } +} + /** * register_netdevice() - register a network device * @dev: device to register @@ -10051,11 +10091,15 @@ int register_netdevice(struct net_device *dev) goto err_uninit; }
+ ret = netdev_do_alloc_pcpu_stats(dev); + if (ret) + goto err_uninit; + ret = -EBUSY; if (!dev->ifindex) dev->ifindex = dev_new_index(net); else if (__dev_get_by_index(net, dev->ifindex)) - goto err_uninit; + goto err_free_pcpu;
/* Transfer changeable features to wanted_features and enable * software offloads (GSO and GRO). @@ -10102,14 +10146,14 @@ int register_netdevice(struct net_device *dev) ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); ret = notifier_to_errno(ret); if (ret) - goto err_uninit; + goto err_free_pcpu;
ret = netdev_register_kobject(dev); write_lock(&dev_base_lock); dev->reg_state = ret ? NETREG_UNREGISTERED : NETREG_REGISTERED; write_unlock(&dev_base_lock); if (ret) - goto err_uninit; + goto err_free_pcpu;
__netdev_update_features(dev);
@@ -10156,6 +10200,8 @@ int register_netdevice(struct net_device *dev) out: return ret;
+err_free_pcpu: + netdev_do_free_pcpu_stats(dev); err_uninit: if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); @@ -10409,6 +10455,7 @@ void netdev_run_todo(void) WARN_ON(rcu_access_pointer(dev->ip_ptr)); WARN_ON(rcu_access_pointer(dev->ip6_ptr));
+ netdev_do_free_pcpu_stats(dev); if (dev->priv_destructor) dev->priv_destructor(dev); if (dev->needs_free_netdev)
From: Peilin Ye peilin.ye@bytedance.com
[ Upstream commit 024ee930cb3c9ae49e4266aee89cfde0ebb407e1 ]
Traffic redirected by bpf_redirect_peer() (used by recent CNIs like Cilium) is not accounted for in the RX stats of supported devices (that is, veth and netkit), confusing user space metrics collectors such as cAdvisor [0], as reported by Youlun.
Fix it by calling dev_sw_netstats_rx_add() in skb_do_redirect(), to update RX traffic counters. Devices that support ndo_get_peer_dev _must_ use the @tstats per-CPU counters (instead of @lstats, or @dstats).
To make this more fool-proof, error out when ndo_get_peer_dev is set but @tstats are not selected.
[0] Specifically, the "container_network_receive_{byte,packet}s_total" counters are affected.
Fixes: 9aa1206e8f48 ("bpf: Add redirect_peer helper") Reported-by: Youlun Zhang zhangyoulun@bytedance.com Signed-off-by: Peilin Ye peilin.ye@bytedance.com Co-developed-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: Daniel Borkmann daniel@iogearbox.net Reviewed-by: Nikolay Aleksandrov razor@blackwall.org Link: https://lore.kernel.org/r/20231114004220.6495-6-daniel@iogearbox.net Signed-off-by: Martin KaFai Lau martin.lau@kernel.org Signed-off-by: Daniel Borkmann daniel@iogearbox.net --- net/core/dev.c | 8 ++++++++ net/core/filter.c | 1 + 2 files changed, 9 insertions(+)
diff --git a/net/core/dev.c b/net/core/dev.c index 5151f69dd724..2ee1a535b3cb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -9995,6 +9995,14 @@ static int netdev_do_alloc_pcpu_stats(struct net_device *dev) { void __percpu *v;
+ /* Drivers implementing ndo_get_peer_dev must support tstat + * accounting, so that skb_do_redirect() can bump the dev's + * RX stats upon network namespace switch. + */ + if (dev->netdev_ops->ndo_get_peer_dev && + dev->pcpu_stat_type != NETDEV_PCPU_STAT_TSTATS) + return -EOPNOTSUPP; + switch (dev->pcpu_stat_type) { case NETDEV_PCPU_STAT_NONE: return 0; diff --git a/net/core/filter.c b/net/core/filter.c index 2f6fef5f5864..e766e66ef62a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2491,6 +2491,7 @@ int skb_do_redirect(struct sk_buff *skb) net_eq(net, dev_net(dev)))) goto out_drop; skb->dev = dev; + dev_sw_netstats_rx_add(dev, skb->len); return -EAGAIN; } return flags & BPF_F_NEIGH ?
[ Sasha's backport helper bot ]
Hi,
The upstream commit SHA1 provided is correct: 024ee930cb3c9ae49e4266aee89cfde0ebb407e1
WARNING: Author mismatch between patch and upstream commit: Backport author: Daniel Borkmann daniel@iogearbox.net Commit author: Peilin Ye peilin.ye@bytedance.com
Status in newer kernel trees: 6.12.y | Present (exact SHA1) 6.6.y | Present (different SHA1: 959f301635dc) 6.1.y | Not found
Note: The patch differs from the upstream commit: --- Failed to apply patch cleanly, falling back to interdiff... ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-6.1.y | Success | Success |
From: Peilin Ye peilin.ye@bytedance.com
[ Upstream commit 6f2684bf2b4460c84d0d34612a939f78b96b03fc ]
Currently veth devices use the lstats per-CPU traffic counters, which only cover TX traffic. veth_get_stats64() actually populates RX stats of a veth device from its peer's TX counters, based on the assumption that a veth device can _only_ receive packets from its peer, which is no longer true:
For example, recent CNIs (like Cilium) can use the bpf_redirect_peer() BPF helper to redirect traffic from NIC's tc ingress to veth's tc ingress (in a different netns), skipping veth's peer device. Unfortunately, this kind of traffic isn't currently accounted for in veth's RX stats.
In preparation for the fix, use tstats (instead of lstats) to maintain both RX and TX counters for each veth device. We'll use RX counters for bpf_redirect_peer() traffic, and keep using TX counters for the usual "peer-to-peer" traffic. In veth_get_stats64(), calculate RX stats by _adding_ RX count to peer's TX count, in order to cover both kinds of traffic.
veth_stats_rx() might need a name change (perhaps to "veth_stats_xdp()") for less confusion, but let's leave it to another patch to keep the fix minimal.
Signed-off-by: Peilin Ye peilin.ye@bytedance.com Co-developed-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: Daniel Borkmann daniel@iogearbox.net Reviewed-by: Nikolay Aleksandrov razor@blackwall.org Link: https://lore.kernel.org/r/20231114004220.6495-5-daniel@iogearbox.net Signed-off-by: Martin KaFai Lau martin.lau@kernel.org Signed-off-by: Daniel Borkmann daniel@iogearbox.net --- drivers/net/veth.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 0a8154611d7f..e1e7df00e85c 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -342,7 +342,7 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); if (likely(veth_forward_skb(rcv, skb, rq, use_napi) == NET_RX_SUCCESS)) { if (!use_napi) - dev_lstats_add(dev, length); + dev_sw_netstats_tx_add(dev, 1, length); } else { drop: atomic64_inc(&priv->dropped); @@ -357,14 +357,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) return ret; }
-static u64 veth_stats_tx(struct net_device *dev, u64 *packets, u64 *bytes) -{ - struct veth_priv *priv = netdev_priv(dev); - - dev_lstats_read(dev, packets, bytes); - return atomic64_read(&priv->dropped); -} - static void veth_stats_rx(struct veth_stats *result, struct net_device *dev) { struct veth_priv *priv = netdev_priv(dev); @@ -402,24 +394,24 @@ static void veth_get_stats64(struct net_device *dev, struct veth_priv *priv = netdev_priv(dev); struct net_device *peer; struct veth_stats rx; - u64 packets, bytes;
- tot->tx_dropped = veth_stats_tx(dev, &packets, &bytes); - tot->tx_bytes = bytes; - tot->tx_packets = packets; + tot->tx_dropped = atomic64_read(&priv->dropped); + dev_fetch_sw_netstats(tot, dev->tstats);
veth_stats_rx(&rx, dev); tot->tx_dropped += rx.xdp_tx_err; tot->rx_dropped = rx.rx_drops + rx.peer_tq_xdp_xmit_err; - tot->rx_bytes = rx.xdp_bytes; - tot->rx_packets = rx.xdp_packets; + tot->rx_bytes += rx.xdp_bytes; + tot->rx_packets += rx.xdp_packets;
rcu_read_lock(); peer = rcu_dereference(priv->peer); if (peer) { - veth_stats_tx(peer, &packets, &bytes); - tot->rx_bytes += bytes; - tot->rx_packets += packets; + struct rtnl_link_stats64 tot_peer = {}; + + dev_fetch_sw_netstats(&tot_peer, peer->tstats); + tot->rx_bytes += tot_peer.tx_bytes; + tot->rx_packets += tot_peer.tx_packets;
veth_stats_rx(&rx, peer); tot->tx_dropped += rx.peer_tq_xdp_xmit_err; @@ -1612,7 +1604,7 @@ static void veth_setup(struct net_device *dev) NETIF_F_HW_VLAN_STAG_RX); dev->needs_free_netdev = true; dev->priv_destructor = veth_dev_free; - dev->pcpu_stat_type = NETDEV_PCPU_STAT_LSTATS; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; dev->max_mtu = ETH_MAX_MTU;
dev->hw_features = VETH_FEATURES;
[ Sasha's backport helper bot ]
Hi,
The upstream commit SHA1 provided is correct: 6f2684bf2b4460c84d0d34612a939f78b96b03fc
WARNING: Author mismatch between patch and upstream commit: Backport author: Daniel Borkmann daniel@iogearbox.net Commit author: Peilin Ye peilin.ye@bytedance.com
Status in newer kernel trees: 6.12.y | Present (exact SHA1) 6.6.y | Present (different SHA1: b74095a4945f) 6.1.y | Not found
Note: The patch differs from the upstream commit: --- Failed to apply patch cleanly, falling back to interdiff... ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-6.1.y | Success | Success |
[ Sasha's backport helper bot ]
Hi,
The upstream commit SHA1 provided is correct: 34d21de99cea9cb17967874313e5b0262527833c
Status in newer kernel trees: 6.12.y | Present (exact SHA1) 6.6.y | Present (different SHA1: 6ae7b3fc7ae8) 6.1.y | Not found
Note: The patch differs from the upstream commit: --- 1: 34d21de99cea9 ! 1: 927b0635b3b95 net: Move {l,t,d}stats allocation to core and convert veth & vrf @@ Metadata ## Commit message ## net: Move {l,t,d}stats allocation to core and convert veth & vrf
+ [ Upstream commit 34d21de99cea9cb17967874313e5b0262527833c ] + [ Note: Simplified vrf bits to reduce patch given unrelated to the fix ] + Move {l,t,d}stats allocation to the core and let netdevs pick the stats type they need. That way the driver doesn't have to bother with error handling (allocation failure checking, making sure free happens in the @@ Commit message Cc: David Ahern dsahern@kernel.org Link: https://lore.kernel.org/r/20231114004220.6495-3-daniel@iogearbox.net Signed-off-by: Martin KaFai Lau martin.lau@kernel.org + Stable-dep-of: 024ee930cb3c ("bpf: Fix dev's rx stats for bpf_redirect_peer traffic") + Signed-off-by: Daniel Borkmann daniel@iogearbox.net
## drivers/net/veth.c ## @@ drivers/net/veth.c: static void veth_free_queues(struct net_device *dev) @@ drivers/net/veth.c: static void veth_setup(struct net_device *dev) dev->hw_features = VETH_FEATURES;
## drivers/net/vrf.c ## -@@ drivers/net/vrf.c: static void vrf_dev_uninit(struct net_device *dev) +@@ drivers/net/vrf.c: struct net_vrf { + int ifindex; + };
- vrf_rtable_release(dev, vrf); - vrf_rt6_release(dev, vrf); +-struct pcpu_dstats { +- u64 tx_pkts; +- u64 tx_bytes; +- u64 tx_drps; +- u64 rx_pkts; +- u64 rx_bytes; +- u64 rx_drps; +- struct u64_stats_sync syncp; +-}; - -- free_percpu(dev->dstats); -- dev->dstats = NULL; - } - - static int vrf_dev_init(struct net_device *dev) + static void vrf_rx_stats(struct net_device *dev, int len) { - struct net_vrf *vrf = netdev_priv(dev); + struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
-- dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats); -- if (!dev->dstats) -- goto out_nomem; -- - /* create the default dst which points back to us */ - if (vrf_rtable_create(dev) != 0) -- goto out_stats; -+ goto out_nomem; - - if (vrf_rt6_create(dev) != 0) - goto out_rth; -@@ drivers/net/vrf.c: static int vrf_dev_init(struct net_device *dev) - - out_rth: - vrf_rtable_release(dev, vrf); --out_stats: -- free_percpu(dev->dstats); -- dev->dstats = NULL; - out_nomem: - return -ENOMEM; + u64_stats_update_begin(&dstats->syncp); +- dstats->rx_pkts++; ++ dstats->rx_packets++; + dstats->rx_bytes += len; + u64_stats_update_end(&dstats->syncp); } -@@ drivers/net/vrf.c: static void vrf_setup(struct net_device *dev) - dev->min_mtu = IPV6_MIN_MTU; - dev->max_mtu = IP6_MAX_MTU; - dev->mtu = dev->max_mtu; -+ -+ dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; +@@ drivers/net/vrf.c: static void vrf_get_stats64(struct net_device *dev, + do { + start = u64_stats_fetch_begin_irq(&dstats->syncp); + tbytes = dstats->tx_bytes; +- tpkts = dstats->tx_pkts; +- tdrops = dstats->tx_drps; ++ tpkts = dstats->tx_packets; ++ tdrops = dstats->tx_drops; + rbytes = dstats->rx_bytes; +- rpkts = dstats->rx_pkts; ++ rpkts = dstats->rx_packets; + } while (u64_stats_fetch_retry_irq(&dstats->syncp, start)); + stats->tx_bytes += tbytes; + stats->tx_packets += tpkts; +@@ drivers/net/vrf.c: static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev, + if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) + vrf_rx_stats(dev, len); + else +- this_cpu_inc(dev->dstats->rx_drps); ++ this_cpu_inc(dev->dstats->rx_drops); + + return NETDEV_TX_OK; } +@@ drivers/net/vrf.c: static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) + struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
- static int vrf_validate(struct nlattr *tb[], struct nlattr *data[], + u64_stats_update_begin(&dstats->syncp); +- dstats->tx_pkts++; ++ dstats->tx_packets++; + dstats->tx_bytes += len; + u64_stats_update_end(&dstats->syncp); + } else { +- this_cpu_inc(dev->dstats->tx_drps); ++ this_cpu_inc(dev->dstats->tx_drops); + } + + return ret;
## include/linux/netdevice.h ## @@ include/linux/netdevice.h: enum netdev_ml_priv_type { @@ include/linux/netdevice.h: struct net_device { union { struct pcpu_lstats __percpu *lstats; struct pcpu_sw_netstats __percpu *tstats; +@@ include/linux/netdevice.h: struct pcpu_sw_netstats { + struct u64_stats_sync syncp; + } __aligned(4 * sizeof(u64)); + ++struct pcpu_dstats { ++ u64 rx_packets; ++ u64 rx_bytes; ++ u64 rx_drops; ++ u64 tx_packets; ++ u64 tx_bytes; ++ u64 tx_drops; ++ struct u64_stats_sync syncp; ++} __aligned(8 * sizeof(u64)); ++ + struct pcpu_lstats { + u64_stats_t packets; + u64_stats_t bytes;
## net/core/dev.c ## @@ net/core/dev.c: void netif_tx_stop_all_queues(struct net_device *dev) @@ net/core/dev.c: int register_netdevice(struct net_device *dev) + if (ret) + goto err_uninit; + - ret = dev_index_reserve(net, dev->ifindex); - if (ret < 0) + ret = -EBUSY; + if (!dev->ifindex) + dev->ifindex = dev_new_index(net); + else if (__dev_get_by_index(net, dev->ifindex)) - goto err_uninit; + goto err_free_pcpu; - dev->ifindex = ret;
/* Transfer changeable features to wanted_features and enable + * software offloads (GSO and GRO). +@@ net/core/dev.c: int register_netdevice(struct net_device *dev) + ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); + ret = notifier_to_errno(ret); + if (ret) +- goto err_uninit; ++ goto err_free_pcpu; + + ret = netdev_register_kobject(dev); + write_lock(&dev_base_lock); + dev->reg_state = ret ? NETREG_UNREGISTERED : NETREG_REGISTERED; + write_unlock(&dev_base_lock); + if (ret) +- goto err_uninit; ++ goto err_free_pcpu; + + __netdev_update_features(dev); + @@ net/core/dev.c: int register_netdevice(struct net_device *dev) - call_netdevice_notifiers(NETDEV_PRE_UNINIT, dev); - err_ifindex_release: - dev_index_release(net, dev->ifindex); + out: + return ret; + +err_free_pcpu: + netdev_do_free_pcpu_stats(dev); err_uninit: ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-6.1.y | Success | Success |
linux-stable-mirror@lists.linaro.org