Commit 133466c3 authored by Jisheng Zhang's avatar Jisheng Zhang Committed by Jakub Kicinski

net: stmmac: use per-queue 64 bit statistics where necessary

Currently, there are two major issues with stmmac driver statistics
First of all, statistics in stmmac_extra_stats, stmmac_rxq_stats
and stmmac_txq_stats are 32 bit variables on 32 bit platforms. This
can cause some stats to overflow after several minutes of
high traffic, for example rx_pkt_n, tx_pkt_n and so on.

Secondly, if HW supports multiqueues, there are frequent cacheline
ping pongs on some driver statistic vars, for example, normal_irq_n,
tx_pkt_n and so on. What's more, frequent cacheline ping pongs on
normal_irq_n happens in ISR, this makes the situation worse.

To improve the driver, we convert those statistics to 64 bit, implement
ndo_get_stats64 and update .get_ethtool_stats implementation
accordingly. We also use per-queue statistics where necessary to remove
the cacheline ping pongs as much as possible to make multiqueue
operations faster. Those statistics which are not possible to overflow
and not frequently updated are kept as is.
Signed-off-by: default avatarJisheng Zhang <jszhang@kernel.org>
Link: https://lore.kernel.org/r/20230717160630.1892-3-jszhang@kernel.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 2eb85b75
...@@ -59,13 +59,25 @@ ...@@ -59,13 +59,25 @@
/* #define FRAME_FILTER_DEBUG */ /* #define FRAME_FILTER_DEBUG */
struct stmmac_txq_stats { struct stmmac_txq_stats {
unsigned long tx_pkt_n; u64 tx_bytes;
unsigned long tx_normal_irq_n; u64 tx_packets;
u64 tx_pkt_n;
u64 tx_normal_irq_n;
u64 napi_poll;
u64 tx_clean;
u64 tx_set_ic_bit;
u64 tx_tso_frames;
u64 tx_tso_nfrags;
struct u64_stats_sync syncp;
}; };
struct stmmac_rxq_stats { struct stmmac_rxq_stats {
unsigned long rx_pkt_n; u64 rx_bytes;
unsigned long rx_normal_irq_n; u64 rx_packets;
u64 rx_pkt_n;
u64 rx_normal_irq_n;
u64 napi_poll;
struct u64_stats_sync syncp;
}; };
/* Extra statistic and debug information exposed by ethtool */ /* Extra statistic and debug information exposed by ethtool */
...@@ -81,6 +93,7 @@ struct stmmac_extra_stats { ...@@ -81,6 +93,7 @@ struct stmmac_extra_stats {
unsigned long tx_frame_flushed; unsigned long tx_frame_flushed;
unsigned long tx_payload_error; unsigned long tx_payload_error;
unsigned long tx_ip_header_error; unsigned long tx_ip_header_error;
unsigned long tx_collision;
/* Receive errors */ /* Receive errors */
unsigned long rx_desc; unsigned long rx_desc;
unsigned long sa_filter_fail; unsigned long sa_filter_fail;
...@@ -113,14 +126,6 @@ struct stmmac_extra_stats { ...@@ -113,14 +126,6 @@ struct stmmac_extra_stats {
/* Tx/Rx IRQ Events */ /* Tx/Rx IRQ Events */
unsigned long rx_early_irq; unsigned long rx_early_irq;
unsigned long threshold; unsigned long threshold;
unsigned long tx_pkt_n;
unsigned long rx_pkt_n;
unsigned long normal_irq_n;
unsigned long rx_normal_irq_n;
unsigned long napi_poll;
unsigned long tx_normal_irq_n;
unsigned long tx_clean;
unsigned long tx_set_ic_bit;
unsigned long irq_receive_pmt_irq_n; unsigned long irq_receive_pmt_irq_n;
/* MMC info */ /* MMC info */
unsigned long mmc_tx_irq_n; unsigned long mmc_tx_irq_n;
...@@ -190,18 +195,16 @@ struct stmmac_extra_stats { ...@@ -190,18 +195,16 @@ struct stmmac_extra_stats {
unsigned long mtl_rx_fifo_ctrl_active; unsigned long mtl_rx_fifo_ctrl_active;
unsigned long mac_rx_frame_ctrl_fifo; unsigned long mac_rx_frame_ctrl_fifo;
unsigned long mac_gmii_rx_proto_engine; unsigned long mac_gmii_rx_proto_engine;
/* TSO */
unsigned long tx_tso_frames;
unsigned long tx_tso_nfrags;
/* EST */ /* EST */
unsigned long mtl_est_cgce; unsigned long mtl_est_cgce;
unsigned long mtl_est_hlbs; unsigned long mtl_est_hlbs;
unsigned long mtl_est_hlbf; unsigned long mtl_est_hlbf;
unsigned long mtl_est_btre; unsigned long mtl_est_btre;
unsigned long mtl_est_btrlm; unsigned long mtl_est_btrlm;
/* per queue statistics */ unsigned long rx_dropped;
struct stmmac_txq_stats txq_stats[MTL_MAX_TX_QUEUES]; unsigned long rx_errors;
struct stmmac_rxq_stats rxq_stats[MTL_MAX_RX_QUEUES]; unsigned long tx_dropped;
unsigned long tx_errors;
}; };
/* Safety Feature statistics exposed by ethtool */ /* Safety Feature statistics exposed by ethtool */
......
...@@ -440,8 +440,10 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, ...@@ -440,8 +440,10 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,
struct stmmac_extra_stats *x, u32 chan, struct stmmac_extra_stats *x, u32 chan,
u32 dir) u32 dir)
{ {
u32 v; struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
int ret = 0; int ret = 0;
u32 v;
v = readl(ioaddr + EMAC_INT_STA); v = readl(ioaddr + EMAC_INT_STA);
...@@ -452,7 +454,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, ...@@ -452,7 +454,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,
if (v & EMAC_TX_INT) { if (v & EMAC_TX_INT) {
ret |= handle_tx; ret |= handle_tx;
x->tx_normal_irq_n++; u64_stats_update_begin(&tx_q->txq_stats.syncp);
tx_q->txq_stats.tx_normal_irq_n++;
u64_stats_update_end(&tx_q->txq_stats.syncp);
} }
if (v & EMAC_TX_DMA_STOP_INT) if (v & EMAC_TX_DMA_STOP_INT)
...@@ -474,7 +478,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, ...@@ -474,7 +478,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,
if (v & EMAC_RX_INT) { if (v & EMAC_RX_INT) {
ret |= handle_rx; ret |= handle_rx;
x->rx_normal_irq_n++; u64_stats_update_begin(&rx_q->rxq_stats.syncp);
rx_q->rxq_stats.rx_normal_irq_n++;
u64_stats_update_end(&rx_q->rxq_stats.syncp);
} }
if (v & EMAC_RX_BUF_UA_INT) if (v & EMAC_RX_BUF_UA_INT)
......
...@@ -82,29 +82,24 @@ static void dwmac100_dump_dma_regs(struct stmmac_priv *priv, ...@@ -82,29 +82,24 @@ static void dwmac100_dump_dma_regs(struct stmmac_priv *priv,
} }
/* DMA controller has two counters to track the number of the missed frames. */ /* DMA controller has two counters to track the number of the missed frames. */
static void dwmac100_dma_diagnostic_fr(struct net_device_stats *stats, static void dwmac100_dma_diagnostic_fr(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
void __iomem *ioaddr) void __iomem *ioaddr)
{ {
u32 csr8 = readl(ioaddr + DMA_MISSED_FRAME_CTR); u32 csr8 = readl(ioaddr + DMA_MISSED_FRAME_CTR);
if (unlikely(csr8)) { if (unlikely(csr8)) {
if (csr8 & DMA_MISSED_FRAME_OVE) { if (csr8 & DMA_MISSED_FRAME_OVE) {
stats->rx_over_errors += 0x800;
x->rx_overflow_cntr += 0x800; x->rx_overflow_cntr += 0x800;
} else { } else {
unsigned int ove_cntr; unsigned int ove_cntr;
ove_cntr = ((csr8 & DMA_MISSED_FRAME_OVE_CNTR) >> 17); ove_cntr = ((csr8 & DMA_MISSED_FRAME_OVE_CNTR) >> 17);
stats->rx_over_errors += ove_cntr;
x->rx_overflow_cntr += ove_cntr; x->rx_overflow_cntr += ove_cntr;
} }
if (csr8 & DMA_MISSED_FRAME_OVE_M) { if (csr8 & DMA_MISSED_FRAME_OVE_M) {
stats->rx_missed_errors += 0xffff;
x->rx_missed_cntr += 0xffff; x->rx_missed_cntr += 0xffff;
} else { } else {
unsigned int miss_f = (csr8 & DMA_MISSED_FRAME_M_CNTR); unsigned int miss_f = (csr8 & DMA_MISSED_FRAME_M_CNTR);
stats->rx_missed_errors += miss_f;
x->rx_missed_cntr += miss_f; x->rx_missed_cntr += miss_f;
} }
} }
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
#include "dwmac4.h" #include "dwmac4.h"
#include "dwmac4_descs.h" #include "dwmac4_descs.h"
static int dwmac4_wrback_get_tx_status(struct net_device_stats *stats, static int dwmac4_wrback_get_tx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p, struct dma_desc *p,
void __iomem *ioaddr) void __iomem *ioaddr)
{ {
...@@ -40,15 +39,13 @@ static int dwmac4_wrback_get_tx_status(struct net_device_stats *stats, ...@@ -40,15 +39,13 @@ static int dwmac4_wrback_get_tx_status(struct net_device_stats *stats,
x->tx_frame_flushed++; x->tx_frame_flushed++;
if (unlikely(tdes3 & TDES3_LOSS_CARRIER)) { if (unlikely(tdes3 & TDES3_LOSS_CARRIER)) {
x->tx_losscarrier++; x->tx_losscarrier++;
stats->tx_carrier_errors++;
} }
if (unlikely(tdes3 & TDES3_NO_CARRIER)) { if (unlikely(tdes3 & TDES3_NO_CARRIER)) {
x->tx_carrier++; x->tx_carrier++;
stats->tx_carrier_errors++;
} }
if (unlikely((tdes3 & TDES3_LATE_COLLISION) || if (unlikely((tdes3 & TDES3_LATE_COLLISION) ||
(tdes3 & TDES3_EXCESSIVE_COLLISION))) (tdes3 & TDES3_EXCESSIVE_COLLISION)))
stats->collisions += x->tx_collision +=
(tdes3 & TDES3_COLLISION_COUNT_MASK) (tdes3 & TDES3_COLLISION_COUNT_MASK)
>> TDES3_COLLISION_COUNT_SHIFT; >> TDES3_COLLISION_COUNT_SHIFT;
...@@ -73,8 +70,7 @@ static int dwmac4_wrback_get_tx_status(struct net_device_stats *stats, ...@@ -73,8 +70,7 @@ static int dwmac4_wrback_get_tx_status(struct net_device_stats *stats,
return ret; return ret;
} }
static int dwmac4_wrback_get_rx_status(struct net_device_stats *stats, static int dwmac4_wrback_get_rx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p) struct dma_desc *p)
{ {
unsigned int rdes1 = le32_to_cpu(p->des1); unsigned int rdes1 = le32_to_cpu(p->des1);
...@@ -93,7 +89,7 @@ static int dwmac4_wrback_get_rx_status(struct net_device_stats *stats, ...@@ -93,7 +89,7 @@ static int dwmac4_wrback_get_rx_status(struct net_device_stats *stats,
if (unlikely(rdes3 & RDES3_ERROR_SUMMARY)) { if (unlikely(rdes3 & RDES3_ERROR_SUMMARY)) {
if (unlikely(rdes3 & RDES3_GIANT_PACKET)) if (unlikely(rdes3 & RDES3_GIANT_PACKET))
stats->rx_length_errors++; x->rx_length++;
if (unlikely(rdes3 & RDES3_OVERFLOW_ERROR)) if (unlikely(rdes3 & RDES3_OVERFLOW_ERROR))
x->rx_gmac_overflow++; x->rx_gmac_overflow++;
...@@ -103,10 +99,8 @@ static int dwmac4_wrback_get_rx_status(struct net_device_stats *stats, ...@@ -103,10 +99,8 @@ static int dwmac4_wrback_get_rx_status(struct net_device_stats *stats,
if (unlikely(rdes3 & RDES3_RECEIVE_ERROR)) if (unlikely(rdes3 & RDES3_RECEIVE_ERROR))
x->rx_mii++; x->rx_mii++;
if (unlikely(rdes3 & RDES3_CRC_ERROR)) { if (unlikely(rdes3 & RDES3_CRC_ERROR))
x->rx_crc_errors++; x->rx_crc_errors++;
stats->rx_crc_errors++;
}
if (unlikely(rdes3 & RDES3_DRIBBLE_ERROR)) if (unlikely(rdes3 & RDES3_DRIBBLE_ERROR))
x->dribbling_bit++; x->dribbling_bit++;
......
...@@ -171,6 +171,8 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, ...@@ -171,6 +171,8 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan)); u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan));
u32 intr_en = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); u32 intr_en = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
int ret = 0; int ret = 0;
if (dir == DMA_DIR_RX) if (dir == DMA_DIR_RX)
...@@ -198,18 +200,19 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, ...@@ -198,18 +200,19 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
} }
} }
/* TX/RX NORMAL interrupts */ /* TX/RX NORMAL interrupts */
if (likely(intr_status & DMA_CHAN_STATUS_NIS))
x->normal_irq_n++;
if (likely(intr_status & DMA_CHAN_STATUS_RI)) { if (likely(intr_status & DMA_CHAN_STATUS_RI)) {
x->rx_normal_irq_n++; u64_stats_update_begin(&rx_q->rxq_stats.syncp);
x->rxq_stats[chan].rx_normal_irq_n++; rx_q->rxq_stats.rx_normal_irq_n++;
u64_stats_update_end(&rx_q->rxq_stats.syncp);
ret |= handle_rx; ret |= handle_rx;
} }
if (likely(intr_status & DMA_CHAN_STATUS_TI)) { if (likely(intr_status & DMA_CHAN_STATUS_TI)) {
x->tx_normal_irq_n++; u64_stats_update_begin(&tx_q->txq_stats.syncp);
x->txq_stats[chan].tx_normal_irq_n++; tx_q->txq_stats.tx_normal_irq_n++;
u64_stats_update_end(&tx_q->txq_stats.syncp);
ret |= handle_tx; ret |= handle_tx;
} }
if (unlikely(intr_status & DMA_CHAN_STATUS_TBU)) if (unlikely(intr_status & DMA_CHAN_STATUS_TBU))
ret |= handle_tx; ret |= handle_tx;
if (unlikely(intr_status & DMA_CHAN_STATUS_ERI)) if (unlikely(intr_status & DMA_CHAN_STATUS_ERI))
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include "common.h" #include "common.h"
#include "dwmac_dma.h" #include "dwmac_dma.h"
#include "stmmac.h"
#define GMAC_HI_REG_AE 0x80000000 #define GMAC_HI_REG_AE 0x80000000
...@@ -161,6 +162,8 @@ static void show_rx_process_state(unsigned int status) ...@@ -161,6 +162,8 @@ static void show_rx_process_state(unsigned int status)
int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_extra_stats *x, u32 chan, u32 dir) struct stmmac_extra_stats *x, u32 chan, u32 dir)
{ {
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
int ret = 0; int ret = 0;
/* read the status register (CSR5) */ /* read the status register (CSR5) */
u32 intr_status = readl(ioaddr + DMA_STATUS); u32 intr_status = readl(ioaddr + DMA_STATUS);
...@@ -208,17 +211,20 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, ...@@ -208,17 +211,20 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
} }
/* TX/RX NORMAL interrupts */ /* TX/RX NORMAL interrupts */
if (likely(intr_status & DMA_STATUS_NIS)) { if (likely(intr_status & DMA_STATUS_NIS)) {
x->normal_irq_n++;
if (likely(intr_status & DMA_STATUS_RI)) { if (likely(intr_status & DMA_STATUS_RI)) {
u32 value = readl(ioaddr + DMA_INTR_ENA); u32 value = readl(ioaddr + DMA_INTR_ENA);
/* to schedule NAPI on real RIE event. */ /* to schedule NAPI on real RIE event. */
if (likely(value & DMA_INTR_ENA_RIE)) { if (likely(value & DMA_INTR_ENA_RIE)) {
x->rx_normal_irq_n++; u64_stats_update_begin(&rx_q->rxq_stats.syncp);
rx_q->rxq_stats.rx_normal_irq_n++;
u64_stats_update_end(&rx_q->rxq_stats.syncp);
ret |= handle_rx; ret |= handle_rx;
} }
} }
if (likely(intr_status & DMA_STATUS_TI)) { if (likely(intr_status & DMA_STATUS_TI)) {
x->tx_normal_irq_n++; u64_stats_update_begin(&tx_q->txq_stats.syncp);
tx_q->txq_stats.tx_normal_irq_n++;
u64_stats_update_end(&tx_q->txq_stats.syncp);
ret |= handle_tx; ret |= handle_tx;
} }
if (unlikely(intr_status & DMA_STATUS_ERI)) if (unlikely(intr_status & DMA_STATUS_ERI))
......
...@@ -8,8 +8,7 @@ ...@@ -8,8 +8,7 @@
#include "common.h" #include "common.h"
#include "dwxgmac2.h" #include "dwxgmac2.h"
static int dwxgmac2_get_tx_status(struct net_device_stats *stats, static int dwxgmac2_get_tx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr) struct dma_desc *p, void __iomem *ioaddr)
{ {
unsigned int tdes3 = le32_to_cpu(p->des3); unsigned int tdes3 = le32_to_cpu(p->des3);
...@@ -23,8 +22,7 @@ static int dwxgmac2_get_tx_status(struct net_device_stats *stats, ...@@ -23,8 +22,7 @@ static int dwxgmac2_get_tx_status(struct net_device_stats *stats,
return ret; return ret;
} }
static int dwxgmac2_get_rx_status(struct net_device_stats *stats, static int dwxgmac2_get_rx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p) struct dma_desc *p)
{ {
unsigned int rdes3 = le32_to_cpu(p->des3); unsigned int rdes3 = le32_to_cpu(p->des3);
......
...@@ -337,6 +337,8 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, ...@@ -337,6 +337,8 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
struct stmmac_extra_stats *x, u32 chan, struct stmmac_extra_stats *x, u32 chan,
u32 dir) u32 dir)
{ {
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
u32 intr_status = readl(ioaddr + XGMAC_DMA_CH_STATUS(chan)); u32 intr_status = readl(ioaddr + XGMAC_DMA_CH_STATUS(chan));
u32 intr_en = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); u32 intr_en = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan));
int ret = 0; int ret = 0;
...@@ -364,16 +366,16 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, ...@@ -364,16 +366,16 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
/* TX/RX NORMAL interrupts */ /* TX/RX NORMAL interrupts */
if (likely(intr_status & XGMAC_NIS)) { if (likely(intr_status & XGMAC_NIS)) {
x->normal_irq_n++;
if (likely(intr_status & XGMAC_RI)) { if (likely(intr_status & XGMAC_RI)) {
x->rx_normal_irq_n++; u64_stats_update_begin(&rx_q->rxq_stats.syncp);
x->rxq_stats[chan].rx_normal_irq_n++; rx_q->rxq_stats.rx_normal_irq_n++;
u64_stats_update_end(&rx_q->rxq_stats.syncp);
ret |= handle_rx; ret |= handle_rx;
} }
if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) { if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) {
x->tx_normal_irq_n++; u64_stats_update_begin(&tx_q->txq_stats.syncp);
x->txq_stats[chan].tx_normal_irq_n++; tx_q->txq_stats.tx_normal_irq_n++;
u64_stats_update_end(&tx_q->txq_stats.syncp);
ret |= handle_tx; ret |= handle_tx;
} }
} }
......
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
#include "common.h" #include "common.h"
#include "descs_com.h" #include "descs_com.h"
static int enh_desc_get_tx_status(struct net_device_stats *stats, static int enh_desc_get_tx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr) struct dma_desc *p, void __iomem *ioaddr)
{ {
unsigned int tdes0 = le32_to_cpu(p->des0); unsigned int tdes0 = le32_to_cpu(p->des0);
...@@ -38,15 +37,13 @@ static int enh_desc_get_tx_status(struct net_device_stats *stats, ...@@ -38,15 +37,13 @@ static int enh_desc_get_tx_status(struct net_device_stats *stats,
if (unlikely(tdes0 & ETDES0_LOSS_CARRIER)) { if (unlikely(tdes0 & ETDES0_LOSS_CARRIER)) {
x->tx_losscarrier++; x->tx_losscarrier++;
stats->tx_carrier_errors++;
} }
if (unlikely(tdes0 & ETDES0_NO_CARRIER)) { if (unlikely(tdes0 & ETDES0_NO_CARRIER)) {
x->tx_carrier++; x->tx_carrier++;
stats->tx_carrier_errors++;
} }
if (unlikely((tdes0 & ETDES0_LATE_COLLISION) || if (unlikely((tdes0 & ETDES0_LATE_COLLISION) ||
(tdes0 & ETDES0_EXCESSIVE_COLLISIONS))) (tdes0 & ETDES0_EXCESSIVE_COLLISIONS)))
stats->collisions += x->tx_collision +=
(tdes0 & ETDES0_COLLISION_COUNT_MASK) >> 3; (tdes0 & ETDES0_COLLISION_COUNT_MASK) >> 3;
if (unlikely(tdes0 & ETDES0_EXCESSIVE_DEFERRAL)) if (unlikely(tdes0 & ETDES0_EXCESSIVE_DEFERRAL))
...@@ -117,8 +114,7 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) ...@@ -117,8 +114,7 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
return ret; return ret;
} }
static void enh_desc_get_ext_status(struct net_device_stats *stats, static void enh_desc_get_ext_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_extended_desc *p) struct dma_extended_desc *p)
{ {
unsigned int rdes0 = le32_to_cpu(p->basic.des0); unsigned int rdes0 = le32_to_cpu(p->basic.des0);
...@@ -182,8 +178,7 @@ static void enh_desc_get_ext_status(struct net_device_stats *stats, ...@@ -182,8 +178,7 @@ static void enh_desc_get_ext_status(struct net_device_stats *stats,
} }
} }
static int enh_desc_get_rx_status(struct net_device_stats *stats, static int enh_desc_get_rx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p) struct dma_desc *p)
{ {
unsigned int rdes0 = le32_to_cpu(p->des0); unsigned int rdes0 = le32_to_cpu(p->des0);
...@@ -193,14 +188,14 @@ static int enh_desc_get_rx_status(struct net_device_stats *stats, ...@@ -193,14 +188,14 @@ static int enh_desc_get_rx_status(struct net_device_stats *stats,
return dma_own; return dma_own;
if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) { if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
stats->rx_length_errors++; x->rx_length++;
return discard_frame; return discard_frame;
} }
if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) { if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) {
x->rx_desc++; x->rx_desc++;
stats->rx_length_errors++; x->rx_length++;
} }
if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR)) if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR))
x->rx_gmac_overflow++; x->rx_gmac_overflow++;
...@@ -209,7 +204,7 @@ static int enh_desc_get_rx_status(struct net_device_stats *stats, ...@@ -209,7 +204,7 @@ static int enh_desc_get_rx_status(struct net_device_stats *stats,
pr_err("\tIPC Csum Error/Giant frame\n"); pr_err("\tIPC Csum Error/Giant frame\n");
if (unlikely(rdes0 & RDES0_COLLISION)) if (unlikely(rdes0 & RDES0_COLLISION))
stats->collisions++; x->rx_collision++;
if (unlikely(rdes0 & RDES0_RECEIVE_WATCHDOG)) if (unlikely(rdes0 & RDES0_RECEIVE_WATCHDOG))
x->rx_watchdog++; x->rx_watchdog++;
...@@ -218,7 +213,6 @@ static int enh_desc_get_rx_status(struct net_device_stats *stats, ...@@ -218,7 +213,6 @@ static int enh_desc_get_rx_status(struct net_device_stats *stats,
if (unlikely(rdes0 & RDES0_CRC_ERROR)) { if (unlikely(rdes0 & RDES0_CRC_ERROR)) {
x->rx_crc_errors++; x->rx_crc_errors++;
stats->rx_crc_errors++;
} }
ret = discard_frame; ret = discard_frame;
} }
......
...@@ -57,8 +57,7 @@ struct stmmac_desc_ops { ...@@ -57,8 +57,7 @@ struct stmmac_desc_ops {
/* Last tx segment reports the transmit status */ /* Last tx segment reports the transmit status */
int (*get_tx_ls)(struct dma_desc *p); int (*get_tx_ls)(struct dma_desc *p);
/* Return the transmit status looking at the TDES1 */ /* Return the transmit status looking at the TDES1 */
int (*tx_status)(struct net_device_stats *stats, int (*tx_status)(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr); struct dma_desc *p, void __iomem *ioaddr);
/* Get the buffer size from the descriptor */ /* Get the buffer size from the descriptor */
int (*get_tx_len)(struct dma_desc *p); int (*get_tx_len)(struct dma_desc *p);
...@@ -67,11 +66,9 @@ struct stmmac_desc_ops { ...@@ -67,11 +66,9 @@ struct stmmac_desc_ops {
/* Get the receive frame size */ /* Get the receive frame size */
int (*get_rx_frame_len)(struct dma_desc *p, int rx_coe_type); int (*get_rx_frame_len)(struct dma_desc *p, int rx_coe_type);
/* Return the reception status looking at the RDES1 */ /* Return the reception status looking at the RDES1 */
int (*rx_status)(struct net_device_stats *stats, int (*rx_status)(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p); struct dma_desc *p);
void (*rx_extended_status)(struct net_device_stats *stats, void (*rx_extended_status)(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_extended_desc *p); struct dma_extended_desc *p);
/* Set tx timestamp enable bit */ /* Set tx timestamp enable bit */
void (*enable_tx_timestamp) (struct dma_desc *p); void (*enable_tx_timestamp) (struct dma_desc *p);
...@@ -191,8 +188,7 @@ struct stmmac_dma_ops { ...@@ -191,8 +188,7 @@ struct stmmac_dma_ops {
void (*dma_tx_mode)(struct stmmac_priv *priv, void __iomem *ioaddr, void (*dma_tx_mode)(struct stmmac_priv *priv, void __iomem *ioaddr,
int mode, u32 channel, int fifosz, u8 qmode); int mode, u32 channel, int fifosz, u8 qmode);
/* To track extra statistic (if supported) */ /* To track extra statistic (if supported) */
void (*dma_diagnostic_fr)(struct net_device_stats *stats, void (*dma_diagnostic_fr)(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
void __iomem *ioaddr); void __iomem *ioaddr);
void (*enable_dma_transmission) (void __iomem *ioaddr); void (*enable_dma_transmission) (void __iomem *ioaddr);
void (*enable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr, void (*enable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr,
......
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
#include "common.h" #include "common.h"
#include "descs_com.h" #include "descs_com.h"
static int ndesc_get_tx_status(struct net_device_stats *stats, static int ndesc_get_tx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr) struct dma_desc *p, void __iomem *ioaddr)
{ {
unsigned int tdes0 = le32_to_cpu(p->des0); unsigned int tdes0 = le32_to_cpu(p->des0);
...@@ -31,15 +30,12 @@ static int ndesc_get_tx_status(struct net_device_stats *stats, ...@@ -31,15 +30,12 @@ static int ndesc_get_tx_status(struct net_device_stats *stats,
if (unlikely(tdes0 & TDES0_ERROR_SUMMARY)) { if (unlikely(tdes0 & TDES0_ERROR_SUMMARY)) {
if (unlikely(tdes0 & TDES0_UNDERFLOW_ERROR)) { if (unlikely(tdes0 & TDES0_UNDERFLOW_ERROR)) {
x->tx_underflow++; x->tx_underflow++;
stats->tx_fifo_errors++;
} }
if (unlikely(tdes0 & TDES0_NO_CARRIER)) { if (unlikely(tdes0 & TDES0_NO_CARRIER)) {
x->tx_carrier++; x->tx_carrier++;
stats->tx_carrier_errors++;
} }
if (unlikely(tdes0 & TDES0_LOSS_CARRIER)) { if (unlikely(tdes0 & TDES0_LOSS_CARRIER)) {
x->tx_losscarrier++; x->tx_losscarrier++;
stats->tx_carrier_errors++;
} }
if (unlikely((tdes0 & TDES0_EXCESSIVE_DEFERRAL) || if (unlikely((tdes0 & TDES0_EXCESSIVE_DEFERRAL) ||
(tdes0 & TDES0_EXCESSIVE_COLLISIONS) || (tdes0 & TDES0_EXCESSIVE_COLLISIONS) ||
...@@ -47,7 +43,7 @@ static int ndesc_get_tx_status(struct net_device_stats *stats, ...@@ -47,7 +43,7 @@ static int ndesc_get_tx_status(struct net_device_stats *stats,
unsigned int collisions; unsigned int collisions;
collisions = (tdes0 & TDES0_COLLISION_COUNT_MASK) >> 3; collisions = (tdes0 & TDES0_COLLISION_COUNT_MASK) >> 3;
stats->collisions += collisions; x->tx_collision += collisions;
} }
ret = tx_err; ret = tx_err;
} }
...@@ -70,8 +66,7 @@ static int ndesc_get_tx_len(struct dma_desc *p) ...@@ -70,8 +66,7 @@ static int ndesc_get_tx_len(struct dma_desc *p)
* and, if required, updates the multicast statistics. * and, if required, updates the multicast statistics.
* In case of success, it returns good_frame because the GMAC device * In case of success, it returns good_frame because the GMAC device
* is supposed to be able to compute the csum in HW. */ * is supposed to be able to compute the csum in HW. */
static int ndesc_get_rx_status(struct net_device_stats *stats, static int ndesc_get_rx_status(struct stmmac_extra_stats *x,
struct stmmac_extra_stats *x,
struct dma_desc *p) struct dma_desc *p)
{ {
int ret = good_frame; int ret = good_frame;
...@@ -81,7 +76,7 @@ static int ndesc_get_rx_status(struct net_device_stats *stats, ...@@ -81,7 +76,7 @@ static int ndesc_get_rx_status(struct net_device_stats *stats,
return dma_own; return dma_own;
if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) { if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
stats->rx_length_errors++; x->rx_length++;
return discard_frame; return discard_frame;
} }
...@@ -96,11 +91,9 @@ static int ndesc_get_rx_status(struct net_device_stats *stats, ...@@ -96,11 +91,9 @@ static int ndesc_get_rx_status(struct net_device_stats *stats,
x->ipc_csum_error++; x->ipc_csum_error++;
if (unlikely(rdes0 & RDES0_COLLISION)) { if (unlikely(rdes0 & RDES0_COLLISION)) {
x->rx_collision++; x->rx_collision++;
stats->collisions++;
} }
if (unlikely(rdes0 & RDES0_CRC_ERROR)) { if (unlikely(rdes0 & RDES0_CRC_ERROR)) {
x->rx_crc_errors++; x->rx_crc_errors++;
stats->rx_crc_errors++;
} }
ret = discard_frame; ret = discard_frame;
} }
......
...@@ -77,6 +77,7 @@ struct stmmac_tx_queue { ...@@ -77,6 +77,7 @@ struct stmmac_tx_queue {
dma_addr_t dma_tx_phy; dma_addr_t dma_tx_phy;
dma_addr_t tx_tail_addr; dma_addr_t tx_tail_addr;
u32 mss; u32 mss;
struct stmmac_txq_stats txq_stats;
}; };
struct stmmac_rx_buffer { struct stmmac_rx_buffer {
...@@ -121,6 +122,7 @@ struct stmmac_rx_queue { ...@@ -121,6 +122,7 @@ struct stmmac_rx_queue {
unsigned int len; unsigned int len;
unsigned int error; unsigned int error;
} state; } state;
struct stmmac_rxq_stats rxq_stats;
}; };
struct stmmac_channel { struct stmmac_channel {
......
...@@ -89,14 +89,6 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { ...@@ -89,14 +89,6 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
/* Tx/Rx IRQ Events */ /* Tx/Rx IRQ Events */
STMMAC_STAT(rx_early_irq), STMMAC_STAT(rx_early_irq),
STMMAC_STAT(threshold), STMMAC_STAT(threshold),
STMMAC_STAT(tx_pkt_n),
STMMAC_STAT(rx_pkt_n),
STMMAC_STAT(normal_irq_n),
STMMAC_STAT(rx_normal_irq_n),
STMMAC_STAT(napi_poll),
STMMAC_STAT(tx_normal_irq_n),
STMMAC_STAT(tx_clean),
STMMAC_STAT(tx_set_ic_bit),
STMMAC_STAT(irq_receive_pmt_irq_n), STMMAC_STAT(irq_receive_pmt_irq_n),
/* MMC info */ /* MMC info */
STMMAC_STAT(mmc_tx_irq_n), STMMAC_STAT(mmc_tx_irq_n),
...@@ -163,9 +155,6 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { ...@@ -163,9 +155,6 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(mtl_rx_fifo_ctrl_active), STMMAC_STAT(mtl_rx_fifo_ctrl_active),
STMMAC_STAT(mac_rx_frame_ctrl_fifo), STMMAC_STAT(mac_rx_frame_ctrl_fifo),
STMMAC_STAT(mac_gmii_rx_proto_engine), STMMAC_STAT(mac_gmii_rx_proto_engine),
/* TSO */
STMMAC_STAT(tx_tso_frames),
STMMAC_STAT(tx_tso_nfrags),
/* EST */ /* EST */
STMMAC_STAT(mtl_est_cgce), STMMAC_STAT(mtl_est_cgce),
STMMAC_STAT(mtl_est_hlbs), STMMAC_STAT(mtl_est_hlbs),
...@@ -175,6 +164,23 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { ...@@ -175,6 +164,23 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
}; };
#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats) #define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
/* statistics collected in queue which will be summed up for all TX or RX
* queues, or summed up for both TX and RX queues(napi_poll, normal_irq_n).
*/
static const char stmmac_qstats_string[][ETH_GSTRING_LEN] = {
"rx_pkt_n",
"rx_normal_irq_n",
"tx_pkt_n",
"tx_normal_irq_n",
"tx_clean",
"tx_set_ic_bit",
"tx_tso_frames",
"tx_tso_nfrags",
"normal_irq_n",
"napi_poll",
};
#define STMMAC_QSTATS ARRAY_SIZE(stmmac_qstats_string)
/* HW MAC Management counters (if supported) */ /* HW MAC Management counters (if supported) */
#define STMMAC_MMC_STAT(m) \ #define STMMAC_MMC_STAT(m) \
{ #m, sizeof_field(struct stmmac_counters, m), \ { #m, sizeof_field(struct stmmac_counters, m), \
...@@ -535,23 +541,44 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data) ...@@ -535,23 +541,44 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data)
{ {
u32 tx_cnt = priv->plat->tx_queues_to_use; u32 tx_cnt = priv->plat->tx_queues_to_use;
u32 rx_cnt = priv->plat->rx_queues_to_use; u32 rx_cnt = priv->plat->rx_queues_to_use;
unsigned int start;
int q, stat; int q, stat;
u64 *pos;
char *p; char *p;
pos = data;
for (q = 0; q < tx_cnt; q++) { for (q = 0; q < tx_cnt; q++) {
p = (char *)priv + offsetof(struct stmmac_priv, struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[q];
xstats.txq_stats[q].tx_pkt_n); struct stmmac_txq_stats snapshot;
data = pos;
do {
start = u64_stats_fetch_begin(&tx_q->txq_stats.syncp);
snapshot = tx_q->txq_stats;
} while (u64_stats_fetch_retry(&tx_q->txq_stats.syncp, start));
p = (char *)&snapshot + offsetof(struct stmmac_txq_stats, tx_pkt_n);
for (stat = 0; stat < STMMAC_TXQ_STATS; stat++) { for (stat = 0; stat < STMMAC_TXQ_STATS; stat++) {
*data++ = (*(unsigned long *)p); *data++ += (*(u64 *)p);
p += sizeof(unsigned long); p += sizeof(u64);
} }
} }
pos = data;
for (q = 0; q < rx_cnt; q++) { for (q = 0; q < rx_cnt; q++) {
p = (char *)priv + offsetof(struct stmmac_priv, struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[q];
xstats.rxq_stats[q].rx_pkt_n); struct stmmac_rxq_stats snapshot;
data = pos;
do {
start = u64_stats_fetch_begin(&rx_q->rxq_stats.syncp);
snapshot = rx_q->rxq_stats;
} while (u64_stats_fetch_retry(&rx_q->rxq_stats.syncp, start));
p = (char *)&snapshot + offsetof(struct stmmac_rxq_stats, rx_pkt_n);
for (stat = 0; stat < STMMAC_RXQ_STATS; stat++) { for (stat = 0; stat < STMMAC_RXQ_STATS; stat++) {
*data++ = (*(unsigned long *)p); *data++ += (*(u64 *)p);
p += sizeof(unsigned long); p += sizeof(u64);
} }
} }
} }
...@@ -562,8 +589,10 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, ...@@ -562,8 +589,10 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
struct stmmac_priv *priv = netdev_priv(dev); struct stmmac_priv *priv = netdev_priv(dev);
u32 rx_queues_count = priv->plat->rx_queues_to_use; u32 rx_queues_count = priv->plat->rx_queues_to_use;
u32 tx_queues_count = priv->plat->tx_queues_to_use; u32 tx_queues_count = priv->plat->tx_queues_to_use;
u64 napi_poll = 0, normal_irq_n = 0;
int i, j = 0, pos, ret;
unsigned long count; unsigned long count;
int i, j = 0, ret; unsigned int start;
if (priv->dma_cap.asp) { if (priv->dma_cap.asp) {
for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) { for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
...@@ -574,8 +603,7 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, ...@@ -574,8 +603,7 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
} }
/* Update the DMA HW counters for dwmac10/100 */ /* Update the DMA HW counters for dwmac10/100 */
ret = stmmac_dma_diagnostic_fr(priv, &dev->stats, (void *) &priv->xstats, ret = stmmac_dma_diagnostic_fr(priv, &priv->xstats, priv->ioaddr);
priv->ioaddr);
if (ret) { if (ret) {
/* If supported, for new GMAC chips expose the MMC counters */ /* If supported, for new GMAC chips expose the MMC counters */
if (priv->dma_cap.rmon) { if (priv->dma_cap.rmon) {
...@@ -606,6 +634,48 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, ...@@ -606,6 +634,48 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
data[j++] = (stmmac_gstrings_stats[i].sizeof_stat == data[j++] = (stmmac_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p); sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p);
} }
pos = j;
for (i = 0; i < rx_queues_count; i++) {
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[i];
struct stmmac_rxq_stats snapshot;
j = pos;
do {
start = u64_stats_fetch_begin(&rx_q->rxq_stats.syncp);
snapshot = rx_q->rxq_stats;
} while (u64_stats_fetch_retry(&rx_q->rxq_stats.syncp, start));
data[j++] += snapshot.rx_pkt_n;
data[j++] += snapshot.rx_normal_irq_n;
normal_irq_n += snapshot.rx_normal_irq_n;
napi_poll += snapshot.napi_poll;
}
pos = j;
for (i = 0; i < tx_queues_count; i++) {
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[i];
struct stmmac_txq_stats snapshot;
j = pos;
do {
start = u64_stats_fetch_begin(&tx_q->txq_stats.syncp);
snapshot = tx_q->txq_stats;
} while (u64_stats_fetch_retry(&tx_q->txq_stats.syncp, start));
data[j++] += snapshot.tx_pkt_n;
data[j++] += snapshot.tx_normal_irq_n;
normal_irq_n += snapshot.tx_normal_irq_n;
data[j++] += snapshot.tx_clean;
data[j++] += snapshot.tx_set_ic_bit;
data[j++] += snapshot.tx_tso_frames;
data[j++] += snapshot.tx_tso_nfrags;
napi_poll += snapshot.napi_poll;
}
normal_irq_n += priv->xstats.rx_early_irq;
data[j++] = normal_irq_n;
data[j++] = napi_poll;
stmmac_get_per_qstats(priv, &data[j]); stmmac_get_per_qstats(priv, &data[j]);
} }
...@@ -618,7 +688,7 @@ static int stmmac_get_sset_count(struct net_device *netdev, int sset) ...@@ -618,7 +688,7 @@ static int stmmac_get_sset_count(struct net_device *netdev, int sset)
switch (sset) { switch (sset) {
case ETH_SS_STATS: case ETH_SS_STATS:
len = STMMAC_STATS_LEN + len = STMMAC_STATS_LEN + STMMAC_QSTATS +
STMMAC_TXQ_STATS * tx_cnt + STMMAC_TXQ_STATS * tx_cnt +
STMMAC_RXQ_STATS * rx_cnt; STMMAC_RXQ_STATS * rx_cnt;
...@@ -691,8 +761,11 @@ static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data) ...@@ -691,8 +761,11 @@ static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
p += ETH_GSTRING_LEN; p += ETH_GSTRING_LEN;
} }
for (i = 0; i < STMMAC_STATS_LEN; i++) { for (i = 0; i < STMMAC_STATS_LEN; i++) {
memcpy(p, stmmac_gstrings_stats[i].stat_string, memcpy(p, stmmac_gstrings_stats[i].stat_string, ETH_GSTRING_LEN);
ETH_GSTRING_LEN); p += ETH_GSTRING_LEN;
}
for (i = 0; i < STMMAC_QSTATS; i++) {
memcpy(p, stmmac_qstats_string[i], ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN; p += ETH_GSTRING_LEN;
} }
stmmac_get_qstats_string(priv, p); stmmac_get_qstats_string(priv, p);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment