From 975e946c85e5b5b0f015e89b78595ec607ed4d5a Mon Sep 17 00:00:00 2001 From: nannaiah Date: Sun, 25 Apr 2021 21:47:54 -0700 Subject: [PATCH] nvethernet: Add workqueue to set Rx mode. Issue: Network RTNL lock disables scheduler which causes IVC hang. Fix: - Add workqueue to set Rx mode. - Change IVC spinlock to workqueue. - Add virtualization check for macsec clk init. - Add Read & Write Register. Bug 2694285 Change-Id: I8354b500c62c0145eeed9a66bfcc8227fc8999e7 Signed-off-by: Nagaraj annaiah Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2520309 --- .../ethernet/nvidia/nvethernet/ether_linux.c | 42 ++++++++++++++---- .../ethernet/nvidia/nvethernet/ether_linux.h | 6 ++- .../net/ethernet/nvidia/nvethernet/ioctl.c | 14 ++++++ .../net/ethernet/nvidia/nvethernet/ioctl.h | 2 + .../net/ethernet/nvidia/nvethernet/macsec.c | 44 ++++++++++--------- drivers/net/ethernet/nvidia/nvethernet/osd.c | 6 +-- .../net/ethernet/nvidia/nvethernet/sysfs.c | 9 +++- 7 files changed, 88 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 99a1253d..3a96a7f7 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -1118,7 +1118,7 @@ static void ether_start_ivc(struct ether_priv_data *pdata) } ictxt->ivc_state = 1; // initialize - spin_lock_init(&ictxt->ivck_lock); + mutex_init(&ictxt->ivck_lock); } } @@ -2532,6 +2532,8 @@ static int ether_close(struct net_device *ndev) /* MAC deinit which inturn stop MAC Tx,Rx */ osi_hw_core_deinit(pdata->osi_core); + cancel_work_sync(&pdata->set_rx_mode_work); + ether_stop_ivc(pdata); if (pdata->xpcs_rst) { @@ -3136,23 +3138,23 @@ static int ether_prepare_uc_list(struct net_device *dev, } /** - * @brief This function is used to set RX mode. + * @brief Work Queue function to call rx mode. * - * Algorithm: Based on Network interface flag, MAC registers are programmed to - * set mode. - * - * @param[in] dev - pointer to net_device structure. + * @param[in] work: work structure * * @note MAC and PHY need to be initialized. */ -void ether_set_rx_mode(struct net_device *dev) +static inline void set_rx_mode_work_func(struct work_struct *work) { - struct ether_priv_data *pdata = netdev_priv(dev); + struct ether_priv_data *pdata = container_of(work, + struct ether_priv_data, set_rx_mode_work); struct osi_core_priv_data *osi_core = pdata->osi_core; /* store last call last_uc_filter_index in temporary variable */ struct osi_ioctl ioctl_data = {}; + struct net_device *dev = pdata->ndev; int ret = -1; + mutex_lock(&pdata->rx_mode_lock); memset(&ioctl_data.l2_filter, 0x0, sizeof(struct osi_filter)); if ((dev->flags & IFF_PROMISC) == IFF_PROMISC) { if (pdata->promisc_mode == OSI_ENABLE) { @@ -3172,6 +3174,7 @@ void ether_set_rx_mode(struct net_device *dev) "Promiscuous mode not supported\n"); } + mutex_unlock(&pdata->rx_mode_lock); return; } else if ((dev->flags & IFF_ALLMULTI) == IFF_ALLMULTI) { ioctl_data.l2_filter.oper_mode = (OSI_OPER_EN_ALLMULTI | @@ -3184,6 +3187,7 @@ void ether_set_rx_mode(struct net_device *dev) dev_err(pdata->dev, "Setting All Multicast allow mode failed\n"); } + mutex_unlock(&pdata->rx_mode_lock); return; } else if (!netdev_mc_empty(dev)) { /*MC list will be always there, invalidate list only once*/ @@ -3206,6 +3210,7 @@ void ether_set_rx_mode(struct net_device *dev) if (ret < 0) { dev_err(pdata->dev, "Invalidating expired L2 filter failed\n"); + mutex_unlock(&pdata->rx_mode_lock); return; } @@ -3223,9 +3228,27 @@ void ether_set_rx_mode(struct net_device *dev) dev_err(pdata->dev, "failed to set operation mode\n"); } + mutex_unlock(&pdata->rx_mode_lock); return; } +/** + * @brief This function is used to set RX mode. + * + * Algorithm: Based on Network interface flag, MAC registers are programmed to + * set mode. + * + * @param[in] dev - pointer to net_device structure. + * + * @note MAC and PHY need to be initialized. + */ +void ether_set_rx_mode(struct net_device *dev) +{ + struct ether_priv_data *pdata = netdev_priv(dev); + + schedule_work(&pdata->set_rx_mode_work); +} + /** * @brief Network stack IOCTL hook to driver * @@ -5582,6 +5605,9 @@ static int ether_probe(struct platform_device *pdev) /* Initialization of delayed workqueue */ INIT_DELAYED_WORK(&pdata->ether_stats_work, ether_stats_work_func); + mutex_init(&pdata->rx_mode_lock); + /* Initialization of delayed workqueue */ + INIT_WORK(&pdata->set_rx_mode_work, set_rx_mode_work_func); osi_core->hw_feature = &pdata->hw_feat; INIT_LIST_HEAD(&pdata->mac_addr_list_head); diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index f6511d6b..2dcbb09b 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -281,7 +281,7 @@ struct ether_ivc_ctxt { /** ivc cookie */ struct tegra_hv_ivc_cookie *ivck; /** ivc lock */ - spinlock_t ivck_lock; + struct mutex ivck_lock; /** ivc work */ struct work_struct ivc_work; /** wait for event */ @@ -440,6 +440,10 @@ struct ether_priv_data { unsigned int promisc_mode; /** Delayed work queue to read RMON counters periodically */ struct delayed_work ether_stats_work; + /** process rx work */ + struct work_struct set_rx_mode_work; + /** rx lock */ + struct mutex rx_mode_lock; /** Flag to check if EEE LPI is enabled for the MAC */ unsigned int eee_enabled; /** Flag to check if EEE LPI is active currently */ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ioctl.c b/drivers/net/ethernet/nvidia/nvethernet/ioctl.c index b2b11434..57788c45 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ioctl.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ioctl.c @@ -1052,6 +1052,7 @@ int ether_handle_priv_ioctl(struct net_device *ndev, struct ether_ifr_data ifdata; struct osi_core_priv_data *osi_core = pdata->osi_core; int ret = -EOPNOTSUPP; + struct osi_ioctl ioctl_data = {}; if (copy_from_user(&ifdata, ifr->ifr_data, sizeof(ifdata)) != 0U) { dev_err(pdata->dev, "%s(): copy_from_user failed %d\n" @@ -1177,6 +1178,19 @@ int ether_handle_priv_ioctl(struct net_device *ndev, case ETHER_CONFIG_FPE: ret = ether_config_fpe(ndev, &ifdata); break; + case ETHER_READ_REG: + ioctl_data.cmd = OSI_CMD_READ_REG; + ioctl_data.arg1_u32 = ifdata.if_flags; + ret = osi_handle_ioctl(pdata->osi_core, &ioctl_data); + ifdata.qinx = ret; + break; + case ETHER_WRITE_REG: + ioctl_data.cmd = OSI_CMD_WRITE_REG; + ioctl_data.arg1_u32 = ifdata.qinx; + ioctl_data.arg2_u32 = ifdata.if_flags; + ret = osi_handle_ioctl(pdata->osi_core, &ioctl_data); + ifdata.qinx = ret; + break; default: break; } diff --git a/drivers/net/ethernet/nvidia/nvethernet/ioctl.h b/drivers/net/ethernet/nvidia/nvethernet/ioctl.h index 00e23782..9b89ab50 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ioctl.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ioctl.h @@ -68,6 +68,8 @@ /* FRP Command */ #define ETHER_CONFIG_FRP_CMD 51 #define ETHER_MC_DMA_ROUTE 52 +#define ETHER_READ_REG 53 +#define ETHER_WRITE_REG 54 /** @} */ /** diff --git a/drivers/net/ethernet/nvidia/nvethernet/macsec.c b/drivers/net/ethernet/nvidia/nvethernet/macsec.c index 0465d309..a117b3ba 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/macsec.c +++ b/drivers/net/ethernet/nvidia/nvethernet/macsec.c @@ -142,28 +142,30 @@ static int macsec_enable_car(struct macsec_priv_data *macsec_pdata) } } } else { - /* For Pre-sil only, reset the MACsec controller directly. - * clk ungate first, followed by disabling reset - * Bit 8 in CLK_RST_CONTROLLER_RST_DEV_MGBE_0 register. - */ - addr = devm_ioremap(dev, 0x21460080, 0x4); - if (addr) { - val = readl(addr); - val |= BIT(2); - writel(val, addr); - devm_iounmap(dev, addr); - } + if (pdata->osi_core->use_virtualization != OSI_ENABLE) { + /* For Pre-sil only, reset the MACsec controller directly. + * clk ungate first, followed by disabling reset + * Bit 8 in CLK_RST_CONTROLLER_RST_DEV_MGBE_0 register. + */ + addr = devm_ioremap(dev, 0x21460080, 0x4); + if (addr) { + val = readl(addr); + val |= BIT(2); + writel(val, addr); + devm_iounmap(dev, addr); + } - /* Followed by disabling reset - Bit 8 in - * CLK_RST_CONTROLLER_RST_DEV_MGBE_0 register. - */ - addr = devm_ioremap(dev, 0x21460018, 0x4); - if (addr) { - val = readl(addr); - val &= ~BIT(8); - writel(val, addr); - devm_iounmap(dev, addr); - } + /* Followed by disabling reset - Bit 8 in + * CLK_RST_CONTROLLER_RST_DEV_MGBE_0 register. + */ + addr = devm_ioremap(dev, 0x21460018, 0x4); + if (addr) { + val = readl(addr); + val &= ~BIT(8); + writel(val, addr); + devm_iounmap(dev, addr); + } + } } goto exit; diff --git a/drivers/net/ethernet/nvidia/nvethernet/osd.c b/drivers/net/ethernet/nvidia/nvethernet/osd.c index 2e270669..b28369e6 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/osd.c +++ b/drivers/net/ethernet/nvidia/nvethernet/osd.c @@ -479,7 +479,6 @@ void ether_assign_osd_ops(struct osi_core_priv_data *osi_core, int osd_ivc_send_cmd(void *priv, ivc_msg_common_t *ivc_buf, unsigned int len) { int ret = -1; - unsigned long flags = 0; static int cnt = 0; struct osi_core_priv_data *core = (struct osi_core_priv_data *)priv; struct ether_priv_data *pdata = (struct ether_priv_data *)core->osd; @@ -494,11 +493,12 @@ int osd_ivc_send_cmd(void *priv, ivc_msg_common_t *ivc_buf, unsigned int len) } ivc_buf->status = -1; - spin_lock_irqsave(&ictxt->ivck_lock, flags); if (in_atomic()) { preempt_enable(); is_atomic = 1; } + + mutex_lock(&ictxt->ivck_lock); ivc_buf->count = cnt++; /* Waiting for the channel to be ready */ while (tegra_hv_ivc_channel_notified(ivck) != 0){ @@ -534,9 +534,9 @@ int osd_ivc_send_cmd(void *priv, ivc_msg_common_t *ivc_buf, unsigned int len) } ret = ivc_buf->status; fail: + mutex_unlock(&ictxt->ivck_lock); if (is_atomic) { preempt_disable(); } - spin_unlock_irqrestore(&ictxt->ivck_lock, flags); return ret; } diff --git a/drivers/net/ethernet/nvidia/nvethernet/sysfs.c b/drivers/net/ethernet/nvidia/nvethernet/sysfs.c index b2df07e9..5696a9d4 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/sysfs.c +++ b/drivers/net/ethernet/nvidia/nvethernet/sysfs.c @@ -2709,6 +2709,8 @@ static int ether_register_dump_read(struct seq_file *seq, void *v) struct osi_core_priv_data *osi_core = pdata->osi_core; int max_address = 0x0; int start_addr = 0x0; + struct osi_ioctl ioctl_data; + int ret = 0; max_address = EOQS_MAX_REGISTER_ADDRESS; @@ -2719,10 +2721,13 @@ static int ether_register_dump_read(struct seq_file *seq, void *v) } while (1) { + ioctl_data.cmd = OSI_CMD_READ_REG; + ioctl_data.arg1_u32 = start_addr; + ret = osi_handle_ioctl(osi_core, &ioctl_data); seq_printf(seq, - "\t Register offset 0x%x value 0x%x\n", + "\t Register offset %x value 0x%x\n", start_addr, - ioread32((void *)osi_core->base + start_addr)); + ret); start_addr += 4; if (start_addr > max_address)