diff mbox series

[v2,51/55] staging: wfx: workaround bug with "iw scan"

Message ID 20191217161318.31402-52-Jerome.Pouiller@silabs.com (mailing list archive)
State Not Applicable
Delegated to: Johannes Berg
Headers show
Series Improve wfx driver | expand

Commit Message

Jérôme Pouiller Dec. 17, 2019, 4:15 p.m. UTC
From: Jérôme Pouiller <jerome.pouiller@silabs.com>

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>
---
 drivers/staging/wfx/scan.c | 47 ++++++++++++++++++++++++--------------
 drivers/staging/wfx/scan.h |  1 +
 drivers/staging/wfx/sta.c  |  1 +
 drivers/staging/wfx/wfx.h  |  2 ++
 4 files changed, 34 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/drivers/staging/wfx/scan.c b/drivers/staging/wfx/scan.c
index b73e61e8da46..540009b72240 100644
--- a/drivers/staging/wfx/scan.c
+++ b/drivers/staging/wfx/scan.c
@@ -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;
 }
 
diff --git a/drivers/staging/wfx/scan.h b/drivers/staging/wfx/scan.h
index 03bc6c7e562d..b547f1927d72 100644
--- a/drivers/staging/wfx/scan.h
+++ b/drivers/staging/wfx/scan.h
@@ -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,
diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c
index 16f5db873275..4354bb8081c5 100644
--- a/drivers/staging/wfx/sta.c
+++ b/drivers/staging/wfx/sta.c
@@ -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);
 
diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h
index 3356d0cbf7af..b5f763c3fac7 100644
--- a/drivers/staging/wfx/wfx.h
+++ b/drivers/staging/wfx/wfx.h
@@ -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;