mac80211: allow scanning operating on DFS channels
Only for multi-radio configurations where the scan would not affect the radio running DFS detection Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
ed26185bf6
commit
91d33d1563
1 changed files with 372 additions and 0 deletions
|
@ -0,0 +1,372 @@
|
|||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -3473,6 +3473,27 @@ static int ieee80211_set_bitrate_mask(st
|
||||
return 0;
|
||||
}
|
||||
|
||||
+bool ieee80211_scanning_busy(struct ieee80211_local *local,
|
||||
+ struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ struct cfg80211_scan_request *scan_req;
|
||||
+ struct wiphy *wiphy = local->hw.wiphy;
|
||||
+ u32 mask;
|
||||
+
|
||||
+ if (list_empty(&local->roc_list) && !local->scanning)
|
||||
+ return false;
|
||||
+
|
||||
+ if (!wiphy->n_radio)
|
||||
+ return true;
|
||||
+
|
||||
+ mask = ieee80211_offchannel_radio_mask(local);
|
||||
+ scan_req = wiphy_dereference(wiphy, local->scan_req);
|
||||
+ if (scan_req)
|
||||
+ mask |= ieee80211_scan_req_radio_mask(local, scan_req);
|
||||
+
|
||||
+ return mask & ieee80211_chandef_radio_mask(local, chandef);
|
||||
+}
|
||||
+
|
||||
static int ieee80211_start_radar_detection(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
@@ -3486,7 +3507,7 @@ static int ieee80211_start_radar_detecti
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- if (!list_empty(&local->roc_list) || local->scanning)
|
||||
+ if (ieee80211_scanning_busy(local, chandef))
|
||||
return -EBUSY;
|
||||
|
||||
link_data = sdata_dereference(sdata->link[link_id], sdata);
|
||||
@@ -3978,7 +3999,7 @@ __ieee80211_channel_switch(struct wiphy
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- if (!list_empty(&local->roc_list) || local->scanning)
|
||||
+ if (ieee80211_scanning_busy(local, ¶ms->chandef))
|
||||
return -EBUSY;
|
||||
|
||||
if (sdata->wdev.links[link_id].cac_started)
|
||||
--- a/net/mac80211/chan.c
|
||||
+++ b/net/mac80211/chan.c
|
||||
@@ -629,14 +629,24 @@ ieee80211_find_chanctx(struct ieee80211_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
-bool ieee80211_is_radar_required(struct ieee80211_local *local)
|
||||
+bool ieee80211_is_radar_required(struct ieee80211_local *local, u32 radio_mask)
|
||||
{
|
||||
+ struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_link_data *link;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
for_each_sdata_link(local, link) {
|
||||
- if (link->radar_required)
|
||||
+ if (!link->radar_required)
|
||||
+ continue;
|
||||
+ if (!local->hw.wiphy->n_radio)
|
||||
+ return true;
|
||||
+
|
||||
+ conf = wiphy_dereference(local->hw.wiphy, link->conf->chanctx_conf);
|
||||
+ if (!conf)
|
||||
+ continue;
|
||||
+
|
||||
+ if (conf->radio_idx >= 0 && (radio_mask & BIT(conf->radio_idx)))
|
||||
return true;
|
||||
}
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -1947,6 +1947,12 @@ int ieee80211_mesh_finish_csa(struct iee
|
||||
u64 *changed);
|
||||
|
||||
/* scan/BSS handling */
|
||||
+u32 ieee80211_scan_req_radio_mask(struct ieee80211_local *local,
|
||||
+ struct cfg80211_scan_request *req);
|
||||
+bool ieee80211_scanning_busy(struct ieee80211_local *local,
|
||||
+ struct cfg80211_chan_def *chandef);
|
||||
+u32 ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata,
|
||||
+ u32 radio_mask);
|
||||
void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work);
|
||||
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *ssid, u8 ssid_len,
|
||||
@@ -1985,6 +1991,7 @@ void ieee80211_sched_scan_stopped_work(s
|
||||
/* off-channel/mgmt-tx */
|
||||
void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);
|
||||
void ieee80211_offchannel_return(struct ieee80211_local *local);
|
||||
+u32 ieee80211_offchannel_radio_mask(struct ieee80211_local *local);
|
||||
void ieee80211_roc_setup(struct ieee80211_local *local);
|
||||
void ieee80211_start_next_roc(struct ieee80211_local *local);
|
||||
void ieee80211_reconfig_roc(struct ieee80211_local *local);
|
||||
@@ -2629,6 +2636,8 @@ bool ieee80211_chandef_s1g_oper(const st
|
||||
struct cfg80211_chan_def *chandef);
|
||||
void ieee80211_chandef_downgrade(struct cfg80211_chan_def *chandef,
|
||||
struct ieee80211_conn_settings *conn);
|
||||
+u32 ieee80211_chandef_radio_mask(struct ieee80211_local *local,
|
||||
+ struct cfg80211_chan_def *chandef);
|
||||
static inline void
|
||||
ieee80211_chanreq_downgrade(struct ieee80211_chan_req *chanreq,
|
||||
struct ieee80211_conn_settings *conn)
|
||||
@@ -2685,7 +2694,7 @@ void ieee80211_recalc_chanctx_min_def(st
|
||||
struct ieee80211_chanctx *ctx,
|
||||
struct ieee80211_link_data *rsvd_for,
|
||||
bool check_reserved);
|
||||
-bool ieee80211_is_radar_required(struct ieee80211_local *local);
|
||||
+bool ieee80211_is_radar_required(struct ieee80211_local *local, u32 radio_mask);
|
||||
|
||||
void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work);
|
||||
void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
|
||||
--- a/net/mac80211/offchannel.c
|
||||
+++ b/net/mac80211/offchannel.c
|
||||
@@ -168,6 +168,35 @@ void ieee80211_offchannel_return(struct
|
||||
false);
|
||||
}
|
||||
|
||||
+u32 ieee80211_offchannel_radio_mask(struct ieee80211_local *local)
|
||||
+{
|
||||
+ const struct wiphy_radio *radio;
|
||||
+ struct ieee80211_roc_work *roc;
|
||||
+ u32 mask = 0;
|
||||
+ int r;
|
||||
+
|
||||
+ for (r = 0; r < local->hw.wiphy->n_radio; r++) {
|
||||
+ radio = &local->hw.wiphy->radio[r];
|
||||
+
|
||||
+ list_for_each_entry(roc, &local->roc_list, list) {
|
||||
+ struct cfg80211_chan_def chandef = {};
|
||||
+
|
||||
+ if (!roc->started)
|
||||
+ continue;
|
||||
+
|
||||
+ cfg80211_chandef_create(&chandef, roc->chan,
|
||||
+ NL80211_CHAN_NO_HT);
|
||||
+ if (!cfg80211_radio_chandef_valid(radio, &chandef))
|
||||
+ continue;
|
||||
+
|
||||
+ mask |= BIT(r);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return mask;
|
||||
+}
|
||||
+
|
||||
static void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
|
||||
{
|
||||
/* was never transmitted */
|
||||
@@ -566,7 +595,9 @@ static int ieee80211_start_roc_work(stru
|
||||
enum ieee80211_roc_type type)
|
||||
{
|
||||
struct ieee80211_roc_work *roc, *tmp;
|
||||
+ struct cfg80211_chan_def chandef = {};
|
||||
bool queued = false, combine_started = true;
|
||||
+ u32 radio_mask;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
@@ -578,6 +609,12 @@ static int ieee80211_start_roc_work(stru
|
||||
if (!local->emulate_chanctx && !local->ops->remain_on_channel)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
+ cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
|
||||
+ radio_mask = ieee80211_chandef_radio_mask(local, &chandef);
|
||||
+ if (!ieee80211_can_leave_ch(sdata, radio_mask) &&
|
||||
+ !ieee80211_scanning_busy(local, &chandef))
|
||||
+ return -EBUSY;
|
||||
+
|
||||
roc = kzalloc(sizeof(*roc), GFP_KERNEL);
|
||||
if (!roc)
|
||||
return -ENOMEM;
|
||||
@@ -613,8 +650,7 @@ static int ieee80211_start_roc_work(stru
|
||||
}
|
||||
|
||||
/* if there's no need to queue, handle it immediately */
|
||||
- if (list_empty(&local->roc_list) &&
|
||||
- !local->scanning && !ieee80211_is_radar_required(local)) {
|
||||
+ if (list_empty(&local->roc_list) && !local->scanning) {
|
||||
/* if not HW assist, just queue & schedule work */
|
||||
if (!local->ops->remain_on_channel) {
|
||||
list_add_tail(&roc->list, &local->roc_list);
|
||||
--- a/net/mac80211/scan.c
|
||||
+++ b/net/mac80211/scan.c
|
||||
@@ -571,36 +571,83 @@ static int ieee80211_start_sw_scan(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
|
||||
+u32 ieee80211_scan_req_radio_mask(struct ieee80211_local *local,
|
||||
+ struct cfg80211_scan_request *req)
|
||||
+{
|
||||
+ const struct wiphy_radio *radio;
|
||||
+ u32 mask = 0;
|
||||
+ int i, r;
|
||||
+
|
||||
+ for (r = 0; r < local->hw.wiphy->n_radio; r++) {
|
||||
+ radio = &local->hw.wiphy->radio[r];
|
||||
+
|
||||
+ for (i = 0; i < req->n_channels; i++) {
|
||||
+ struct cfg80211_chan_def chandef = {};
|
||||
+
|
||||
+ chandef.chan = req->channels[i];
|
||||
+ cfg80211_chandef_create(&chandef, req->channels[i],
|
||||
+ NL80211_CHAN_NO_HT);
|
||||
+ if (!cfg80211_radio_chandef_valid(radio, &chandef))
|
||||
+ continue;
|
||||
+
|
||||
+ mask |= BIT(r);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return mask;
|
||||
+}
|
||||
+
|
||||
+u32 ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata,
|
||||
+ u32 radio_mask)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_sub_if_data *sdata_iter;
|
||||
+ struct wiphy *wiphy = local->hw.wiphy;
|
||||
+ struct ieee80211_chanctx_conf *conf;
|
||||
+ struct ieee80211_link_data *link;
|
||||
unsigned int link_id;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- if (!ieee80211_is_radar_required(local))
|
||||
+ if (!ieee80211_is_radar_required(local, radio_mask))
|
||||
return true;
|
||||
|
||||
if (!regulatory_pre_cac_allowed(local->hw.wiphy))
|
||||
return false;
|
||||
|
||||
list_for_each_entry(sdata_iter, &local->interfaces, list) {
|
||||
- for_each_valid_link(&sdata_iter->wdev, link_id)
|
||||
- if (sdata_iter->wdev.links[link_id].cac_started)
|
||||
+ for_each_valid_link(&sdata_iter->wdev, link_id) {
|
||||
+ if (!sdata_iter->wdev.links[link_id].cac_started)
|
||||
+ continue;
|
||||
+
|
||||
+ if (!wiphy->n_radio)
|
||||
return false;
|
||||
+
|
||||
+ link = sdata_dereference(sdata->link[link_id], sdata);
|
||||
+ if (!link)
|
||||
+ continue;
|
||||
+
|
||||
+ conf = wiphy_dereference(wiphy, link->conf->chanctx_conf);
|
||||
+ if (!conf)
|
||||
+ continue;
|
||||
+
|
||||
+ if (conf->radio_idx >= 0 &&
|
||||
+ (radio_mask & BIT(conf->radio_idx)))
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ieee80211_can_scan(struct ieee80211_local *local,
|
||||
- struct ieee80211_sub_if_data *sdata)
|
||||
+ struct ieee80211_sub_if_data *sdata,
|
||||
+ u32 radio_mask)
|
||||
{
|
||||
- if (!__ieee80211_can_leave_ch(sdata))
|
||||
- return false;
|
||||
-
|
||||
- if (!list_empty(&local->roc_list))
|
||||
+ if (!list_empty(&local->roc_list) &&
|
||||
+ (!local->hw.wiphy->n_radio ||
|
||||
+ (radio_mask & ieee80211_offchannel_radio_mask(local))))
|
||||
return false;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
|
||||
@@ -612,15 +659,22 @@ static bool ieee80211_can_scan(struct ie
|
||||
|
||||
void ieee80211_run_deferred_scan(struct ieee80211_local *local)
|
||||
{
|
||||
+ struct ieee80211_sub_if_data *sdata;
|
||||
+ struct cfg80211_scan_request *req;
|
||||
+ u32 radio_mask;
|
||||
+
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- if (!local->scan_req || local->scanning)
|
||||
+ req = wiphy_dereference(local->hw.wiphy, local->scan_req);
|
||||
+ if (!req || local->scanning)
|
||||
+ return;
|
||||
+
|
||||
+ radio_mask = ieee80211_scan_req_radio_mask(local, req);
|
||||
+ sdata = wiphy_dereference(local->hw.wiphy, local->scan_sdata);
|
||||
+ if (!ieee80211_can_leave_ch(sdata, radio_mask))
|
||||
return;
|
||||
|
||||
- if (!ieee80211_can_scan(local,
|
||||
- rcu_dereference_protected(
|
||||
- local->scan_sdata,
|
||||
- lockdep_is_held(&local->hw.wiphy->mtx))))
|
||||
+ if (!ieee80211_can_scan(local, sdata, radio_mask))
|
||||
return;
|
||||
|
||||
wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work,
|
||||
@@ -703,6 +757,7 @@ static int __ieee80211_start_scan(struct
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
bool hw_scan = local->ops->hw_scan;
|
||||
+ u32 radio_mask;
|
||||
int rc;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
@@ -717,10 +772,11 @@ static int __ieee80211_start_scan(struct
|
||||
!(sdata->vif.active_links & BIT(req->tsf_report_link_id)))
|
||||
return -EINVAL;
|
||||
|
||||
- if (!__ieee80211_can_leave_ch(sdata))
|
||||
+ radio_mask = ieee80211_scan_req_radio_mask(local, req);
|
||||
+ if (!ieee80211_can_leave_ch(sdata, radio_mask))
|
||||
return -EBUSY;
|
||||
|
||||
- if (!ieee80211_can_scan(local, sdata)) {
|
||||
+ if (!ieee80211_can_scan(local, sdata, radio_mask)) {
|
||||
/* wait for the work to finish/time out */
|
||||
rcu_assign_pointer(local->scan_req, req);
|
||||
rcu_assign_pointer(local->scan_sdata, sdata);
|
||||
--- a/net/mac80211/util.c
|
||||
+++ b/net/mac80211/util.c
|
||||
@@ -3644,6 +3644,23 @@ again:
|
||||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||
}
|
||||
|
||||
+u32 ieee80211_chandef_radio_mask(struct ieee80211_local *local,
|
||||
+ struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ struct wiphy *wiphy = local->hw.wiphy;
|
||||
+ const struct wiphy_radio *radio;
|
||||
+ u32 mask = 0;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < wiphy->n_radio; i++) {
|
||||
+ radio = &wiphy->radio[i];
|
||||
+ if (cfg80211_radio_chandef_valid(radio, chandef))
|
||||
+ mask |= BIT(i);
|
||||
+ }
|
||||
+
|
||||
+ return mask;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Returns true if smps_mode_new is strictly more restrictive than
|
||||
* smps_mode_old.
|
||||
--- a/net/wireless/util.c
|
||||
+++ b/net/wireless/util.c
|
||||
@@ -2911,6 +2911,9 @@ bool cfg80211_radio_chandef_valid(const
|
||||
{
|
||||
u32 freq, width;
|
||||
|
||||
+ if (!cfg80211_chandef_valid(chandef))
|
||||
+ return false;
|
||||
+
|
||||
freq = ieee80211_chandef_to_khz(chandef);
|
||||
width = nl80211_chan_width_to_mhz(chandef->width);
|
||||
if (!ieee80211_radio_freq_range_valid(radio, freq, width))
|
Loading…
Reference in a new issue