wil6210: fix for memory corruption upon rmmod
authorDedy Lansky <qca_dlansky@qca.qualcomm.com>
Wed, 10 Sep 2014 13:34:38 +0000 (16:34 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 11 Sep 2014 19:27:37 +0000 (15:27 -0400)
Driver disabled PCI master before making sure HW is idle.
This caused memory corruption in case HW access system memory after
PCI master got disabled.
The fix is to change uninit sequence. Make sure FW/HW is idle before
disabling PCI

Signed-off-by: Dedy Lansky <qca_dlansky@qca.qualcomm.com>
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/pcie_bus.c

index 696be3e71a82ebda77c9477cfe04e97f7879c9e2..22e9b8aeb1cfde5df4fcecb03613d8fa7356e274 100644 (file)
 
 #include "wil6210.h"
 #include "txrx.h"
+#include "wmi.h"
+
+#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
+#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
 
 static bool no_fw_recovery;
 module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
@@ -631,6 +635,9 @@ int wil_up(struct wil6210_priv *wil)
 
 static int __wil_down(struct wil6210_priv *wil)
 {
+       int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS /
+                       WAIT_FOR_DISCONNECT_INTERVAL_MS;
+
        WARN_ON(!mutex_is_locked(&wil->mutex));
 
        if (wil->platform_ops.bus_request)
@@ -648,7 +655,24 @@ static int __wil_down(struct wil6210_priv *wil)
                wil->scan_request = NULL;
        }
 
-       wil6210_disconnect(wil, NULL);
+       if (test_bit(wil_status_fwconnected, &wil->status) ||
+           test_bit(wil_status_fwconnecting, &wil->status))
+               wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
+
+       /* make sure wil is idle (not connected) */
+       mutex_unlock(&wil->mutex);
+       while (iter--) {
+               int idle = !test_bit(wil_status_fwconnected, &wil->status) &&
+                          !test_bit(wil_status_fwconnecting, &wil->status);
+               if (idle)
+                       break;
+               msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS);
+       }
+       mutex_lock(&wil->mutex);
+
+       if (!iter)
+               wil_err(wil, "timeout waiting for idle FW/HW\n");
+
        wil_rx_fini(wil);
 
        return 0;
index d2a1ca289d900494c6348fdcae922310d6cc24a0..264c0f06ea0f633704b46f90ddea212e29811fc2 100644 (file)
@@ -233,8 +233,8 @@ static void wil_pcie_remove(struct pci_dev *pdev)
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        wil6210_debugfs_remove(wil);
-       wil_if_pcie_disable(wil);
        wil_if_remove(wil);
+       wil_if_pcie_disable(wil);
        if (wil->platform_ops.uninit)
                wil->platform_ops.uninit(wil->platform_handle);
        wil_if_free(wil);