Skip to contentSkip to conversation

iwlwifi: rewrite iwl-scan.c to avoid race conditions

RFC In review Stanislaw Gruszka 2 participants 4 messages Aug 20, 2010 – Aug 26, 2010 Networking drivers (wireless)

lore
Share & tools
lore
#0 lore raw

RFC 1/1 iwlwifi: rewrite iwl-scan.c to avoid race conditions

In this patch I'm trying to avoid hardware scanning race conditions that may lead to not call ieee80211_scan_completed() (what in consequences gives "WARNING: at net/wireless/core.c:614 wdev_cleanup_work+0xb7/0xf0"), or call iee80211_scan_completed() more then once (what gives " WARNING: at net/mac80211/scan.c:312 ieee80211_scan_completed+0x5f/0x1f1").

First problem (warning in wdev_cleanup_work) make any further scan request from cfg80211 are ignored by mac80211 with EBUSY error, hence Networkmanager can not perform successful scan and not allow to establish new connection. So after suspend/resume (but maybe not only then) user is not able to connect to wireless network again.

We can not relay on that the commands (start and abort scan) are successful. Even if they are successfully send to the hardware, we can not get back notification from firmware (i.e. firmware hung or it was reseted), or we can get notification when we actually perform abort scan in driver code or after that.

To assure we call ieee80211_scan_completed() only once when scan was started we use SCAN_SCANNING bit. Code path, which first clear STATUS_SCANNING bit will call ieee80211_scan_completed().
We do this in many cases, in scan complete notification, scan abort and timeout, firmware reset, etc. each time we check SCANNING bit.

Note: there are still some race conditions. I commented them in code, I'm still working on that problems.

---
 drivers/net/wireless/iwlwifi/iwl-3945.h     |    2 +-
 drivers/net/wireless/iwlwifi/iwl-agn-lib.c  |   70 +----
 drivers/net/wireless/iwlwifi/iwl-agn.c      |   14 +
 drivers/net/wireless/iwlwifi/iwl-agn.h      |    2 +-
 drivers/net/wireless/iwlwifi/iwl-core.c     |   16 +-
 drivers/net/wireless/iwlwifi/iwl-core.h     |    4 +-
 drivers/net/wireless/iwlwifi/iwl-scan.c     |  453 +++++++++++++++-----------
 drivers/net/wireless/iwlwifi/iwl-sta.c      |    6 +-
 drivers/net/wireless/iwlwifi/iwl3945-base.c |   87 ++----
 9 files changed, 322 insertions(+), 332 deletions(-)
drivers/net/wireless/iwlwifi/iwl-3945.h +1 -1
index bb2aeeb..98509c5 100644
@@ -295,7 +295,7 @@ extern const struct iwl_channel_info *iwl3945_get_channel_info(
 extern int iwl3945_rs_next_rate(struct iwl_priv *priv, int rate);
 
 /* scanning */
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
 
 /* Requires full declaration of iwl_priv before including */
 #include "iwl-io.h"
drivers/net/wireless/iwlwifi/iwl-agn-lib.c +9 -61

108 lines not on this page · raw message

drivers/net/wireless/iwlwifi/iwl-agn.c +14 -0
index 26bc048..7602765 100644
@@ -3075,13 +3075,27 @@ static void iwl_bg_restart(struct work_struct *data)
 		return;
 
 	if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
+		bool scan_pending = false;
+
 		mutex_lock(&priv->mutex);
 		priv->vif = NULL;
 		priv->is_open = 0;
+		if (test_bit(STATUS_SCANNING, &priv->status) &&
+		    !priv->is_internal_short_scan) {
+			scan_pending = true;
+			clear_bit(STATUS_SCAN_HW, &priv->status);
+			clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+		}
 		mutex_unlock(&priv->mutex);
+
+		if (scan_pending)
+			ieee80211_scan_completed(priv->hw, true);
 		iwl_down(priv);
 		ieee80211_restart_hw(priv->hw);
 	} else {
+		mutex_lock(&priv->mutex);
+		iwl_scan_cancel_timeout(priv, 200);
+		mutex_unlock(&priv->mutex);
 		iwl_down(priv);
 
 		if (test_bit(STATUS_EXIT_PENDING, &priv->status))
drivers/net/wireless/iwlwifi/iwl-agn.h +1 -1
index cc6464d..4d51c85 100644
@@ -216,7 +216,7 @@ void iwl_reply_statistics(struct iwl_priv *priv,
 			  struct iwl_rx_mem_buffer *rxb);
 
 /* scan */
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int  iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
 
 /* station mgmt */
 int iwlagn_manage_ibss_station(struct iwl_priv *priv,
drivers/net/wireless/iwlwifi/iwl-core.c +8 -8
index 3d9443b..11a7ebd 100644
@@ -1704,8 +1704,8 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
 		 * background then we need to cancel it else the RXON
 		 * below/in post_associate will fail.
 		 */
-		if (iwl_scan_cancel_timeout(priv, 100)) {
-			IWL_WARN(priv, "Aborted scan still in progress after 100ms\n");
+		if (iwl_scan_cancel_timeout(priv, 200)) {
+			IWL_WARN(priv, "Aborted scan still in progress after 200ms\n");
 			IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n");
 			mutex_unlock(&priv->mutex);
 			return;
@@ -1894,20 +1894,20 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
 
 	mutex_lock(&priv->mutex);
 
+	iwl_scan_cancel_timeout(priv, 100);
+	if (test_and_clear_bit(STATUS_SCANNING, &priv->status))
+		scan_completed = true;
+
 	if (iwl_is_ready_rf(priv)) {
-		iwl_scan_cancel_timeout(priv, 100);
 		priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
 		iwlcore_commit_rxon(priv);
 	}
+
 	if (priv->vif == vif) {
 		priv->vif = NULL;
-		if (priv->scan_vif == vif) {
-			scan_completed = true;
-			priv->scan_vif = NULL;
-			priv->scan_request = NULL;
-		}
 		memset(priv->bssid, 0, ETH_ALEN);
 	}
+
 	mutex_unlock(&priv->mutex);
 
 	if (scan_completed)
drivers/net/wireless/iwlwifi/iwl-core.h +2 -2
index 7b1e832..cc7375b 100644
@@ -109,7 +109,7 @@ struct iwl_hcmd_utils_ops {
 				  __le16 fc, __le32 *tx_flags);
 	int  (*calc_rssi)(struct iwl_priv *priv,
 			  struct iwl_rx_phy_res *rx_resp);
-	void (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
+	int (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
 };
 
 struct iwl_apm_ops {
@@ -538,7 +538,7 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
  * Scanning
  ******************************************************************************/
 void iwl_init_scan_params(struct iwl_priv *priv);
-int iwl_scan_cancel(struct iwl_priv *priv);
+void iwl_scan_cancel(struct iwl_priv *priv);
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
 int iwl_mac_hw_scan(struct ieee80211_hw *hw,
 		    struct ieee80211_vif *vif,
drivers/net/wireless/iwlwifi/iwl-scan.c +261 -192

605 lines not on this page · raw message

drivers/net/wireless/iwlwifi/iwl-sta.c +3 -3
index d5e8db3..a2d0ffe 100644
@@ -989,11 +989,11 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
 	unsigned long flags;
 	int i;
 
-	if (iwl_scan_cancel(priv)) {
-		/* cancel scan failed, just live w/ bad key and rely
-		   briefly on SW decryption */
+	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+		/* just live w/ bad key and rely briefly on SW decryption */
 		return;
 	}
+	/* XXX: race condition: nothing prevent to start HW scanning now */
 
 	sta_id = iwl_sta_id_or_broadcast(priv, sta);
 	if (sta_id == IWL_INVALID_STATION)
drivers/net/wireless/iwlwifi/iwl3945-base.c +23 -64

140 lines not on this page · raw message

Signature · 1 line
1.7.1
maintainer
#1 lore raw

Hi Stanislaw,

Sorry for the long delay here.

Quoted 19 lines

In this patch I'm trying to avoid hardware scanning race conditions that may lead to not call ieee80211_scan_completed() (what in consequences gives "WARNING: at net/wireless/core.c:614 wdev_cleanup_work+0xb7/0xf0"), or call iee80211_scan_completed() more then once (what gives " WARNING: at net/mac80211/scan.c:312 ieee80211_scan_completed+0x5f/0x1f1").

First problem (warning in wdev_cleanup_work) make any further scan request from cfg80211 are ignored by mac80211 with EBUSY error, hence Networkmanager can not perform successful scan and not allow to establish new connection. So after suspend/resume (but maybe not only then) user is not able to connect to wireless network again.

We can not relay on that the commands (start and abort scan) are successful. Even if they are successfully send to the hardware, we can not get back notification from firmware (i.e. firmware hung or it was reseted), or we can get notification when we actually perform abort scan in driver code or after that.

Quoted 8 lines

To assure we call ieee80211_scan_completed() only once when scan was started we use SCAN_SCANNING bit. Code path, which first clear STATUS_SCANNING bit will call ieee80211_scan_completed().
We do this in many cases, in scan complete notification, scan abort and timeout, firmware reset, etc. each time we check SCANNING bit.

Note: there are still some race conditions. I commented them in code, I'm still working on that problems.

Good description, thanks.

Quoted 71 lines
drivers/net/wireless/iwlwifi/iwl-3945.h +1 -1
index bb2aeeb..98509c5 100644
@@ -295,7 +295,7 @@ extern const struct iwl_channel_info *iwl3945_get_channel_info(
 extern int iwl3945_rs_next_rate(struct iwl_priv *priv, int rate);
 
 /* scanning */
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
 
 /* Requires full declaration of iwl_priv before including */
 #include "iwl-io.h"
drivers/net/wireless/iwlwifi/iwl-agn-lib.c +1 -35
index eedd71f..29a5e8f 100644
@@ -1147,7 +1147,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
 	return added;
 }
 
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 {
 	struct iwl_host_cmd cmd = {
 		.id = REPLY_SCAN_CMD,
@@ -1155,7 +1155,6 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 		.flags = CMD_SIZE_HUGE,
 	};
 	struct iwl_scan_cmd *scan;
-	struct ieee80211_conf *conf = NULL;
 	u32 rate_flags = 0;
 	u16 cmd_len;
 	u16 rx_chain = 0;
@@ -1167,48 +1166,7 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 	int  chan_mod;
 	u8 active_chains;
 	u8 scan_tx_antennas = priv->hw_params.valid_tx_ant;
-
-	conf = ieee80211_get_hw_conf(priv->hw);
-
-	cancel_delayed_work(&priv->scan_check);
-
-	if (!iwl_is_ready(priv)) {
-		IWL_WARN(priv, "request scan called when driver not ready.\n");
-		goto done;
-	}
-
-	/* Make sure the scan wasn't canceled before this queued work
-	 * was given the chance to run... */
-	if (!test_bit(STATUS_SCANNING, &priv->status))
-		goto done;
-
-	/* This should never be called or scheduled if there is currently
-	 * a scan active in the hardware. */
-	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-		IWL_DEBUG_INFO(priv, "Multiple concurrent scan requests in parallel. "
-			       "Ignoring second request.\n");
-		goto done;
-	}
-
-	if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
-		IWL_DEBUG_SCAN(priv, "Aborting scan due to device shutdown\n");
-		goto done;
-	}
-
-	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-		IWL_DEBUG_HC(priv, "Scan request while abort pending.  Queuing.\n");
-		goto done;
-	}
-

drivers/net/wireless/iwlwifi/iwl-agn-lib.c void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) · line 1169

Quoted 10 lines
drivers/net/wireless/iwlwifi/iwl-agn-lib.c +1 -9
-	if (iwl_is_rfkill(priv)) {
-		IWL_DEBUG_HC(priv, "Aborting scan due to RF Kill activation\n");
-		goto done;
-	}
-
-	if (!test_bit(STATUS_READY, &priv->status)) {
-		IWL_DEBUG_HC(priv, "Scan request while uninitialized.  Queuing.\n");
-		goto done;
-	}
+	int ret = -ENOMEM;

Goodie :)

Quoted 14 lines
drivers/net/wireless/iwlwifi/iwl-agn-lib.c +0 -0
 int iwlagn_manage_ibss_station(struct iwl_priv *priv,
drivers/net/wireless/iwlwifi/iwl-agn.c +2 -0
index 26bc048..7602765 100644
@@ -3075,13 +3075,27 @@ static void iwl_bg_restart(struct work_struct *data)
 		return;
 
 	if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
+		bool scan_pending = false;
+
 		mutex_lock(&priv->mutex);
 		priv->vif = NULL;
 		priv->is_open = 0;

drivers/net/wireless/iwlwifi/iwl-agn.c static void iwl_bg_restart(struct work_struct *data) · line 3092

Quoted 10 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +9 -0
+		if (test_bit(STATUS_SCANNING, &priv->status) &&
+		    !priv->is_internal_short_scan) {
+			scan_pending = true;
+			clear_bit(STATUS_SCAN_HW, &priv->status);
+			clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+		}
 		mutex_unlock(&priv->mutex);
+
+		if (scan_pending)
+			ieee80211_scan_completed(priv->hw, true);

Since we've had locking problems in such situations a lot, I'm just going to allow mac80211 to call scan_completed() from any context. That'll get rid of all the problems with the mutx here, so that you can move this code into a helper function (based on your description, I suspect I'll see it again in the patch)

drivers/net/wireless/iwlwifi/iwl-agn.c static void iwl_bg_restart(struct work_struct *data) · line 3093

Quoted 2 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +1 -1
-static void iwl_bg_scan_check(struct work_struct *data)
+/* NOTE: priv->mutex is required before calling this function */

make that "lockdep_assert_held(&priv->mutex);" (at least in addition)

Quoted 10 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +8 -0
+static int iwl_do_scan_abort(struct iwl_priv *priv)
 
+	int ret = 0;
 
+	if (test_bit(STATUS_SCANNING, &priv->status)) {
+		if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status))
+			IWL_DEBUG_SCAN(priv, "Scan abort in progress.\n");
+		else {
+			ret = iwl_send_scan_abort(priv);
+			if (ret) {

drivers/net/wireless/iwlwifi/iwl-agn.c static void iwl_bg_restart(struct work_struct *data)

Quoted 10 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +10 -0
+				clear_bit(STATUS_SCANNING, &priv->status);
+				clear_bit(STATUS_SCAN_HW, &priv->status);
+				clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+
+				/* If we did internal scan and mac80211 does
+				 * not schedule scan by itself we do not
+				 * have to complete it */
+				if (priv->is_internal_short_scan &&
+				    priv->scan_request == NULL)
+					ret = 0;

Is that && really correct? It's just an extra check, right? I mean, scan_request is always NULL for internal short scans...

Also, if I make the change I just talked about earlier, could this function call ieee80211_scan_completed() itself?

drivers/net/wireless/iwlwifi/iwl-agn.c

Quoted 8 lines

/**
- * iwl_fill_probe_req - fill in all required fields and IE for probe request
+ * iwl_scan_cancel - Cancel any currently executing HW scan
*/

drivers/net/wireless/iwlwifi/iwl-agn.c +4 -0
+void iwl_scan_cancel(struct iwl_priv *priv)
+{
+	IWL_DEBUG_SCAN(priv, "Queuing scan abort.\n");
+	schedule_work(&priv->abort_scan);

This probably needs an explanation of why it uses schedule_work?

Quoted 1 line

+ * NOTE: priv->mutex must be held before calling this function

lockdep_assert etc.

Quoted 4 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +1 -2
+int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
 {
-	int len = 0;
-	u8 *pos = NULL;

drivers/net/wireless/iwlwifi/iwl-agn.c

Quoted 10 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +4 -5
+	int ret;
+	unsigned long now = jiffies;
 
-	/* Make sure there is enough space for the probe request,
-	 * two mandatory IEs and the data */
-	left -= 24;
-	if (left < 0)
-		return 0;
+	ret = iwl_do_scan_abort(priv);
+	mutex_unlock(&priv->mutex);

I'd prefer if we never dropped the mutex in a function that requires being called with it held. That can break locking really badly in the caller without anybody noticing.

drivers/net/wireless/iwlwifi/iwl-agn.c

Quoted 4 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +4 -0
+	if (ret) {
+		ieee80211_scan_completed(priv->hw, true);
+		goto out;
+	}

I guess it's just because of this though, so that should go away.

drivers/net/wireless/iwlwifi/iwl-agn.c

Quoted 6 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +5 -1
-	len += 24;
+	while (time_before(jiffies, now + msecs_to_jiffies(ms))) {
+		if (!test_bit(STATUS_SCANNING, &priv->status))
+			break;
+		msleep(20);
+	}

Or because of this?

drivers/net/wireless/iwlwifi/iwl-agn.c

Quoted 5 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +3 -2
-	/* ...next IE... */
-	pos = &frame->u.probe_req.variable[0];
+	/* XXX: race condtion: we can sucessufly cancel scan, but
+	 *	a new scan request could arrive, so we can still
+	 *	have scanning pending */ 

Can a new request really arrive? mac80211 needs to be processing the completed first? Or is this maybe for internal short scans?

drivers/net/wireless/iwlwifi/iwl-agn.c

Quoted 4 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +4 -0
+	/* When scanning is marked as pending, we will send abort command,
+	 * but do not care if it will be successful or not. Just cancel
+	 * bits and do cleanups here, we can not relay that we get
+	 * abort notification from hardware. */

"rely on getting abort notification from hardware"

drivers/net/wireless/iwlwifi/iwl-agn.c

Quoted 9 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +8 -0
+	/* We report scan completion to mac8011 only if external scan was
+	 * actually performed and nobody else report the completion */
+	if (test_and_clear_bit(STATUS_SCANNING, &priv->status) && !internal) {
+		completed = true;
+		IWL_DEBUG_INFO(priv, "SCAN completed.\n");
+	}
+
+out_unlock:
 	mutex_unlock(&priv->mutex);

Hmm, an only tangentially related question: do we really need to do all these atomic bit operations? We hold the mutex everywhere anyway, no?

Quoted 4 lines
drivers/net/wireless/iwlwifi/iwl-sta.c +0 -0
@@ -989,11 +989,11 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
 	unsigned long flags;

drivers/net/wireless/iwlwifi/iwl-sta.c void iwl_update_tkip_key(struct iwl_priv *priv, · line 996

Quoted 10 lines
drivers/net/wireless/iwlwifi/iwl-sta.c +3 -3
 	int i;
 
-	if (iwl_scan_cancel(priv)) {
-		/* cancel scan failed, just live w/ bad key and rely
-		   briefly on SW decryption */
+	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+		/* just live w/ bad key and rely briefly on SW decryption */
 		return;
 	}
+	/* XXX: race condition: nothing prevent to start HW scanning now */

TBH, I don't even understand why we need to cancel the scan here. We're just updating a key ... and we don't really do that while scanning anyway since it's triggered only by receiving frames successfully...

I'll send out the mac80211 patch in a bit.

johannes

#2 lore raw
Quoted 3 lines · Johannes Berg

Hi Stanislaw,

Sorry for the long delay here.

No problem, thanks for review.

Quoted 14 lines
Quoted 14 lines
drivers/net/wireless/iwlwifi/iwl-agn.c +9 -0
 	if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
+		bool scan_pending = false;
+
 		mutex_lock(&priv->mutex);
 		priv->vif = NULL;
 		priv->is_open = 0;
+		if (test_bit(STATUS_SCANNING, &priv->status) &&
+		    !priv->is_internal_short_scan) {
+			scan_pending = true;
+			clear_bit(STATUS_SCAN_HW, &priv->status);
+			clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+		}
 		mutex_unlock(&priv->mutex);
+
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+		if (scan_pending)
+			ieee80211_scan_completed(priv->hw, true);

Since we've had locking problems in such situations a lot, I'm just going to allow mac80211 to call scan_completed() from any context. That'll get rid of all the problems with the mutx here, so that you can move this code into a helper function (based on your description, I suspect I'll see it again in the patch)

This is mutex recursion problem, scan_completed() call ieee80211_hw_config() -> iwl_mac_config() -> mutex_lock(&priv->mutex). I should put short comment about that. Even on possibly removal of ieee80211_hw_config (for example when aborted==true) we still can have possible deadlock, because of mac80211 local->mtx and iwlwifi priv->mutex locking ordering.

Quoted 11 lines
Quoted 11 lines
drivers/net/wireless/iwlwifi/iwl-scan.c +11 -0
+	if (test_bit(STATUS_SCANNING, &priv->status)) {
+		if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status))
+			IWL_DEBUG_SCAN(priv, "Scan abort in progress.\n");
+		else {
+			ret = iwl_send_scan_abort(priv);
+			if (ret) {
+				clear_bit(STATUS_SCANNING, &priv->status);
+				clear_bit(STATUS_SCAN_HW, &priv->status);
+				clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+
+				/* If we did internal scan and mac80211 does
Quoted 8 lines

drivers/net/wireless/iwlwifi/iwl-scan.c void iwl_internal_short_hw_scan(struct iwl_priv *priv) · line 391

Quoted 5 lines
drivers/net/wireless/iwlwifi/iwl-scan.c +5 -0
+				 * not schedule scan by itself we do not
+				 * have to complete it */
+				if (priv->is_internal_short_scan &&
+				    priv->scan_request == NULL)
+					ret = 0;

Is that && really correct? It's just an extra check, right? I mean, scan_request is always NULL for internal short scans...

Nope, your commit f84b29ec0a1ab767679d3f2428877b65f94bc3ff changed that :-)

Quoted 2 lines

Also, if I make the change I just talked about earlier, could this function call ieee80211_scan_completed() itself?

Yep, if scan_completed() could be called with priv->mutex that will be great. I'm not sure if you can do this ...

Quoted 13 lines

drivers/net/wireless/iwlwifi/iwl-scan.c void iwl_internal_short_hw_scan(struct iwl_priv *priv) · line 423

Quoted 9 lines
drivers/net/wireless/iwlwifi/iwl-scan.c +3 -5
+	unsigned long now = jiffies;
 
-	/* Make sure there is enough space for the probe request,
-	 * two mandatory IEs and the data */
-	left -= 24;
-	if (left < 0)
-		return 0;
+	ret = iwl_do_scan_abort(priv);
+	mutex_unlock(&priv->mutex);

I'd prefer if we never dropped the mutex in a function that requires being called with it held. That can break locking really badly in the caller without anybody noticing.

Yes, this patch have a bug, I'm working on second version to eliminate it.

Quoted 7 lines

drivers/net/wireless/iwlwifi/iwl-scan.c void iwl_internal_short_hw_scan(struct iwl_priv *priv) · line 428

Quoted 5 lines
drivers/net/wireless/iwlwifi/iwl-scan.c +4 -0
+	if (ret) {
+		ieee80211_scan_completed(priv->hw, true);
+		goto out;
+	}

I guess it's just because of this though, so that should go away.

This is one reason.

Quoted 9 lines

drivers/net/wireless/iwlwifi/iwl-scan.c void iwl_internal_short_hw_scan(struct iwl_priv *priv) · line 434

Quoted 7 lines
drivers/net/wireless/iwlwifi/iwl-scan.c +5 -1
-	len += 24;
+	while (time_before(jiffies, now + msecs_to_jiffies(ms))) {
+		if (!test_bit(STATUS_SCANNING, &priv->status))
+			break;
+		msleep(20);
+	}

Or because of this?

Second reason, we can not have priv->mutex locked, otherwise iwl_bg_scan_completed will not be able to start. Maybe is possible to keep mutex locked here by changing logic - I'm thinking about that.

Quoted 9 lines

drivers/net/wireless/iwlwifi/iwl-scan.c void iwl_internal_short_hw_scan(struct iwl_priv *priv) · line 438

Quoted 6 lines
drivers/net/wireless/iwlwifi/iwl-scan.c +3 -2
-	/* ...next IE... */
-	pos = &frame->u.probe_req.variable[0];
+	/* XXX: race condtion: we can sucessufly cancel scan, but
+	 *	a new scan request could arrive, so we can still
+	 *	have scanning pending */ 

Can a new request really arrive? mac80211 needs to be processing the completed first? Or is this maybe for internal short scans?

W can scan_completed() from other function and new scan can arrive when we sleep here. Internal scan is problem here as well.

Quoted 2 lines

Hmm, an only tangentially related question: do we really need to do all these atomic bit operations? We hold the mutex everywhere anyway, no?

No. This my way to write things in one line instead of two, but perhaps two lines version should be used to not confuse readers.

Quoted 9 lines

drivers/net/wireless/iwlwifi/iwl-sta.c void iwl_update_tkip_key(struct iwl_priv *priv, · line 996

Quoted 5 lines
drivers/net/wireless/iwlwifi/iwl-sta.c +3 -0
+	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+		/* just live w/ bad key and rely briefly on SW decryption */
 		return;
 	}
+	/* XXX: race condition: nothing prevent to start HW scanning now */

TBH, I don't even understand why we need to cancel the scan here. We're just updating a key ... and we don't really do that while scanning anyway since it's triggered only by receiving frames successfully...

I don't understand this also.

Stanislaw

maintainer
#3 lore raw
Quoted 6 lines · Stanislaw Gruszka
Quoted 5 lines

Since we've had locking problems in such situations a lot, I'm just going to allow mac80211 to call scan_completed() from any context. That'll get rid of all the problems with the mutx here, so that you can move this code into a helper function (based on your description, I suspect I'll see it again in the patch)

Quoted 6 lines

This is mutex recursion problem, scan_completed() call ieee80211_hw_config() -> iwl_mac_config() -> mutex_lock(&priv->mutex). I should put short comment about that. Even on possibly removal of ieee80211_hw_config (for example when aborted==true) we still can have possible deadlock, because of mac80211 local->mtx and iwlwifi priv->mutex locking ordering.

Yeah, but I see you found my other patch now, which makes ieee80211_scan_completed() really callable everywhere :)

Quoted 8 lines
Quoted 6 lines

drivers/net/wireless/iwlwifi/iwl-scan.c void iwl_internal_short_hw_scan(struct iwl_priv *priv) · line 391

Quoted 3 lines
drivers/net/wireless/iwlwifi/iwl-scan.c +3 -0
+				if (priv->is_internal_short_scan &&
+				    priv->scan_request == NULL)
+					ret = 0;

Is that && really correct? It's just an extra check, right? I mean, scan_request is always NULL for internal short scans...

Nope, your commit f84b29ec0a1ab767679d3f2428877b65f94bc3ff changed that :-)

D'oh! You're right.

Quoted 5 lines
Quoted 2 lines

Hmm, an only tangentially related question: do we really need to do all these atomic bit operations? We hold the mutex everywhere anyway, no?

No. This my way to write things in one line instead of two, but perhaps two lines version should be used to not confuse readers.

No, I wasn't thinking of set_bit/clear_bit etc. but rather using bool values... Don't worry about that though, I'll go through at some point and check all the status bits.

Quoted 11 lines
Quoted 9 lines

drivers/net/wireless/iwlwifi/iwl-sta.c void iwl_update_tkip_key(struct iwl_priv *priv, · line 996

Quoted 5 lines
drivers/net/wireless/iwlwifi/iwl-sta.c +3 -0
+	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+		/* just live w/ bad key and rely briefly on SW decryption */
 		return;
 	}
+	/* XXX: race condition: nothing prevent to start HW scanning now */

TBH, I don't even understand why we need to cancel the scan here. We're just updating a key ... and we don't really do that while scanning anyway since it's triggered only by receiving frames successfully...

I don't understand this also.

It's probably some hw requirement that failed to make it into a comment ... Wey-yi?

johannes