From 8708cf6e986bb5edce42601c5ed9ed77c5335754 Mon Sep 17 00:00:00 2001 From: Rakesh Goyal Date: Tue, 20 Aug 2019 16:32:44 +0530 Subject: [PATCH] nvethernet: read ethernet hw counters periodically HW MMC registers are 32 bits in size. Some of these will overrun in few secs if there are live traffic at line rate. Scheduling work queue to get periodic value from MMC HW registers and update in 64 bits local variables, will be solution to this HW limitation. Bug 200544686 Change-Id: Ifc358f3f6b50839f7d9f48c2f98cb2cdd9ac0821 Signed-off-by: Rakesh Goyal Reviewed-on: https://git-master.nvidia.com/r/2179298 Reviewed-by: Bhadram Varka GVS: Gerrit_Virtual_Submit Reviewed-by: Srinivas Ramachandran Reviewed-by: Ashutosh Jha Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../ethernet/nvidia/nvethernet/ether_linux.c | 82 ++++++++++++++++++- .../ethernet/nvidia/nvethernet/ether_linux.h | 19 +++-- 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 2748479f..4e3e4734 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -16,6 +16,68 @@ #include "ether_linux.h" +/** + * @brief Work Queue function to call osi_read_mmc() periodically. + * + * Algorithm: call osi_read_mmc in periodic manner to avoid possibility of + * overrun of 32 bit MMC hw registers. + * + * @param[in] work: work structure + * + * @note MAC and PHY need to be initialized. + */ +static inline void ether_stats_work_func(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct ether_priv_data *pdata = container_of(dwork, + struct ether_priv_data, ether_stats_work); + struct osi_core_priv_data *osi_core = pdata->osi_core; + int ret; + + ret = osi_read_mmc(osi_core); + if (ret < 0) { + dev_err(pdata->dev, "failed to read MMC counters %s\n", + __func__); + } + schedule_delayed_work(&pdata->ether_stats_work, + msecs_to_jiffies(ETHER_STATS_TIMER * 1000)); +} + +/** + * @brief Start delayed workqueue. + * + * Algorithm: Start workqueue to read RMON HW counters periodically. + * Workqueue will get schedule in every ETHER_STATS_TIMER sec. + * Workqueue will be scheduled only if HW supports RMON HW counters. + * + * @param[in] pdata:OSD private data + * + * @note MAC and PHY need to be initialized. + */ +static inline void ether_stats_work_queue_start(struct ether_priv_data *pdata) +{ + if (pdata->hw_feat.mmc_sel == OSI_ENABLE) { + schedule_delayed_work(&pdata->ether_stats_work, + msecs_to_jiffies(ETHER_STATS_TIMER * + 1000)); + } +} + +/** + * @brief Stop delayed workqueue. + * + * Algorithm: + * Cancel workqueue. + * + * @param[in] pdata:OSD private data + */ +static inline void ether_stats_work_queue_stop(struct ether_priv_data *pdata) +{ + if (pdata->hw_feat.mmc_sel == OSI_ENABLE) { + cancel_delayed_work_sync(&pdata->ether_stats_work); + } +} + /** * @brief Disable all MAC related clks * @@ -1216,6 +1278,9 @@ static int ether_open(struct net_device *dev) /* start network queues */ netif_tx_start_all_queues(pdata->ndev); + /* call function to schedule workqueue */ + ether_stats_work_queue_start(pdata); + return ret; err_hw_init: @@ -1263,6 +1328,9 @@ static int ether_close(struct net_device *dev) struct ether_priv_data *pdata = netdev_priv(dev); int ret = 0; + /* Stop workqueue to get further scheduled */ + ether_stats_work_queue_stop(pdata); + /* Stop and disconnect the PHY */ if (pdata->phydev != NULL) { phy_stop(pdata->phydev); @@ -3257,10 +3325,11 @@ static void init_filter_values(struct ether_priv_data *pdata) * 1) Get the number of channels from DT. * 2) Allocate the network device for those many channels. * 3) Parse MAC and PHY DT. - * 4) Get all required clks/reset/IRQ's + * 4) Get all required clks/reset/IRQ's. * 5) Register MDIO bus and network device. - * 6) initialize spinlock - * 7) Update filter value based on HW feature + * 6) Initialize spinlock. + * 7) Update filter value based on HW feature. + * 8) Initialize Workqueue to read MMC counters periodically. * * @param[in] pdev: platform device associated with platform driver. * @@ -3421,6 +3490,8 @@ static int ether_probe(struct platform_device *pdev) if (gpio_is_valid(pdata->phy_reset)) { gpio_set_value(pdata->phy_reset, OSI_DISABLE); } + /* Initialization of delayed workqueue */ + INIT_DELAYED_WORK(&pdata->ether_stats_work, ether_stats_work_func); return 0; @@ -3490,6 +3561,9 @@ static int ether_suspend_noirq(struct device *dev) if (!netif_running(ndev)) return 0; + /* Stop workqueue while DUT is going to suspend state */ + ether_stats_work_queue_stop(pdata); + if (pdata->phydev) { phy_stop(pdata->phydev); if (gpio_is_valid(pdata->phy_reset)) @@ -3580,6 +3654,8 @@ static int ether_resume(struct ether_priv_data *pdata) phy_start(pdata->phydev); /* start network queues */ netif_tx_start_all_queues(ndev); + /* re-start workqueue */ + ether_stats_work_queue_start(pdata); return 0; diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 0d8cdabb..3ac4f218 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -160,6 +161,12 @@ static inline int ether_avail_txdesc_cnt(struct osi_tx_ring *tx_ring) #define ETHER_MAX_THERM_STATE 5UL #endif /* THERMAL_CAL */ +/** + * @brief Timer to trigger Work queue periodically which read HW counters + * and store locally. If data is at line rate, 2^32 entry get will filled in + * 36 second for 1 G interface and 3.6 sec for 10 G interface. + */ +#define ETHER_STATS_TIMER 3U /** * @brief DMA Transmit Channel NAPI */ @@ -300,14 +307,16 @@ struct ether_priv_data { raw_spinlock_t ptp_lock; /** Clocks enable check */ bool clks_enable; + /** Delayed work queue to read RMON counters periodically */ + struct delayed_work ether_stats_work; }; /** * @brief Set ethtool operations - * + * * @param[in] ndev: network device instance * - * @note Network device needs to created. + * @note Network device needs to created. */ void ether_set_ethtool_ops(struct net_device *ndev); /** @@ -315,7 +324,7 @@ void ether_set_ethtool_ops(struct net_device *ndev); * * @param[in] dev: device instance * - * @retval 0 - success, + * @retval 0 - success, * @retval "negative value" - failure. */ int ether_sysfs_register(struct device *dev); @@ -340,7 +349,7 @@ void ether_sysfs_unregister(struct device *dev); * network device created * * @retval 0 on success - * @retval "negative value" on Failure + * @retval "negative value" on Failure */ int ether_ptp_init(struct ether_priv_data *pdata); @@ -365,7 +374,7 @@ void ether_ptp_remove(struct ether_priv_data *pdata); * * @note PTP clock driver need to be successfully registered during * initialization and HW need to support PTP functionality. - * + * * @retval 0 on success * @retval "negative value" on Failure */