Skip to content

Commit 38cc3c6

Browse files
ptesarikdavem330
authored andcommitted
net: stmmac: protect updates of 64-bit statistics counters
As explained by a comment in <linux/u64_stats_sync.h>, write side of struct u64_stats_sync must ensure mutual exclusion, or one seqcount update could be lost on 32-bit platforms, thus blocking readers forever. Such lockups have been observed in real world after stmmac_xmit() on one CPU raced with stmmac_napi_poll_tx() on another CPU. To fix the issue without introducing a new lock, split the statics into three parts: 1. fields updated only under the tx queue lock, 2. fields updated only during NAPI poll, 3. fields updated only from interrupt context, Updates to fields in the first two groups are already serialized through other locks. It is sufficient to split the existing struct u64_stats_sync so that each group has its own. Note that tx_set_ic_bit is updated from both contexts. Split this counter so that each context gets its own, and calculate their sum to get the total value in stmmac_get_ethtool_stats(). For the third group, multiple interrupts may be processed by different CPUs at the same time, but interrupts on the same CPU will not nest. Move fields from this group to a newly created per-cpu struct stmmac_pcpu_stats. Fixes: 133466c ("net: stmmac: use per-queue 64 bit statistics where necessary") Link: https://lore.kernel.org/netdev/[email protected]/t/ Cc: [email protected] Signed-off-by: Petr Tesarik <[email protected]> Reviewed-by: Jisheng Zhang <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent cb88cb5 commit 38cc3c6

File tree

7 files changed

+221
-157
lines changed

7 files changed

+221
-157
lines changed

drivers/net/ethernet/stmicro/stmmac/common.h

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,28 +59,51 @@
5959
#undef FRAME_FILTER_DEBUG
6060
/* #define FRAME_FILTER_DEBUG */
6161

62+
struct stmmac_q_tx_stats {
63+
u64_stats_t tx_bytes;
64+
u64_stats_t tx_set_ic_bit;
65+
u64_stats_t tx_tso_frames;
66+
u64_stats_t tx_tso_nfrags;
67+
};
68+
69+
struct stmmac_napi_tx_stats {
70+
u64_stats_t tx_packets;
71+
u64_stats_t tx_pkt_n;
72+
u64_stats_t poll;
73+
u64_stats_t tx_clean;
74+
u64_stats_t tx_set_ic_bit;
75+
};
76+
6277
struct stmmac_txq_stats {
63-
u64 tx_bytes;
64-
u64 tx_packets;
65-
u64 tx_pkt_n;
66-
u64 tx_normal_irq_n;
67-
u64 napi_poll;
68-
u64 tx_clean;
69-
u64 tx_set_ic_bit;
70-
u64 tx_tso_frames;
71-
u64 tx_tso_nfrags;
72-
struct u64_stats_sync syncp;
78+
/* Updates protected by tx queue lock. */
79+
struct u64_stats_sync q_syncp;
80+
struct stmmac_q_tx_stats q;
81+
82+
/* Updates protected by NAPI poll logic. */
83+
struct u64_stats_sync napi_syncp;
84+
struct stmmac_napi_tx_stats napi;
7385
} ____cacheline_aligned_in_smp;
7486

87+
struct stmmac_napi_rx_stats {
88+
u64_stats_t rx_bytes;
89+
u64_stats_t rx_packets;
90+
u64_stats_t rx_pkt_n;
91+
u64_stats_t poll;
92+
};
93+
7594
struct stmmac_rxq_stats {
76-
u64 rx_bytes;
77-
u64 rx_packets;
78-
u64 rx_pkt_n;
79-
u64 rx_normal_irq_n;
80-
u64 napi_poll;
81-
struct u64_stats_sync syncp;
95+
/* Updates protected by NAPI poll logic. */
96+
struct u64_stats_sync napi_syncp;
97+
struct stmmac_napi_rx_stats napi;
8298
} ____cacheline_aligned_in_smp;
8399

100+
/* Updates on each CPU protected by not allowing nested irqs. */
101+
struct stmmac_pcpu_stats {
102+
struct u64_stats_sync syncp;
103+
u64_stats_t rx_normal_irq_n[MTL_MAX_TX_QUEUES];
104+
u64_stats_t tx_normal_irq_n[MTL_MAX_RX_QUEUES];
105+
};
106+
84107
/* Extra statistic and debug information exposed by ethtool */
85108
struct stmmac_extra_stats {
86109
/* Transmit errors */
@@ -205,6 +228,7 @@ struct stmmac_extra_stats {
205228
/* per queue statistics */
206229
struct stmmac_txq_stats txq_stats[MTL_MAX_TX_QUEUES];
207230
struct stmmac_rxq_stats rxq_stats[MTL_MAX_RX_QUEUES];
231+
struct stmmac_pcpu_stats __percpu *pcpu_stats;
208232
unsigned long rx_dropped;
209233
unsigned long rx_errors;
210234
unsigned long tx_dropped;

drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,7 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,
441441
struct stmmac_extra_stats *x, u32 chan,
442442
u32 dir)
443443
{
444-
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan];
445-
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan];
444+
struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);
446445
int ret = 0;
447446
u32 v;
448447

@@ -455,9 +454,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,
455454

456455
if (v & EMAC_TX_INT) {
457456
ret |= handle_tx;
458-
u64_stats_update_begin(&txq_stats->syncp);
459-
txq_stats->tx_normal_irq_n++;
460-
u64_stats_update_end(&txq_stats->syncp);
457+
u64_stats_update_begin(&stats->syncp);
458+
u64_stats_inc(&stats->tx_normal_irq_n[chan]);
459+
u64_stats_update_end(&stats->syncp);
461460
}
462461

463462
if (v & EMAC_TX_DMA_STOP_INT)
@@ -479,9 +478,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,
479478

480479
if (v & EMAC_RX_INT) {
481480
ret |= handle_rx;
482-
u64_stats_update_begin(&rxq_stats->syncp);
483-
rxq_stats->rx_normal_irq_n++;
484-
u64_stats_update_end(&rxq_stats->syncp);
481+
u64_stats_update_begin(&stats->syncp);
482+
u64_stats_inc(&stats->rx_normal_irq_n[chan]);
483+
u64_stats_update_end(&stats->syncp);
485484
}
486485

487486
if (v & EMAC_RX_BUF_UA_INT)

drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,7 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
171171
const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
172172
u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan));
173173
u32 intr_en = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
174-
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan];
175-
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan];
174+
struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);
176175
int ret = 0;
177176

178177
if (dir == DMA_DIR_RX)
@@ -201,15 +200,15 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
201200
}
202201
/* TX/RX NORMAL interrupts */
203202
if (likely(intr_status & DMA_CHAN_STATUS_RI)) {
204-
u64_stats_update_begin(&rxq_stats->syncp);
205-
rxq_stats->rx_normal_irq_n++;
206-
u64_stats_update_end(&rxq_stats->syncp);
203+
u64_stats_update_begin(&stats->syncp);
204+
u64_stats_inc(&stats->rx_normal_irq_n[chan]);
205+
u64_stats_update_end(&stats->syncp);
207206
ret |= handle_rx;
208207
}
209208
if (likely(intr_status & DMA_CHAN_STATUS_TI)) {
210-
u64_stats_update_begin(&txq_stats->syncp);
211-
txq_stats->tx_normal_irq_n++;
212-
u64_stats_update_end(&txq_stats->syncp);
209+
u64_stats_update_begin(&stats->syncp);
210+
u64_stats_inc(&stats->tx_normal_irq_n[chan]);
211+
u64_stats_update_end(&stats->syncp);
213212
ret |= handle_tx;
214213
}
215214

drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ static void show_rx_process_state(unsigned int status)
162162
int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
163163
struct stmmac_extra_stats *x, u32 chan, u32 dir)
164164
{
165-
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan];
166-
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan];
165+
struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);
167166
int ret = 0;
168167
/* read the status register (CSR5) */
169168
u32 intr_status = readl(ioaddr + DMA_STATUS);
@@ -215,16 +214,16 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
215214
u32 value = readl(ioaddr + DMA_INTR_ENA);
216215
/* to schedule NAPI on real RIE event. */
217216
if (likely(value & DMA_INTR_ENA_RIE)) {
218-
u64_stats_update_begin(&rxq_stats->syncp);
219-
rxq_stats->rx_normal_irq_n++;
220-
u64_stats_update_end(&rxq_stats->syncp);
217+
u64_stats_update_begin(&stats->syncp);
218+
u64_stats_inc(&stats->rx_normal_irq_n[chan]);
219+
u64_stats_update_end(&stats->syncp);
221220
ret |= handle_rx;
222221
}
223222
}
224223
if (likely(intr_status & DMA_STATUS_TI)) {
225-
u64_stats_update_begin(&txq_stats->syncp);
226-
txq_stats->tx_normal_irq_n++;
227-
u64_stats_update_end(&txq_stats->syncp);
224+
u64_stats_update_begin(&stats->syncp);
225+
u64_stats_inc(&stats->tx_normal_irq_n[chan]);
226+
u64_stats_update_end(&stats->syncp);
228227
ret |= handle_tx;
229228
}
230229
if (unlikely(intr_status & DMA_STATUS_ERI))

drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,7 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
337337
struct stmmac_extra_stats *x, u32 chan,
338338
u32 dir)
339339
{
340-
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan];
341-
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan];
340+
struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);
342341
u32 intr_status = readl(ioaddr + XGMAC_DMA_CH_STATUS(chan));
343342
u32 intr_en = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan));
344343
int ret = 0;
@@ -367,15 +366,15 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
367366
/* TX/RX NORMAL interrupts */
368367
if (likely(intr_status & XGMAC_NIS)) {
369368
if (likely(intr_status & XGMAC_RI)) {
370-
u64_stats_update_begin(&rxq_stats->syncp);
371-
rxq_stats->rx_normal_irq_n++;
372-
u64_stats_update_end(&rxq_stats->syncp);
369+
u64_stats_update_begin(&stats->syncp);
370+
u64_stats_inc(&stats->rx_normal_irq_n[chan]);
371+
u64_stats_update_end(&stats->syncp);
373372
ret |= handle_rx;
374373
}
375374
if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) {
376-
u64_stats_update_begin(&txq_stats->syncp);
377-
txq_stats->tx_normal_irq_n++;
378-
u64_stats_update_end(&txq_stats->syncp);
375+
u64_stats_update_begin(&stats->syncp);
376+
u64_stats_inc(&stats->tx_normal_irq_n[chan]);
377+
u64_stats_update_end(&stats->syncp);
379378
ret |= handle_tx;
380379
}
381380
}

drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c

Lines changed: 87 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -549,44 +549,79 @@ stmmac_set_pauseparam(struct net_device *netdev,
549549
}
550550
}
551551

552+
static u64 stmmac_get_rx_normal_irq_n(struct stmmac_priv *priv, int q)
553+
{
554+
u64 total;
555+
int cpu;
556+
557+
total = 0;
558+
for_each_possible_cpu(cpu) {
559+
struct stmmac_pcpu_stats *pcpu;
560+
unsigned int start;
561+
u64 irq_n;
562+
563+
pcpu = per_cpu_ptr(priv->xstats.pcpu_stats, cpu);
564+
do {
565+
start = u64_stats_fetch_begin(&pcpu->syncp);
566+
irq_n = u64_stats_read(&pcpu->rx_normal_irq_n[q]);
567+
} while (u64_stats_fetch_retry(&pcpu->syncp, start));
568+
total += irq_n;
569+
}
570+
return total;
571+
}
572+
573+
static u64 stmmac_get_tx_normal_irq_n(struct stmmac_priv *priv, int q)
574+
{
575+
u64 total;
576+
int cpu;
577+
578+
total = 0;
579+
for_each_possible_cpu(cpu) {
580+
struct stmmac_pcpu_stats *pcpu;
581+
unsigned int start;
582+
u64 irq_n;
583+
584+
pcpu = per_cpu_ptr(priv->xstats.pcpu_stats, cpu);
585+
do {
586+
start = u64_stats_fetch_begin(&pcpu->syncp);
587+
irq_n = u64_stats_read(&pcpu->tx_normal_irq_n[q]);
588+
} while (u64_stats_fetch_retry(&pcpu->syncp, start));
589+
total += irq_n;
590+
}
591+
return total;
592+
}
593+
552594
static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data)
553595
{
554596
u32 tx_cnt = priv->plat->tx_queues_to_use;
555597
u32 rx_cnt = priv->plat->rx_queues_to_use;
556598
unsigned int start;
557-
int q, stat;
558-
char *p;
599+
int q;
559600

560601
for (q = 0; q < tx_cnt; q++) {
561602
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[q];
562-
struct stmmac_txq_stats snapshot;
603+
u64 pkt_n;
563604

564605
do {
565-
start = u64_stats_fetch_begin(&txq_stats->syncp);
566-
snapshot = *txq_stats;
567-
} while (u64_stats_fetch_retry(&txq_stats->syncp, start));
606+
start = u64_stats_fetch_begin(&txq_stats->napi_syncp);
607+
pkt_n = u64_stats_read(&txq_stats->napi.tx_pkt_n);
608+
} while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start));
568609

569-
p = (char *)&snapshot + offsetof(struct stmmac_txq_stats, tx_pkt_n);
570-
for (stat = 0; stat < STMMAC_TXQ_STATS; stat++) {
571-
*data++ = (*(u64 *)p);
572-
p += sizeof(u64);
573-
}
610+
*data++ = pkt_n;
611+
*data++ = stmmac_get_tx_normal_irq_n(priv, q);
574612
}
575613

576614
for (q = 0; q < rx_cnt; q++) {
577615
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[q];
578-
struct stmmac_rxq_stats snapshot;
616+
u64 pkt_n;
579617

580618
do {
581-
start = u64_stats_fetch_begin(&rxq_stats->syncp);
582-
snapshot = *rxq_stats;
583-
} while (u64_stats_fetch_retry(&rxq_stats->syncp, start));
619+
start = u64_stats_fetch_begin(&rxq_stats->napi_syncp);
620+
pkt_n = u64_stats_read(&rxq_stats->napi.rx_pkt_n);
621+
} while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start));
584622

585-
p = (char *)&snapshot + offsetof(struct stmmac_rxq_stats, rx_pkt_n);
586-
for (stat = 0; stat < STMMAC_RXQ_STATS; stat++) {
587-
*data++ = (*(u64 *)p);
588-
p += sizeof(u64);
589-
}
623+
*data++ = pkt_n;
624+
*data++ = stmmac_get_rx_normal_irq_n(priv, q);
590625
}
591626
}
592627

@@ -645,39 +680,49 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
645680
pos = j;
646681
for (i = 0; i < rx_queues_count; i++) {
647682
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[i];
648-
struct stmmac_rxq_stats snapshot;
683+
struct stmmac_napi_rx_stats snapshot;
684+
u64 n_irq;
649685

650686
j = pos;
651687
do {
652-
start = u64_stats_fetch_begin(&rxq_stats->syncp);
653-
snapshot = *rxq_stats;
654-
} while (u64_stats_fetch_retry(&rxq_stats->syncp, start));
655-
656-
data[j++] += snapshot.rx_pkt_n;
657-
data[j++] += snapshot.rx_normal_irq_n;
658-
normal_irq_n += snapshot.rx_normal_irq_n;
659-
napi_poll += snapshot.napi_poll;
688+
start = u64_stats_fetch_begin(&rxq_stats->napi_syncp);
689+
snapshot = rxq_stats->napi;
690+
} while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start));
691+
692+
data[j++] += u64_stats_read(&snapshot.rx_pkt_n);
693+
n_irq = stmmac_get_rx_normal_irq_n(priv, i);
694+
data[j++] += n_irq;
695+
normal_irq_n += n_irq;
696+
napi_poll += u64_stats_read(&snapshot.poll);
660697
}
661698

662699
pos = j;
663700
for (i = 0; i < tx_queues_count; i++) {
664701
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[i];
665-
struct stmmac_txq_stats snapshot;
702+
struct stmmac_napi_tx_stats napi_snapshot;
703+
struct stmmac_q_tx_stats q_snapshot;
704+
u64 n_irq;
666705

667706
j = pos;
668707
do {
669-
start = u64_stats_fetch_begin(&txq_stats->syncp);
670-
snapshot = *txq_stats;
671-
} while (u64_stats_fetch_retry(&txq_stats->syncp, start));
672-
673-
data[j++] += snapshot.tx_pkt_n;
674-
data[j++] += snapshot.tx_normal_irq_n;
675-
normal_irq_n += snapshot.tx_normal_irq_n;
676-
data[j++] += snapshot.tx_clean;
677-
data[j++] += snapshot.tx_set_ic_bit;
678-
data[j++] += snapshot.tx_tso_frames;
679-
data[j++] += snapshot.tx_tso_nfrags;
680-
napi_poll += snapshot.napi_poll;
708+
start = u64_stats_fetch_begin(&txq_stats->q_syncp);
709+
q_snapshot = txq_stats->q;
710+
} while (u64_stats_fetch_retry(&txq_stats->q_syncp, start));
711+
do {
712+
start = u64_stats_fetch_begin(&txq_stats->napi_syncp);
713+
napi_snapshot = txq_stats->napi;
714+
} while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start));
715+
716+
data[j++] += u64_stats_read(&napi_snapshot.tx_pkt_n);
717+
n_irq = stmmac_get_tx_normal_irq_n(priv, i);
718+
data[j++] += n_irq;
719+
normal_irq_n += n_irq;
720+
data[j++] += u64_stats_read(&napi_snapshot.tx_clean);
721+
data[j++] += u64_stats_read(&q_snapshot.tx_set_ic_bit) +
722+
u64_stats_read(&napi_snapshot.tx_set_ic_bit);
723+
data[j++] += u64_stats_read(&q_snapshot.tx_tso_frames);
724+
data[j++] += u64_stats_read(&q_snapshot.tx_tso_nfrags);
725+
napi_poll += u64_stats_read(&napi_snapshot.poll);
681726
}
682727
normal_irq_n += priv->xstats.rx_early_irq;
683728
data[j++] = normal_irq_n;

0 commit comments

Comments
 (0)