wil6210: fix race condition between BACK event and Rx data
[deliverable/linux.git] / drivers / net / wireless / ath / wil6210 / main.c
index 7dfbcfb513378a4040568557e3da0846ba78d7d1..d2f2c1e98e9e07a14431b524257a392de195eae4 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);
@@ -71,6 +75,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
        struct wil_sta_info *sta = &wil->sta[cid];
+
        wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
                     sta->status);
 
@@ -90,9 +95,16 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
        }
 
        for (i = 0; i < WIL_STA_TID_NUM; i++) {
-               struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+               struct wil_tid_ampdu_rx *r;
+               unsigned long flags;
+
+               spin_lock_irqsave(&sta->tid_rx_lock, flags);
+
+               r = sta->tid_rx[i];
                sta->tid_rx[i] = NULL;
                wil_tid_ampdu_rx_free(wil, r);
+
+               spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
        }
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
                if (wil->vring2cid_tid[i][0] == cid)
@@ -227,6 +239,7 @@ static void wil_fw_error_worker(struct work_struct *work)
 static int wil_find_free_vring(struct wil6210_priv *wil)
 {
        int i;
+
        for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
                if (!wil->vring_tx[i].va)
                        return i;
@@ -261,9 +274,13 @@ static void wil_connect_worker(struct work_struct *work)
 
 int wil_priv_init(struct wil6210_priv *wil)
 {
+       uint i;
+
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        memset(wil->sta, 0, sizeof(wil->sta));
+       for (i = 0; i < WIL6210_MAX_CID; i++)
+               spin_lock_init(&wil->sta[i].tid_rx_lock);
 
        mutex_init(&wil->mutex);
        mutex_init(&wil->wmi_mutex);
@@ -299,12 +316,16 @@ int wil_priv_init(struct wil6210_priv *wil)
 
 void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
 {
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        del_timer_sync(&wil->connect_timer);
        _wil6210_disconnect(wil, bssid);
 }
 
 void wil_priv_deinit(struct wil6210_priv *wil)
 {
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        del_timer_sync(&wil->scan_timer);
        cancel_work_sync(&wil->disconnect_worker);
        cancel_work_sync(&wil->fw_error_worker);
@@ -391,7 +412,6 @@ static int wil_target_reset(struct wil6210_priv *wil)
                        W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
                        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
                }
-
        }
 
        /* TODO: check order here!!! Erez code is different */
@@ -436,6 +456,7 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
 {
        ulong to = msecs_to_jiffies(1000);
        ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
+
        if (0 == left) {
                wil_err(wil, "Firmware not ready\n");
                return -ETIME;
@@ -455,15 +476,16 @@ int wil_reset(struct wil6210_priv *wil)
 {
        int rc;
 
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        WARN_ON(!mutex_is_locked(&wil->mutex));
 
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL);
 
        wil->status = 0; /* prevent NAPI from being scheduled */
-       if (test_bit(wil_status_napi_en, &wil->status)) {
+       if (test_bit(wil_status_napi_en, &wil->status))
                napi_synchronize(&wil->napi_rx);
-       }
 
        if (wil->scan_request) {
                wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
@@ -602,6 +624,10 @@ static int __wil_up(struct wil6210_priv *wil)
        napi_enable(&wil->napi_tx);
        set_bit(wil_status_napi_en, &wil->status);
 
+       if (wil->platform_ops.bus_request)
+               wil->platform_ops.bus_request(wil->platform_handle,
+                                             WIL_MAX_BUS_REQUEST_KBPS);
+
        return 0;
 }
 
@@ -609,6 +635,8 @@ int wil_up(struct wil6210_priv *wil)
 {
        int rc;
 
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        mutex_lock(&wil->mutex);
        rc = __wil_up(wil);
        mutex_unlock(&wil->mutex);
@@ -618,8 +646,14 @@ 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)
+               wil->platform_ops.bus_request(wil->platform_handle, 0);
+
        clear_bit(wil_status_napi_en, &wil->status);
        napi_disable(&wil->napi_rx);
        napi_disable(&wil->napi_tx);
@@ -632,7 +666,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;
@@ -642,6 +693,8 @@ int wil_down(struct wil6210_priv *wil)
 {
        int rc;
 
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        mutex_lock(&wil->mutex);
        rc = __wil_down(wil);
        mutex_unlock(&wil->mutex);
This page took 0.027233 seconds and 5 git commands to generate.