OSDN Git Service

staging: wfx: workaround bug with "iw scan"
authorJérôme Pouiller <jerome.pouiller@silabs.com>
Tue, 17 Dec 2019 16:15:37 +0000 (16:15 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 18 Dec 2019 14:56:31 +0000 (15:56 +0100)
mac80211 specification does not forbid hw_scan() to call
ieee80211_scan_completed(). However, from userspace point of view, not
all applications support this behavior. In particular, the code of iw
contains a big fat warning:

   /*
    * This code has a bug, which requires creating a separate
    * nl80211 socket to fix:
    * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
    * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
    * before (!) we listen to it, because we only start listening
    * after we send our scan request.
    [...]
    * Alas, the kernel doesn't do that (yet).
    */

So, we have to avoid to call ieee80211_scan_completed() from hw_scan()
(it's a kind of unwritten rule).

This patch relocates the hw_scan() process to a work_struct to fix the
problem.

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20191217161318.31402-52-Jerome.Pouiller@silabs.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/wfx/scan.c
drivers/staging/wfx/scan.h
drivers/staging/wfx/sta.c
drivers/staging/wfx/wfx.h

index b73e61e..540009b 100644 (file)
@@ -71,23 +71,19 @@ static int send_scan_req(struct wfx_vif *wvif,
        return i - start_idx;
 }
 
-int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-               struct ieee80211_scan_request *hw_req)
+/*
+ * It is not really necessary to run scan request asynchronously. However,
+ * there is a bug in "iw scan" when ieee80211_scan_completed() is called before
+ * wfx_hw_scan() return
+ */
+void wfx_hw_scan_work(struct work_struct *work)
 {
-       struct wfx_dev *wdev = hw->priv;
-       struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv;
+       struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan_work);
+       struct ieee80211_scan_request *hw_req = wvif->scan_req;
        int chan_cur, ret;
 
-       WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
-
-       if (vif->type == NL80211_IFTYPE_AP)
-               return -EOPNOTSUPP;
-
-       if (wvif->state == WFX_STATE_PRE_STA)
-               return -EBUSY;
-
        mutex_lock(&wvif->scan_lock);
-       mutex_lock(&wdev->conf_mutex);
+       mutex_lock(&wvif->wdev->conf_mutex);
        update_probe_tmpl(wvif, &hw_req->req);
        wfx_fwd_probe_req(wvif, true);
        chan_cur = 0;
@@ -96,18 +92,35 @@ int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                if (ret > 0)
                        chan_cur += ret;
        } while (ret > 0 && chan_cur < hw_req->req.n_channels);
-       __ieee80211_scan_completed_compat(hw, ret < 0);
-       mutex_unlock(&wdev->conf_mutex);
+       mutex_unlock(&wvif->wdev->conf_mutex);
        mutex_unlock(&wvif->scan_lock);
+       __ieee80211_scan_completed_compat(wvif->wdev->hw, ret < 0);
        if (wvif->delayed_unjoin) {
                wvif->delayed_unjoin = false;
-               wfx_tx_lock(wdev);
+               wfx_tx_lock(wvif->wdev);
                if (!schedule_work(&wvif->unjoin_work))
-                       wfx_tx_unlock(wdev);
+                       wfx_tx_unlock(wvif->wdev);
        } else if (wvif->delayed_link_loss) {
                wvif->delayed_link_loss = false;
                wfx_cqm_bssloss_sm(wvif, 1, 0, 0);
        }
+}
+
+int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+               struct ieee80211_scan_request *hw_req)
+{
+       struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
+
+       WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
+
+       if (vif->type == NL80211_IFTYPE_AP)
+               return -EOPNOTSUPP;
+
+       if (wvif->state == WFX_STATE_PRE_STA)
+               return -EBUSY;
+
+       wvif->scan_req = hw_req;
+       schedule_work(&wvif->scan_work);
        return 0;
 }
 
index 03bc6c7..b547f19 100644 (file)
@@ -15,6 +15,7 @@
 struct wfx_dev;
 struct wfx_vif;
 
+void wfx_hw_scan_work(struct work_struct *work);
 int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                struct ieee80211_scan_request *req);
 void wfx_scan_complete(struct wfx_vif *wvif,
index 16f5db8..4354bb8 100644 (file)
@@ -1427,6 +1427,7 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
        mutex_init(&wvif->scan_lock);
        init_completion(&wvif->scan_complete);
+       INIT_WORK(&wvif->scan_work, wfx_hw_scan_work);
 
        mutex_unlock(&wdev->conf_mutex);
 
index 3356d0c..b5f763c 100644 (file)
@@ -127,7 +127,9 @@ struct wfx_vif {
 
        /* avoid some operations in parallel with scan */
        struct mutex            scan_lock;
+       struct work_struct      scan_work;
        struct completion       scan_complete;
+       struct ieee80211_scan_request *scan_req;
 
        struct completion       set_pm_mode_complete;