From bb918e40dcc7d082f898234cf29cd545de78621e Mon Sep 17 00:00:00 2001 From: StanleyYP Wang Date: Wed, 15 Nov 2023 15:05:17 +0800 Subject: [PATCH] mac80211: mtk: add DFS CAC countdown in CSA flow Signed-off-by: StanleyYP Wang --- include/net/cfg80211.h | 32 +++++++++++++++ net/mac80211/cfg.c | 84 +++++++++++++++++++++++++++++++++++--- net/mac80211/ieee80211_i.h | 2 + net/mac80211/iface.c | 2 + net/mac80211/mlme.c | 6 ++- net/mac80211/util.c | 11 ++++- net/wireless/chan.c | 72 ++++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 5 ++- net/wireless/rdev-ops.h | 17 ++++++++ 9 files changed, 221 insertions(+), 10 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 03f072f..a443b0d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4308,6 +4308,10 @@ struct cfg80211_ops { struct net_device *dev, struct cfg80211_chan_def *chandef, u32 cac_time_ms); + int (*start_radar_detection_post_csa)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms); void (*end_cac)(struct wiphy *wiphy, struct net_device *dev); int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev, @@ -7796,6 +7800,34 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, enum nl80211_iftype iftype); +/** + * cfg80211_reg_can_beacon_dfs_relax - check if beaconing is allowed with DFS & IR-relaxation + * @wiphy: the wiphy + * @chandef: the channel definition + * @iftype: interface type + * + * Return: %true if there is no secondary channel or the secondary channel(s) + * can be used for beaconing. This version bypasses radar channel check, allowing + * channel switch to a USABLE DFS channel and performing CAC after the channel switch. + * It also checks if IR-relaxation conditions apply, to allow beaconing under more + * permissive conditions. + * + * Requires the wiphy mutex to be held. + */ +bool cfg80211_reg_can_beacon_dfs_relax(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype); + +/** + * cfg80211_start_radar_detection_post_csa - start radar detection after CSA + * @wiphy: the wiphy + * @wdev: the wireless device + * @chandef: the channel definition to start radar detection on + */ +int cfg80211_start_radar_detection_post_csa(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef); + /* * cfg80211_ch_switch_notify - update wdev channel and notify userspace * @dev: the device which switched channels diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 56381f8..7a30ca6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3328,6 +3328,39 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, return 0; } +void ieee80211_cac_sta_flush_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + cac_sta_flush_work); + + __sta_info_flush(sdata, true); +} + +static int ieee80211_start_radar_detection_post_csa(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + + if (!list_empty(&local->roc_list) || local->scanning) + return -EBUSY; + + /* whatever, but channel contexts should not complain about that one */ + sdata->smps_mode = IEEE80211_SMPS_OFF; + sdata->needed_rx_chains = local->rx_chains; + + ieee80211_queue_work(&local->hw, &sdata->cac_sta_flush_work); + + ieee80211_queue_delayed_work(&local->hw, + &sdata->dfs_cac_timer_work, + msecs_to_jiffies(cac_time_ms)); + + return 1; +} + static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -3361,6 +3394,11 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) &sdata->csa_chandef)) return -EINVAL; + err = cfg80211_start_radar_detection_post_csa(local->hw.wiphy, &sdata->wdev, + &sdata->vif.bss_conf.chandef); + if (err) + return err > 0 ? 0 : err; + sdata->vif.csa_active = false; err = ieee80211_set_after_csa_beacon(sdata, &changed); @@ -3428,6 +3466,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, switch (sdata->vif.type) { case NL80211_IFTYPE_AP: + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after); if (!sdata->u.ap.next_beacon) @@ -3586,15 +3629,14 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (!list_empty(&local->roc_list) || local->scanning) return -EBUSY; - if (sdata->wdev.cac_started) - return -EBUSY; - if (cfg80211_chandef_identical(¶ms->chandef, &sdata->vif.bss_conf.chandef)) return -EINVAL; - /* don't allow another channel switch if one is already active. */ - if (sdata->vif.csa_active) + /* don't allow another channel switch if one is already active + * unless its during post CSA radar detection. + */ + if (sdata->vif.csa_active && !sdata->wdev.cac_started) return -EBUSY; mutex_lock(&local->chanctx_mtx); @@ -3646,6 +3688,14 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, goto out; } + /* Finalize CSA immediately if CAC is started during last channel switch */ + if (sdata->wdev.cac_started) { + ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); + cancel_delayed_work(&sdata->dfs_cac_timer_work); + sdata->wdev.cac_started = false; + changed = 0; + } + sdata->csa_chandef = params->chandef; sdata->csa_block_tx = params->block_tx; sdata->vif.csa_active = true; @@ -3661,6 +3711,23 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, ieee80211_bss_info_change_notify(sdata, changed); drv_channel_switch_beacon(sdata, ¶ms->chandef); } else { + struct ieee80211_sub_if_data *tmp; + int n_assigned = 0, n_reserved = 0; + + list_for_each_entry(tmp, &chanctx->assigned_vifs, + assigned_chanctx_list) { + n_assigned++; + if (tmp->reserved_chanctx) + n_reserved++; + } + + /* Wait for all interfaces to be ready */ + if (n_assigned != n_reserved) { + sdata->reserved_ready = true; + err = 0; + goto out; + } + /* if the beacon didn't change, we can finalize immediately */ ieee80211_csa_finalize(sdata); } @@ -4538,7 +4605,11 @@ ieee80211_skip_cac(struct wireless_dev *wdev) cancel_delayed_work(&sdata->dfs_cac_timer_work); if (wdev->cac_started) { - ieee80211_vif_release_channel(sdata); + if (sdata->vif.csa_active) + ieee80211_queue_work(&sdata->local->hw, + &sdata->csa_finalize_work); + else + ieee80211_vif_release_channel(sdata); cac_time_ms = wdev->cac_time_ms; wdev->cac_start_time = jiffies - msecs_to_jiffies(cac_time_ms + 1); @@ -4630,6 +4701,7 @@ const struct cfg80211_ops mac80211_config_ops = { #endif .get_channel = ieee80211_cfg_get_channel, .start_radar_detection = ieee80211_start_radar_detection, + .start_radar_detection_post_csa = ieee80211_start_radar_detection_post_csa, .end_cac = ieee80211_end_cac, .channel_switch = ieee80211_channel_switch, .set_qos_map = ieee80211_set_qos_map, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2519c14..bb5906d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -962,6 +962,7 @@ struct ieee80211_sub_if_data { struct mac80211_qos_map __rcu *qos_map; struct work_struct csa_finalize_work; + struct work_struct cac_sta_flush_work; bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ struct cfg80211_chan_def csa_chandef; @@ -1812,6 +1813,7 @@ int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, void ieee80211_csa_finalize_work(struct work_struct *work); int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params); +void ieee80211_cac_sta_flush_work(struct work_struct *work); #define IEEE80211_BSS_COLOR_AGEOUT_TIME 10 #define IEEE80211_BSS_COLOR_MAX 64 diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 00b0443..ef32d53 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -463,6 +463,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do sdata_unlock(sdata); cancel_work_sync(&sdata->csa_finalize_work); + cancel_work_sync(&sdata->cac_sta_flush_work); cancel_work_sync(&sdata->color_change_finalize_work); cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); @@ -1749,6 +1750,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, INIT_WORK(&sdata->work, ieee80211_iface_work); INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work); + INIT_WORK(&sdata->cac_sta_flush_work, ieee80211_cac_sta_flush_work); INIT_WORK(&sdata->color_change_finalize_work, ieee80211_color_change_finalize_work); INIT_LIST_HEAD(&sdata->assigned_chanctx_list); INIT_LIST_HEAD(&sdata->reserved_chanctx_list); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2dbc18c..ed81ebf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1870,7 +1870,11 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work) mutex_lock(&sdata->local->mtx); if (sdata->wdev.cac_started) { - ieee80211_vif_release_channel(sdata); + if (sdata->vif.csa_active) + ieee80211_queue_work(&sdata->local->hw, + &sdata->csa_finalize_work); + else + ieee80211_vif_release_channel(sdata); cfg80211_cac_event(sdata->dev, &chandef, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 26cd627..e07fe73 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3873,7 +3873,16 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) if (sdata->wdev.cac_started) { chandef = sdata->vif.bss_conf.chandef; - ieee80211_vif_release_channel(sdata); + if (sdata->vif.csa_active) { + sdata->vif.csa_active = false; + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } + } else { + ieee80211_vif_release_channel(sdata); + } cfg80211_cac_event(sdata->dev, &chandef, NL80211_RADAR_CAC_ABORTED, diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 9f651f9..f02598b 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -1262,6 +1262,78 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_reg_can_beacon_relax); +bool cfg80211_reg_can_beacon_dfs_relax(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + u32 prohibited_flags = IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_RADAR; + + lockdep_assert_held(&rdev->wiphy.mtx); + + /* Bypass available and usable dfs channel */ + if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 && + (cfg80211_chandef_dfs_usable(wiphy, chandef) || + cfg80211_chandef_dfs_available(wiphy, chandef))) + prohibited_flags = IEEE80211_CHAN_DISABLED; + + /* Under certain conditions suggested by some regulatory bodies a + * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag + * only if such relaxations are not enabled and the conditions are not + * met. + */ + if (!cfg80211_ir_permissive_chan(wiphy, iftype, chandef->chan)) + prohibited_flags |= IEEE80211_CHAN_NO_IR; + + return cfg80211_chandef_usable(wiphy, chandef, prohibited_flags); +} +EXPORT_SYMBOL(cfg80211_reg_can_beacon_dfs_relax); + +int cfg80211_start_radar_detection_post_csa(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + u32 cac_time_ms; + enum nl80211_dfs_regions dfs_region; + int ret = 0; + + /* Update DFS channel state especially when original channel include DFS channel */ + cfg80211_sched_dfs_chan_update(rdev); + + if (cfg80211_chandef_dfs_available(wiphy, chandef)) + goto out; + + dfs_region = reg_get_dfs_region(wiphy); + if (dfs_region == NL80211_DFS_UNSET) + goto out; + + cac_time_ms = cfg80211_chandef_dfs_cac_time(wiphy, chandef); + if (WARN_ON(!cac_time_ms)) + cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; + + pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n", + __func__, dfs_region, chandef->center_freq1, chandef->center_freq2, cac_time_ms); + + ret = rdev_start_radar_detection_post_csa(rdev, wdev->netdev, chandef, cac_time_ms); + if (ret > 0) { + wdev->chandef = *chandef; + wdev->cac_started = true; + wdev->cac_start_time = jiffies; + wdev->cac_time_ms = cac_time_ms; + if (rdev->background_cac_started && + cfg80211_is_sub_chan(chandef, rdev->background_radar_chandef.chan)) { + cfg80211_stop_background_radar_detection(rdev->background_radar_wdev); + } + cfg80211_cac_event(wdev->netdev, chandef, NL80211_RADAR_CAC_STARTED, GFP_KERNEL); + } + +out: + return ret; +} +EXPORT_SYMBOL(cfg80211_start_radar_detection_post_csa); + int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, struct cfg80211_chan_def *chandef) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 97c2833..4883b1f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9625,8 +9625,9 @@ skip_beacons: cfg80211_set_dfs_state(&rdev->wiphy, ¶ms.chandef, NL80211_DFS_AVAILABLE); } - if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef, - wdev->iftype)) { + /* handle DFS CAC after CSA is sent */ + if (!cfg80211_reg_can_beacon_dfs_relax(&rdev->wiphy, ¶ms.chandef, + wdev->iftype)) { err = -EINVAL; goto free; } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 26f4604..f4d050b 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1190,6 +1190,23 @@ rdev_start_radar_detection(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_start_radar_detection_post_csa(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) +{ + int ret = -EOPNOTSUPP; + + trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef, + cac_time_ms); + if (rdev->ops->start_radar_detection_post_csa) + ret = rdev->ops->start_radar_detection_post_csa(&rdev->wiphy, dev, + chandef, cac_time_ms); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline void rdev_end_cac(struct cfg80211_registered_device *rdev, struct net_device *dev) -- 2.18.0