This allows monitoring on one or more radios while minimizing performance impact on the others. Signed-off-by: Felix Fietkau <nbd@xxxxxxxx> --- 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(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9618c82262e3..7bee2d912efe 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -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; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 304cce0b771d..3be9f8149117 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -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 * diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 56fed4f69427..4db867ae97f0 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -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); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6abf85a58133..2ce771744ea8 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -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); -- git-series 0.9.1