From d38959ba907d8758756aedcfea59bd12026748e3 Mon Sep 17 00:00:00 2001 From: Rakesh Goyal Date: Mon, 18 Apr 2022 18:12:35 +0530 Subject: [PATCH] nvethernet: add spinlock around skb link list Issue: In race condition node delete and addition happen at same time, which leads to corruption of list Fix: add spinlock to protect critical section Bug 3580369 Change-Id: Ief5495f049ed62d4e3832855a92eb066975eb672 Signed-off-by: Rakesh Goyal Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2699119 Reviewed-by: svcacv Reviewed-by: Bharat Nihalani Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/net/ethernet/nvidia/nvethernet/ether_linux.c | 7 +++++++ drivers/net/ethernet/nvidia/nvethernet/ether_linux.h | 2 ++ drivers/net/ethernet/nvidia/nvethernet/osd.c | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 56515c04..9a6d854b 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -44,6 +44,7 @@ static void ether_get_tx_ts(struct work_struct *work) unsigned long long nsec = 0x0; struct ether_tx_ts_skb_list *pnode; int ret = -1; + unsigned long flags; if (list_empty(&pdata->tx_ts_skb_head)) { return; @@ -87,8 +88,10 @@ update_skb: dev_consume_skb_any(pnode->skb); } + raw_spin_lock_irqsave(&pdata->txts_lock, flags); list_del(head_node); pnode->in_use = OSI_DISABLE; + raw_spin_unlock_irqrestore(&pdata->txts_lock, flags); } else { dev_dbg(pdata->dev, "Unable to retrieve TS from OSI\n"); @@ -2828,10 +2831,12 @@ static inline void ether_flush_tx_ts_skb_list(struct ether_priv_data *pdata) { struct ether_tx_ts_skb_list *pnode; struct list_head *head_node, *temp_head_node; + unsigned long flags; /* stop workqueue */ cancel_delayed_work_sync(&pdata->tx_ts_work); + raw_spin_lock_irqsave(&pdata->txts_lock, flags); /* Delete nodes from list and rest static memory for reuse */ if (!list_empty(&pdata->tx_ts_skb_head)) { list_for_each_safe(head_node, temp_head_node, @@ -2844,6 +2849,7 @@ static inline void ether_flush_tx_ts_skb_list(struct ether_priv_data *pdata) pnode->in_use = OSI_DISABLE; } } + raw_spin_unlock_irqrestore(&pdata->txts_lock, flags); } /** @@ -6560,6 +6566,7 @@ static int ether_probe(struct platform_device *pdev) raw_spin_lock_init(&pdata->rlock); + raw_spin_lock_init(&pdata->txts_lock); init_filter_values(pdata); if (osi_core->mac == OSI_MAC_HW_MGBE) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 694f41f7..efc79be8 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -617,6 +617,8 @@ struct ether_priv_data { /** HSI lock */ struct mutex hsi_lock; #endif + /** Protect critical section of TX TS SKB list */ + raw_spinlock_t txts_lock; }; /** diff --git a/drivers/net/ethernet/nvidia/nvethernet/osd.c b/drivers/net/ethernet/nvidia/nvethernet/osd.c index f5ada383..f5d6b86e 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/osd.c +++ b/drivers/net/ethernet/nvidia/nvethernet/osd.c @@ -704,6 +704,7 @@ static void osd_transmit_complete(void *priv, const struct osi_tx_swcx *swcx, unsigned int chan, qinx; unsigned int idx; unsigned int len = swcx->len; + unsigned long flags; ndev->stats.tx_bytes += len; @@ -746,11 +747,13 @@ static void osd_transmit_complete(void *priv, const struct osi_tx_swcx *swcx, OSI_TXDONE_CX_TS_DELAYED) { struct ether_tx_ts_skb_list *pnode = NULL; + raw_spin_lock_irqsave(&pdata->txts_lock, flags); idx = ether_get_free_tx_ts_node(pdata); if (idx == ETHER_MAX_PENDING_SKB_CNT) { dev_dbg(pdata->dev, "No free node to store pending SKB\n"); dev_consume_skb_any(skb); + raw_spin_unlock_irqrestore(&pdata->txts_lock, flags); return; } @@ -763,6 +766,7 @@ static void osd_transmit_complete(void *priv, const struct osi_tx_swcx *swcx, skb, txdone_pkt_cx->pktid); list_add_tail(&pnode->list_head, &pdata->tx_ts_skb_head); + raw_spin_unlock_irqrestore(&pdata->txts_lock, flags); schedule_delayed_work(&pdata->tx_ts_work, msecs_to_jiffies(ETHER_TS_MS_TIMER)); } else {