[PATCH rtw-next v5 04/10] wifi: rtw89: protect pure_monitor_mode_vif with RCU
From: Enver Ikromov
Date: Fri Nov 13 2026 - 18:54:57 EST
A following patch reads rtwdev->pure_monitor_mode_vif from
rtw89_ops_tx(), which runs without the wiphy lock, while interface
add/remove updates the pointer under the wiphy lock.
Annotate pure_monitor_mode_vif with __rcu, convert existing readers to
rcu_access_pointer() and rcu_dereference_protected(), and recompute
the pointer via rtw89_recalc_pure_monitor_vif() and
rtw89_set_pure_monitor_vif().
Whereas mac80211 only creates local->monitor_sdata while no other
interface is open, a monitor interface opened with MONITOR_FLAG_ACTIVE
is passed directly to drv_add_interface() even alongside an active
station or AP interface. Walking rtwdev->rtwvifs_list ensures
pure_monitor_mode_vif is published if and only if a single monitor
interface is the sole active driver vif, and unpublishes it with
synchronize_rcu() before its link, port, and MAC ID are released.
Signed-off-by: Enver Ikromov <owner@xxxxxxxxxxxxxx>
---
drivers/net/wireless/realtek/rtw89/core.c | 9 +++-
drivers/net/wireless/realtek/rtw89/core.h | 2 +-
drivers/net/wireless/realtek/rtw89/mac80211.c | 49 +++++++++++++++++--
3 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 527593e..3447f0e 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -530,7 +530,7 @@ void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
- bool mon = !!rtwdev->pure_monitor_mode_vif;
+ bool mon = !!rcu_access_pointer(rtwdev->pure_monitor_mode_vif);
bool prehdl_link = false;
if (chip->chip_gen != RTW89_CHIP_AX &&
@@ -560,8 +560,13 @@ void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev,
static void rtw89_chip_rfk_channel_for_pure_mon_vif(struct rtw89_dev *rtwdev,
enum rtw89_phy_idx phy_idx)
{
- struct rtw89_vif *rtwvif = rtwdev->pure_monitor_mode_vif;
+ struct wiphy *wiphy = rtwdev->hw->wiphy;
struct rtw89_vif_link *rtwvif_link;
+ struct rtw89_vif *rtwvif;
+
+ lockdep_assert_wiphy(wiphy);
+ rtwvif = rcu_dereference_protected(rtwdev->pure_monitor_mode_vif,
+ lockdep_is_held(&wiphy->mtx));
if (!rtwvif)
return;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 7be8810..78f6ef0 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -7832,7 +7832,7 @@ struct rtw89_dev {
int napi_budget_countdown;
struct rtw89_debugfs *debugfs;
- struct rtw89_vif *pure_monitor_mode_vif;
+ struct rtw89_vif __rcu *pure_monitor_mode_vif;
struct rtw89_led led;
/* HCI related data, keep last */
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 2a3adf0..b38e04f 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -183,6 +183,47 @@ static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev,
rtw89_mac_remove_vif(rtwdev, rtwvif_link);
}
+static bool rtw89_set_pure_monitor_vif(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif, bool sync)
+{
+ struct wiphy *wiphy = rtwdev->hw->wiphy;
+ struct rtw89_vif *old;
+
+ lockdep_assert_wiphy(wiphy);
+
+ old = rcu_dereference_protected(rtwdev->pure_monitor_mode_vif,
+ lockdep_is_held(&wiphy->mtx));
+ if (old == rtwvif)
+ return false;
+
+ rcu_assign_pointer(rtwdev->pure_monitor_mode_vif, rtwvif);
+
+ /* wait for lockless readers still using the previous vif */
+ if (old && sync)
+ synchronize_rcu();
+
+ return !!old;
+}
+
+static bool rtw89_recalc_pure_monitor_vif(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *exclude, bool sync)
+{
+ struct rtw89_vif *sole = NULL;
+ struct rtw89_vif *iter;
+
+ rtw89_for_each_rtwvif(rtwdev, iter) {
+ if (iter == exclude)
+ continue;
+ if (sole || rtwvif_to_vif(iter)->type != NL80211_IFTYPE_MONITOR) {
+ sole = NULL;
+ break;
+ }
+ sole = iter;
+ }
+
+ return rtw89_set_pure_monitor_vif(rtwdev, sole, sync);
+}
+
static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -241,8 +282,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
if (ret)
goto unset_link;
- rtwdev->pure_monitor_mode_vif = vif->type == NL80211_IFTYPE_MONITOR ?
- rtwvif : NULL;
+ rtw89_recalc_pure_monitor_vif(rtwdev, NULL, true);
rtw89_recalc_lps(rtwdev);
return 0;
@@ -271,6 +311,9 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n",
vif->addr, vif->type, vif->p2p);
+ /* unpublish before tearing down the link, port and MAC ID */
+ rtw89_recalc_pure_monitor_vif(rtwdev, rtwvif, true);
+
wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);
rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID];
@@ -290,8 +333,6 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
rtw89_core_release_bit_map(rtwdev->hw_port, port);
rtw89_release_mac_id(rtwdev, macid);
- rtwdev->pure_monitor_mode_vif = NULL;
-
rtw89_recalc_lps(rtwdev);
rtw89_enter_ips_by_hwflags(rtwdev);
}
--
2.56.0.windows.4