Skip to content

Commit 936e9bc

Browse files
wtarreaugregkh
authored andcommitted
net: mvneta: use per_cpu stats to fix an SMP lock up
commit 74c41b0 upstream. Stats writers are mvneta_rx() and mvneta_tx(). They don't lock anything when they update the stats, and as a result, it randomly happens that the stats freeze on SMP if two updates happen during stats retrieval. This is very easily reproducible by starting two HTTP servers and binding each of them to a different CPU, then consulting /proc/net/dev in loops during transfers, the interface should immediately lock up. This issue also randomly happens upon link state changes during transfers, because the stats are collected in this situation, but it takes more attempts to reproduce it. The comments in netdevice.h suggest using per_cpu stats instead to get rid of this issue. This patch implements this. It merges both rx_stats and tx_stats into a single "stats" member with a single syncp. Both mvneta_rx() and mvneta_rx() now only update the a single CPU's counters. In turn, mvneta_get_stats64() does the summing by iterating over all CPUs to get their respective stats. With this change, stats are still correct and no more lockup is encountered. Note that this bug was present since the first import of the mvneta driver. It might make sense to backport it to some stable trees. If so, it depends on "d33dc73 net: mvneta: increase the 64-bit rx/tx stats out of the hot path". Cc: Thomas Petazzoni <[email protected]> Cc: Gregory CLEMENT <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Tested-by: Arnaud Ebalard <[email protected]> Signed-off-by: Willy Tarreau <[email protected]> Signed-off-by: David S. Miller <[email protected]> [wt: port to 3.10 : u64_stats_init() does not exist in 3.10 and is not needed] Signed-off-by: Willy Tarreau <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 5831364 commit 936e9bc

File tree

1 file changed

+48
-26
lines changed

1 file changed

+48
-26
lines changed

drivers/net/ethernet/marvell/mvneta.c

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,12 @@
219219

220220
#define MVNETA_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD)
221221

222-
struct mvneta_stats {
222+
struct mvneta_pcpu_stats {
223223
struct u64_stats_sync syncp;
224-
u64 packets;
225-
u64 bytes;
224+
u64 rx_packets;
225+
u64 rx_bytes;
226+
u64 tx_packets;
227+
u64 tx_bytes;
226228
};
227229

228230
struct mvneta_port {
@@ -248,8 +250,7 @@ struct mvneta_port {
248250
u8 mcast_count[256];
249251
u16 tx_ring_size;
250252
u16 rx_ring_size;
251-
struct mvneta_stats tx_stats;
252-
struct mvneta_stats rx_stats;
253+
struct mvneta_pcpu_stats *stats;
253254

254255
struct mii_bus *mii_bus;
255256
struct phy_device *phy_dev;
@@ -428,21 +429,29 @@ struct rtnl_link_stats64 *mvneta_get_stats64(struct net_device *dev,
428429
{
429430
struct mvneta_port *pp = netdev_priv(dev);
430431
unsigned int start;
432+
int cpu;
431433

432-
memset(stats, 0, sizeof(struct rtnl_link_stats64));
433-
434-
do {
435-
start = u64_stats_fetch_begin_bh(&pp->rx_stats.syncp);
436-
stats->rx_packets = pp->rx_stats.packets;
437-
stats->rx_bytes = pp->rx_stats.bytes;
438-
} while (u64_stats_fetch_retry_bh(&pp->rx_stats.syncp, start));
434+
for_each_possible_cpu(cpu) {
435+
struct mvneta_pcpu_stats *cpu_stats;
436+
u64 rx_packets;
437+
u64 rx_bytes;
438+
u64 tx_packets;
439+
u64 tx_bytes;
439440

441+
cpu_stats = per_cpu_ptr(pp->stats, cpu);
442+
do {
443+
start = u64_stats_fetch_begin_bh(&cpu_stats->syncp);
444+
rx_packets = cpu_stats->rx_packets;
445+
rx_bytes = cpu_stats->rx_bytes;
446+
tx_packets = cpu_stats->tx_packets;
447+
tx_bytes = cpu_stats->tx_bytes;
448+
} while (u64_stats_fetch_retry_bh(&cpu_stats->syncp, start));
440449

441-
do {
442-
start = u64_stats_fetch_begin_bh(&pp->tx_stats.syncp);
443-
stats->tx_packets = pp->tx_stats.packets;
444-
stats->tx_bytes = pp->tx_stats.bytes;
445-
} while (u64_stats_fetch_retry_bh(&pp->tx_stats.syncp, start));
450+
stats->rx_packets += rx_packets;
451+
stats->rx_bytes += rx_bytes;
452+
stats->tx_packets += tx_packets;
453+
stats->tx_bytes += tx_bytes;
454+
}
446455

447456
stats->rx_errors = dev->stats.rx_errors;
448457
stats->rx_dropped = dev->stats.rx_dropped;
@@ -1416,10 +1425,12 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
14161425
}
14171426

14181427
if (rcvd_pkts) {
1419-
u64_stats_update_begin(&pp->rx_stats.syncp);
1420-
pp->rx_stats.packets += rcvd_pkts;
1421-
pp->rx_stats.bytes += rcvd_bytes;
1422-
u64_stats_update_end(&pp->rx_stats.syncp);
1428+
struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats);
1429+
1430+
u64_stats_update_begin(&stats->syncp);
1431+
stats->rx_packets += rcvd_pkts;
1432+
stats->rx_bytes += rcvd_bytes;
1433+
u64_stats_update_end(&stats->syncp);
14231434
}
14241435

14251436
/* Update rxq management counters */
@@ -1552,11 +1563,12 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
15521563

15531564
out:
15541565
if (frags > 0) {
1555-
u64_stats_update_begin(&pp->tx_stats.syncp);
1556-
pp->tx_stats.packets++;
1557-
pp->tx_stats.bytes += skb->len;
1558-
u64_stats_update_end(&pp->tx_stats.syncp);
1566+
struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats);
15591567

1568+
u64_stats_update_begin(&stats->syncp);
1569+
stats->tx_packets++;
1570+
stats->tx_bytes += skb->len;
1571+
u64_stats_update_end(&stats->syncp);
15601572
} else {
15611573
dev->stats.tx_dropped++;
15621574
dev_kfree_skb_any(skb);
@@ -2758,6 +2770,13 @@ static int mvneta_probe(struct platform_device *pdev)
27582770

27592771
clk_prepare_enable(pp->clk);
27602772

2773+
/* Alloc per-cpu stats */
2774+
pp->stats = alloc_percpu(struct mvneta_pcpu_stats);
2775+
if (!pp->stats) {
2776+
err = -ENOMEM;
2777+
goto err_clk;
2778+
}
2779+
27612780
pp->tx_done_timer.data = (unsigned long)dev;
27622781

27632782
pp->tx_ring_size = MVNETA_MAX_TXD;
@@ -2769,7 +2788,7 @@ static int mvneta_probe(struct platform_device *pdev)
27692788
err = mvneta_init(pp, phy_addr);
27702789
if (err < 0) {
27712790
dev_err(&pdev->dev, "can't init eth hal\n");
2772-
goto err_clk;
2791+
goto err_free_stats;
27732792
}
27742793
mvneta_port_power_up(pp, phy_mode);
27752794

@@ -2798,6 +2817,8 @@ static int mvneta_probe(struct platform_device *pdev)
27982817

27992818
err_deinit:
28002819
mvneta_deinit(pp);
2820+
err_free_stats:
2821+
free_percpu(pp->stats);
28012822
err_clk:
28022823
clk_disable_unprepare(pp->clk);
28032824
err_unmap:
@@ -2818,6 +2839,7 @@ static int mvneta_remove(struct platform_device *pdev)
28182839
unregister_netdev(dev);
28192840
mvneta_deinit(pp);
28202841
clk_disable_unprepare(pp->clk);
2842+
free_percpu(pp->stats);
28212843
iounmap(pp->base);
28222844
irq_dispose_mapping(dev->irq);
28232845
free_netdev(dev);

0 commit comments

Comments
 (0)