@@ -1748,6 +1748,8 @@ enum ieee80211_smps_mode {
*
* @flags: configuration flags defined above
*
+ * @monitor_radios: bitmask of radios with monitor mode enabled.
+ *
* @listen_interval: listen interval in units of beacon interval
* @ps_dtim_period: The DTIM period of the AP we're connected to, for use
* in power saving. Power saving will not be enabled until a beacon
@@ -1777,6 +1779,7 @@ enum ieee80211_smps_mode {
*/
struct ieee80211_conf {
u32 flags;
+ u32 monitor_radios;
int power_level, dynamic_ps_timeout;
u16 listen_interval;
@@ -1330,6 +1330,10 @@ enum mac80211_scan_state {
DECLARE_STATIC_KEY_FALSE(aql_disable);
+struct ieee80211_radio_data {
+ u32 monitors;
+};
+
struct ieee80211_local {
/* embed the driver visible part.
* don't cast (use the static inlines below), but we keep
@@ -1613,6 +1617,8 @@ struct ieee80211_local {
u8 ext_capa[8];
bool wbrf_supported;
+
+ struct ieee80211_radio_data *radio_data;
};
static inline struct ieee80211_sub_if_data *
@@ -601,6 +601,18 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
}
+ for (i = 0; i < local->hw.wiphy->n_radio; i++) {
+ if (!(sdata->wdev.radio_mask & BIT(i)))
+ continue;
+
+ local->radio_data[i].monitors--;
+ if (local->radio_data[i].monitors)
+ continue;
+
+ local->hw.conf.monitor_radios &= ~BIT(i);
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
+ }
+
ieee80211_adjust_monitor_flags(sdata, -1);
break;
case NL80211_IFTYPE_NAN:
@@ -1214,7 +1226,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
struct net_device *dev = wdev->netdev;
struct ieee80211_local *local = sdata->local;
u64 changed = 0;
- int res;
+ int i, res;
u32 hw_reconf_flags = 0;
lockdep_assert_wiphy(local->hw.wiphy);
@@ -1330,6 +1342,18 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
}
+ for (i = 0; i < local->hw.wiphy->n_radio; i++) {
+ if (!(sdata->wdev.radio_mask & BIT(i)))
+ continue;
+
+ local->radio_data[i].monitors++;
+ if (local->radio_data[i].monitors > 1)
+ continue;
+
+ local->hw.conf.monitor_radios |= BIT(i);
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
+ }
+
ieee80211_adjust_monitor_flags(sdata, 1);
ieee80211_configure_filter(local);
ieee80211_recalc_offload(local);
@@ -1348,6 +1348,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (!local->int_scan_req)
return -ENOMEM;
+ if (hw->wiphy->n_radio) {
+ local->radio_data = kcalloc(hw->wiphy->n_radio,
+ sizeof(*local->radio_data),
+ GFP_KERNEL);
+ if (!local->radio_data) {
+ result = -ENOMEM;
+ goto fail_workqueue;
+ }
+ }
+
eth_broadcast_addr(local->int_scan_req->bssid);
for (band = 0; band < NUM_NL80211_BANDS; band++) {
@@ -1642,6 +1652,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->wiphy_ciphers_allocated = false;
}
kfree(local->int_scan_req);
+ kfree(local->radio_data);
return result;
}
EXPORT_SYMBOL(ieee80211_register_hw);
@@ -1694,6 +1705,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
destroy_workqueue(local->workqueue);
ieee80211_led_exit(local);
kfree(local->int_scan_req);
+ kfree(local->radio_data);
}
EXPORT_SYMBOL(ieee80211_unregister_hw);
This allows monitoring on one or more radios while minimizing performance impact on the others. Signed-off-by: Felix Fietkau <nbd@nbd.name> --- include/net/mac80211.h | 3 +++ net/mac80211/ieee80211_i.h | 6 ++++++ net/mac80211/iface.c | 26 +++++++++++++++++++++++++- net/mac80211/main.c | 12 ++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-)