diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index d1bcc8afefa7..0f38346b5ce3 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1342,6 +1342,30 @@ static void mtk_stats_update(struct mtk_eth *eth) } } +/* MTK_GDM1_AF and MTK_GDM2_AF describe a two-GDM frame engine and are the + * only trigger the driver has for draining the counters from the hot path. + * On NETSYS v3 that trigger does not fire, so drain them on a timer as well. + */ +static void mtk_stats_work(struct work_struct *work) +{ + struct delayed_work *del_work = to_delayed_work(work); + struct mtk_eth *eth = container_of(del_work, struct mtk_eth, + stats_work); + + if (test_bit(MTK_HW_INIT, ð->state) && + !test_bit(MTK_RESETTING, ð->state)) { + /* mtk_stats_update() is written for the NAPI path and takes + * stats_lock with spin_trylock(). Keep the lock out of two + * different contexts. + */ + local_bh_disable(); + mtk_stats_update(eth); + local_bh_enable(); + } + + schedule_delayed_work(ð->stats_work, MTK_STATS_DRAIN_TIMEOUT); +} + static void mtk_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *storage) { @@ -5078,6 +5102,7 @@ static int mtk_cleanup(struct mtk_eth *eth) mtk_free_dev(eth); cancel_work_sync(ð->pending_work); cancel_delayed_work_sync(ð->reset.monitor_work); + cancel_delayed_work_sync(ð->stats_work); return 0; } @@ -6134,6 +6159,7 @@ static int mtk_probe(struct platform_device *pdev) INIT_WORK(ð->rx_dim.work, mtk_dim_rx); atomic_set(ð->reset.force, 0); INIT_DELAYED_WORK(ð->reset.monitor_work, mtk_hw_reset_monitor_work); + INIT_DELAYED_WORK(ð->stats_work, mtk_stats_work); eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; INIT_WORK(ð->tx_dim.work, mtk_dim_tx); @@ -6465,6 +6491,7 @@ static int mtk_probe(struct platform_device *pdev) platform_set_drvdata(pdev, eth); schedule_delayed_work(ð->reset.monitor_work, MTK_DMA_MONITOR_TIMEOUT); + schedule_delayed_work(ð->stats_work, MTK_STATS_DRAIN_TIMEOUT); return 0; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 13de4a984d70..7150a7efa42f 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -1478,6 +1478,12 @@ struct mtk_soc_data { #define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000) +/* The GDM MIB counters are clear-on-read and the packet counters are only + * 32 bits wide, so they have to be drained on a schedule rather than only + * when something asks for statistics. + */ +#define MTK_STATS_DRAIN_TIMEOUT msecs_to_jiffies(1000) + /* currently no SoC has more than 3 macs */ #define MTK_MAX_DEVS 3 @@ -1570,6 +1576,9 @@ struct mtk_eth { const struct mtk_soc_data *soc; + /* periodic drain of the clear-on-read GDM MIB counters */ + struct delayed_work stats_work; + spinlock_t dim_lock; u32 rx_events;