Search Linux Wireless

[PATCH v5 4/4] mwifiex: fix race for card->adapter

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Xinming Hu <huxm@xxxxxxxxxxx>

card->adapter is set/reset in register/unregister calls. Firmware gets
downloaded asynchronously in a separate thread during init. We have a
unregister call when firmware initialization fails. It may have a race
with suspend thread where "card->adapter" is being read.

This patch adds spinlock to fix the problem.

Signed-off-by: Cathy Luo <cluo@xxxxxxxxxxx>
Signed-off-by: Xinming Hu <huxm@xxxxxxxxxxx>
Signed-off-by: Amitkumar Karwar <akarwar@xxxxxxxxxxx>
---
New patch introduced in v5 to address race between driver init and suspend
threads.
---
 drivers/net/wireless/marvell/mwifiex/pcie.c | 38 +++++++++++++++++++++++------
 drivers/net/wireless/marvell/mwifiex/pcie.h |  2 ++
 2 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
index 94f75be..9147e6a 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -102,23 +102,32 @@ static int mwifiex_pcie_suspend(struct device *dev)
 	struct mwifiex_adapter *adapter;
 	struct pcie_service_card *card;
 	struct pci_dev *pdev = to_pci_dev(dev);
+	unsigned long flags;
 
 	card = pci_get_drvdata(pdev);
-	if (!card || !card->adapter ||
-	    card->adapter->hw_status == MWIFIEX_HW_STATUS_RESET ||
-	    card->adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) {
-		pr_err("Card or adapter structure is not valid or hw_status shows failure\n");
+	if (!card) {
+		pr_err("Card is not valid\n");
 		return 0;
 	}
 
+	spin_lock_irqsave(&card->adapter_lock, flags);
 	adapter = card->adapter;
+	if (!adapter ||
+	    adapter->hw_status == MWIFIEX_HW_STATUS_RESET ||
+	    adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) {
+		spin_unlock_irqrestore(&card->adapter_lock, flags);
+		pr_err("Adapter structure is not valid or hw_status shows failure\n");
+		return 0;
+	}
 
 	if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING ||
 	    adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE ||
 	    adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) {
+		spin_unlock_irqrestore(&card->adapter_lock, flags);
 		pr_err("We are in the middle of initialzaion or closing\n");
 		return -EBUSY;
 	}
+	spin_unlock_irqrestore(&card->adapter_lock, flags);
 
 	/* Enable the Host Sleep */
 	if (!mwifiex_enable_hs(adapter)) {
@@ -150,16 +159,22 @@ static int mwifiex_pcie_resume(struct device *dev)
 	struct mwifiex_adapter *adapter;
 	struct pcie_service_card *card;
 	struct pci_dev *pdev = to_pci_dev(dev);
+	unsigned long flags;
 
 	card = pci_get_drvdata(pdev);
-
-	if (!card || !card->adapter ||
-	    card->adapter->hw_status != MWIFIEX_HW_STATUS_READY) {
-		pr_err("Card or adapter structure is not valid or hw_status is not ready\n");
+	if (!card) {
+		pr_err("Card is not valid\n");
 		return 0;
 	}
 
+	spin_lock_irqsave(&card->adapter_lock, flags);
 	adapter = card->adapter;
+	if (!adapter || adapter->hw_status != MWIFIEX_HW_STATUS_READY) {
+		spin_unlock_irqrestore(&card->adapter_lock, flags);
+		pr_err("adapter structure is not valid or hw_status is not ready\n");
+		return 0;
+	}
+	spin_unlock_irqrestore(&card->adapter_lock, flags);
 
 	if (!adapter->is_suspended) {
 		mwifiex_dbg(adapter, WARN,
@@ -195,6 +210,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
 		return -ENOMEM;
 
 	card->dev = pdev;
+	spin_lock_init(&card->adapter_lock);
 
 	if (ent->driver_data) {
 		struct mwifiex_pcie_device *data = (void *)ent->driver_data;
@@ -2973,9 +2989,12 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 {
 	struct pcie_service_card *card = adapter->card;
 	struct pci_dev *pdev = card->dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&card->adapter_lock, flags);
 	/* save adapter pointer in card */
 	card->adapter = adapter;
+	spin_unlock_irqrestore(&card->adapter_lock, flags);
 	adapter->dev = &pdev->dev;
 
 	if (mwifiex_pcie_request_irq(adapter))
@@ -3001,6 +3020,7 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
 	struct pcie_service_card *card = adapter->card;
 	struct pci_dev *pdev;
 	int i;
+	unsigned long flags;
 
 	if (card) {
 		pdev = card->dev;
@@ -3022,7 +3042,9 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
 			if (card->msi_enable)
 				pci_disable_msi(pdev);
 	       }
+		spin_lock_irqsave(&card->adapter_lock, flags);
 		card->adapter = NULL;
+		spin_unlock_irqrestore(&card->adapter_lock, flags);
 	}
 }
 
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h
index 46f99ca..f6e20ea 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.h
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.h
@@ -344,6 +344,8 @@ struct mwifiex_msix_context {
 struct pcie_service_card {
 	struct pci_dev *dev;
 	struct mwifiex_adapter *adapter;
+	/* spin lock for card->adapter */
+	spinlock_t adapter_lock;
 	struct mwifiex_pcie_device pcie;
 
 	u8 txbd_flush;
-- 
1.9.1




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux