diff mbox series

[v2,1/5] wcn36xx: Fix dxe lock layering violation

Message ID 20211018231722.873525-2-bryan.odonoghue@linaro.org (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show
Series wcn36xx: Fix DMA buffer allocation and free logic | expand

Commit Message

Bryan O'Donoghue Oct. 18, 2021, 11:17 p.m. UTC
We are looking at some interesting crashes with wcn36xx in the wild, with
some of the data appearing to indicate multiple instances of "WARNING
Spurious TX complete indication".

Looking at the code here we see that txrx.c is taking the dxe.c lock to set
and unset the current TX skbuff pointer.

There is no obvious logical bug however, it is a layering violation to
share locks like this.

Lets tidy up the code a bit by making access functions to set and unset the
TX sbuff. This makes it easier to reason about this code without having to
switch between multiple files.

Fixes: 8e84c2582169 ("wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware")
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/net/wireless/ath/wcn36xx/dxe.c  | 26 +++++++++++++++++++++++++
 drivers/net/wireless/ath/wcn36xx/dxe.h  |  2 ++
 drivers/net/wireless/ath/wcn36xx/txrx.c | 15 ++------------
 3 files changed, 30 insertions(+), 13 deletions(-)

Comments

Kalle Valo Oct. 25, 2021, 1:31 p.m. UTC | #1
Bryan O'Donoghue <bryan.odonoghue@linaro.org> wrote:

> We are looking at some interesting crashes with wcn36xx in the wild, with
> some of the data appearing to indicate multiple instances of "WARNING
> Spurious TX complete indication".
> 
> Looking at the code here we see that txrx.c is taking the dxe.c lock to set
> and unset the current TX skbuff pointer.
> 
> There is no obvious logical bug however, it is a layering violation to
> share locks like this.
> 
> Lets tidy up the code a bit by making access functions to set and unset the
> TX sbuff. This makes it easier to reason about this code without having to
> switch between multiple files.
> 
> Fixes: 8e84c2582169 ("wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware")
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>

Failed to apply, please rebase on top of ath.git master branch. But
please wait few days as there are quite a few wcn36xx patches pending.

error: patch failed: drivers/net/wireless/ath/wcn36xx/dxe.c:878
error: drivers/net/wireless/ath/wcn36xx/dxe.c: patch does not apply
error: patch failed: drivers/net/wireless/ath/wcn36xx/dxe.h:468
error: drivers/net/wireless/ath/wcn36xx/dxe.h: patch does not apply
error: patch failed: drivers/net/wireless/ath/wcn36xx/txrx.c:584
error: drivers/net/wireless/ath/wcn36xx/txrx.c: patch does not apply
stg import: Diff does not apply cleanly

5 patches set to Changes Requested.

12568271 [v2,1/5] wcn36xx: Fix dxe lock layering violation
12568273 [v2,2/5] wcn36xx: Fix DMA channel enable/disable cycle
12568275 [v2,3/5] wcn36xx: Release DMA channel descriptor allocations
12568277 [v2,4/5] wcn36xx: Functionally decompose DXE reset
12568279 [v2,5/5] wcn36xx: Put DXE block into reset before freeing memory
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
index c4e9e939d7d6d..6c43df4bc92c3 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.c
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
@@ -878,6 +878,32 @@  int wcn36xx_dxe_tx_flush(struct wcn36xx *wcn)
 	return -EBUSY;
 }
 
+int wcn36xx_dxe_set_tx_ack_skb(struct wcn36xx *wcn, struct sk_buff *skb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wcn->dxe_lock, flags);
+	if (wcn->tx_ack_skb) {
+		spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+		wcn36xx_warn("tx_ack_skb already set\n");
+		return -EINVAL;
+	}
+
+	wcn->tx_ack_skb = skb;
+	spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+
+	return 0;
+}
+
+void wcn36xx_dxe_unset_tx_ack_skb(struct wcn36xx *wcn)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wcn->dxe_lock, flags);
+	wcn->tx_ack_skb = NULL;
+	spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+}
+
 int wcn36xx_dxe_init(struct wcn36xx *wcn)
 {
 	int reg_data = 0, ret;
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h
index 26a31edf52e99..9a7655d6af982 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.h
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.h
@@ -468,4 +468,6 @@  int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
 			 bool is_low);
 int wcn36xx_dxe_tx_flush(struct wcn36xx *wcn);
 void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
+int wcn36xx_dxe_set_tx_ack_skb(struct wcn36xx *wcn, struct sk_buff *skb);
+void wcn36xx_dxe_unset_tx_ack_skb(struct wcn36xx *wcn);
 #endif	/* _DXE_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c
index d727b0dd98de5..1218bd85de3ba 100644
--- a/drivers/net/wireless/ath/wcn36xx/txrx.c
+++ b/drivers/net/wireless/ath/wcn36xx/txrx.c
@@ -584,7 +584,6 @@  int wcn36xx_start_tx(struct wcn36xx *wcn,
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct wcn36xx_vif *vif_priv = NULL;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-	unsigned long flags;
 	bool is_low = ieee80211_is_data(hdr->frame_control);
 	bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
 		is_multicast_ether_addr(hdr->addr1);
@@ -606,15 +605,8 @@  int wcn36xx_start_tx(struct wcn36xx *wcn,
 	if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
 		wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
 
-		spin_lock_irqsave(&wcn->dxe_lock, flags);
-		if (wcn->tx_ack_skb) {
-			spin_unlock_irqrestore(&wcn->dxe_lock, flags);
-			wcn36xx_warn("tx_ack_skb already set\n");
+		if (wcn36xx_dxe_set_tx_ack_skb(wcn, skb))
 			return -EINVAL;
-		}
-
-		wcn->tx_ack_skb = skb;
-		spin_unlock_irqrestore(&wcn->dxe_lock, flags);
 
 		/* Only one at a time is supported by fw. Stop the TX queues
 		 * until the ack status gets back.
@@ -644,10 +636,7 @@  int wcn36xx_start_tx(struct wcn36xx *wcn,
 		/* If the skb has not been transmitted,
 		 * don't keep a reference to it.
 		 */
-		spin_lock_irqsave(&wcn->dxe_lock, flags);
-		wcn->tx_ack_skb = NULL;
-		spin_unlock_irqrestore(&wcn->dxe_lock, flags);
-
+		wcn36xx_dxe_unset_tx_ack_skb(wcn);
 		ieee80211_wake_queues(wcn->hw);
 	}