[PATCH 3/3] wifi: rsi: serialize background scan state
From: Zhao Li
Date: Wed Oct 28 2026 - 10:24:05 EST
The firmware completion event runs in the RX thread without
common->mutex, while scan cancellation can clear hwscan under that mutex.
Both paths test bgscan_en before sending STOP and reporting completion.
An interleaving can therefore dereference a NULL or freed request in
rsi_send_bgscan_params(), or report the scan twice.
The RX path cannot take common->mutex: rsi_mac80211_start() holds it while
waiting for that path to finish firmware initialization after
hibernation.
Add a scan-specific mutex and use it to serialize start, cancellation,
and firmware completion. Clear bgscan_en and hwscan together even when
STOP cannot be queued so a later event cannot report the same scan again.
LLM assistance was used to find the bug and review.
Fixes: 7fdcb8e12660 ("rsi: add support for hardware scan offload")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Zhao Li <enderaoelyther@xxxxxxxxx>
---
Validation: W=1 module builds with MODPOST passed with CONFIG_RSI_COEX
enabled and disabled. Not reproduced; RS9113/RS9116 hardware was
unavailable.
Sparse C=2 passed. Targeted Smatch introduced no new diagnostics.
Host state is serialized, but no hardware or protocol evidence established
how a late event from a scan whose STOP command was not queued is attributed
after a later scan starts.
drivers/net/wireless/rsi/rsi_91x_mac80211.c | 31 +++++++++++++++------
drivers/net/wireless/rsi/rsi_91x_main.c | 1 +
drivers/net/wireless/rsi/rsi_91x_mgmt.c | 16 +++++++----
drivers/net/wireless/rsi/rsi_main.h | 2 ++
4 files changed, 36 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index a2e981e261d6..5617c7ef6196 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -249,15 +249,21 @@ static int rsi_mac80211_hw_scan_start(struct ieee80211_hw *hw,
scan_req->n_channels == 0)
return -EINVAL;
+ mutex_lock(&common->scan_mutex);
+
/* Scan already in progress. So return */
- if (common->bgscan_en)
- return -EBUSY;
+ if (common->bgscan_en) {
+ ret = -EBUSY;
+ goto out;
+ }
/* If STA is not connected, return with special value 1, in order
* to start sw_scan in mac80211
*/
- if (!vif->cfg.assoc)
- return 1;
+ if (!vif->cfg.assoc) {
+ ret = 1;
+ goto out;
+ }
mutex_lock(&common->mutex);
common->hwscan = scan_req;
@@ -270,6 +276,9 @@ static int rsi_mac80211_hw_scan_start(struct ieee80211_hw *hw,
}
mutex_unlock(&common->mutex);
+out:
+ mutex_unlock(&common->scan_mutex);
+
return ret;
}
@@ -283,16 +292,20 @@ static void rsi_mac80211_cancel_hw_scan(struct ieee80211_hw *hw,
};
rsi_dbg(INFO_ZONE, "***** Hardware scan stop *****\n");
- mutex_lock(&common->mutex);
+ mutex_lock(&common->scan_mutex);
if (common->bgscan_en) {
- if (!rsi_send_bgscan_params(common, RSI_STOP_BGSCAN))
- common->bgscan_en = false;
+ mutex_lock(&common->mutex);
+ if (rsi_send_bgscan_params(common, RSI_STOP_BGSCAN))
+ rsi_dbg(ERR_ZONE, "Failed to stop background scan\n");
+ mutex_unlock(&common->mutex);
+ common->bgscan_en = false;
+ common->hwscan = NULL;
ieee80211_scan_completed(adapter->hw, &info);
rsi_dbg(INFO_ZONE, "Back ground scan cancelled\n");
}
- common->hwscan = NULL;
- mutex_unlock(&common->mutex);
+
+ mutex_unlock(&common->scan_mutex);
}
/**
diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c
index 2ce514766620..16f57c964ca3 100644
--- a/drivers/net/wireless/rsi/rsi_91x_main.c
+++ b/drivers/net/wireless/rsi/rsi_91x_main.c
@@ -325,6 +325,7 @@ struct rsi_hw *rsi_91x_init(u16 oper_mode)
rsi_init_event(&common->tx_thread.event);
mutex_init(&common->mutex);
+ mutex_init(&common->scan_mutex);
mutex_init(&common->tx_lock);
mutex_init(&common->rx_lock);
mutex_init(&common->tx_bus_mutex);
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 165423313c5f..5f11de44f880 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -2107,17 +2107,23 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
rsi_dbg(INFO_ZONE, "Wakeup/Sleep confirmation.\n");
return rsi_handle_ps_confirm(adapter, msg);
- case BG_SCAN_PROBE_REQ:
+ case BG_SCAN_PROBE_REQ: {
+ struct cfg80211_scan_info info = {};
+
rsi_dbg(INFO_ZONE, "BG scan complete event\n");
+ mutex_lock(&common->scan_mutex);
if (common->bgscan_en) {
- struct cfg80211_scan_info info = {};
-
- if (!rsi_send_bgscan_params(common, RSI_STOP_BGSCAN))
- common->bgscan_en = 0;
+ if (rsi_send_bgscan_params(common, RSI_STOP_BGSCAN))
+ rsi_dbg(ERR_ZONE,
+ "Failed to stop background scan\n");
+ common->bgscan_en = false;
+ common->hwscan = NULL;
ieee80211_scan_completed(adapter->hw, &info);
}
+ mutex_unlock(&common->scan_mutex);
rsi_dbg(INFO_ZONE, "Background scan completed\n");
break;
+ }
default:
rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n",
diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h
index dcf8fb40698b..5fa8fd93a17e 100644
--- a/drivers/net/wireless/rsi/rsi_main.h
+++ b/drivers/net/wireless/rsi/rsi_main.h
@@ -250,6 +250,8 @@ struct rsi_common {
struct completion wlan_init_completion;
/* Mutex declaration */
struct mutex mutex;
+ /* Protect background scan state. */
+ struct mutex scan_mutex;
/* Mutex used for tx thread */
struct mutex tx_lock;
/* Mutex used for rx thread */
--
2.56.0