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 <rgoyal@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/2179298
Reviewed-by: Bhadram Varka <vbhadram@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Srinivas Ramachandran <srinivasra@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Rakesh Goyal
2019-08-20 16:32:44 +05:30
committed by Revanth Kumar Uppala
parent e93d5e1e55
commit 8708cf6e98
2 changed files with 93 additions and 8 deletions

View File

@@ -16,6 +16,68 @@
#include "ether_linux.h" #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 * @brief Disable all MAC related clks
* *
@@ -1216,6 +1278,9 @@ static int ether_open(struct net_device *dev)
/* start network queues */ /* start network queues */
netif_tx_start_all_queues(pdata->ndev); netif_tx_start_all_queues(pdata->ndev);
/* call function to schedule workqueue */
ether_stats_work_queue_start(pdata);
return ret; return ret;
err_hw_init: err_hw_init:
@@ -1263,6 +1328,9 @@ static int ether_close(struct net_device *dev)
struct ether_priv_data *pdata = netdev_priv(dev); struct ether_priv_data *pdata = netdev_priv(dev);
int ret = 0; int ret = 0;
/* Stop workqueue to get further scheduled */
ether_stats_work_queue_stop(pdata);
/* Stop and disconnect the PHY */ /* Stop and disconnect the PHY */
if (pdata->phydev != NULL) { if (pdata->phydev != NULL) {
phy_stop(pdata->phydev); 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. * 1) Get the number of channels from DT.
* 2) Allocate the network device for those many channels. * 2) Allocate the network device for those many channels.
* 3) Parse MAC and PHY DT. * 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. * 5) Register MDIO bus and network device.
* 6) initialize spinlock * 6) Initialize spinlock.
* 7) Update filter value based on HW feature * 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. * @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)) { if (gpio_is_valid(pdata->phy_reset)) {
gpio_set_value(pdata->phy_reset, OSI_DISABLE); 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; return 0;
@@ -3490,6 +3561,9 @@ static int ether_suspend_noirq(struct device *dev)
if (!netif_running(ndev)) if (!netif_running(ndev))
return 0; return 0;
/* Stop workqueue while DUT is going to suspend state */
ether_stats_work_queue_stop(pdata);
if (pdata->phydev) { if (pdata->phydev) {
phy_stop(pdata->phydev); phy_stop(pdata->phydev);
if (gpio_is_valid(pdata->phy_reset)) if (gpio_is_valid(pdata->phy_reset))
@@ -3580,6 +3654,8 @@ static int ether_resume(struct ether_priv_data *pdata)
phy_start(pdata->phydev); phy_start(pdata->phydev);
/* start network queues */ /* start network queues */
netif_tx_start_all_queues(ndev); netif_tx_start_all_queues(ndev);
/* re-start workqueue */
ether_stats_work_queue_start(pdata);
return 0; return 0;

View File

@@ -22,6 +22,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
@@ -160,6 +161,12 @@ static inline int ether_avail_txdesc_cnt(struct osi_tx_ring *tx_ring)
#define ETHER_MAX_THERM_STATE 5UL #define ETHER_MAX_THERM_STATE 5UL
#endif /* THERMAL_CAL */ #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 * @brief DMA Transmit Channel NAPI
*/ */
@@ -300,14 +307,16 @@ struct ether_priv_data {
raw_spinlock_t ptp_lock; raw_spinlock_t ptp_lock;
/** Clocks enable check */ /** Clocks enable check */
bool clks_enable; bool clks_enable;
/** Delayed work queue to read RMON counters periodically */
struct delayed_work ether_stats_work;
}; };
/** /**
* @brief Set ethtool operations * @brief Set ethtool operations
* *
* @param[in] ndev: network device instance * @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); 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 * @param[in] dev: device instance
* *
* @retval 0 - success, * @retval 0 - success,
* @retval "negative value" - failure. * @retval "negative value" - failure.
*/ */
int ether_sysfs_register(struct device *dev); int ether_sysfs_register(struct device *dev);
@@ -340,7 +349,7 @@ void ether_sysfs_unregister(struct device *dev);
* network device created * network device created
* *
* @retval 0 on success * @retval 0 on success
* @retval "negative value" on Failure * @retval "negative value" on Failure
*/ */
int ether_ptp_init(struct ether_priv_data *pdata); 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 * @note PTP clock driver need to be successfully registered during
* initialization and HW need to support PTP functionality. * initialization and HW need to support PTP functionality.
* *
* @retval 0 on success * @retval 0 on success
* @retval "negative value" on Failure * @retval "negative value" on Failure
*/ */