Fix: - Send CAC_ABORT event when background CAC is canceled - Cancel CAC done workqueue when radar is detected - Release background wdev ownership when CAC is aborted or passed - Clean lower layer background radar state when CAC is aborted or passed - Prevent sending abort event when radar event is sent Signed-off-by: Janusz Dziedzic --- net/wireless/mlme.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 3fc175f9f868..212178d04efa 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1115,8 +1115,10 @@ void __cfg80211_radar_event(struct wiphy *wiphy, */ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); - if (offchan) + if (offchan) { + cancel_delayed_work(&rdev->background_cac_done_wk); queue_work(cfg80211_wq, &rdev->background_cac_abort_wk); + } cfg80211_sched_dfs_chan_update(rdev); @@ -1187,21 +1189,16 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, if (!cfg80211_chandef_valid(chandef)) return; - if (!rdev->background_radar_wdev) - return; - switch (event) { case NL80211_RADAR_CAC_FINISHED: cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef)); queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); cfg80211_sched_dfs_chan_update(rdev); - wdev = rdev->background_radar_wdev; break; case NL80211_RADAR_CAC_ABORTED: if (!cancel_delayed_work(&rdev->background_cac_done_wk)) return; - wdev = rdev->background_radar_wdev; break; case NL80211_RADAR_CAC_STARTED: break; @@ -1213,17 +1210,6 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL); } -static void -cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, - const struct cfg80211_chan_def *chandef, - enum nl80211_radar_event event) -{ - guard(wiphy)(&rdev->wiphy); - - __cfg80211_background_cac_event(rdev, rdev->background_radar_wdev, - chandef, event); -} - void cfg80211_background_cac_done_wk(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); @@ -1231,18 +1217,30 @@ void cfg80211_background_cac_done_wk(struct work_struct *work) rdev = container_of(delayed_work, struct cfg80211_registered_device, background_cac_done_wk); - cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef, - NL80211_RADAR_CAC_FINISHED); + + guard(wiphy)(&rdev->wiphy); + + rdev_set_radar_background(rdev, NULL); + rdev->background_radar_wdev = NULL; + + __cfg80211_background_cac_event(rdev, rdev->background_radar_wdev, + &rdev->background_radar_chandef, + NL80211_RADAR_CAC_FINISHED); } void cfg80211_background_cac_abort_wk(struct work_struct *work) { struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; rdev = container_of(work, struct cfg80211_registered_device, background_cac_abort_wk); - cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef, - NL80211_RADAR_CAC_ABORTED); + + guard(wiphy)(&rdev->wiphy); + + wdev = rdev->background_radar_wdev; + if (wdev) + cfg80211_stop_background_radar_detection(wdev); } void cfg80211_background_cac_abort(struct wiphy *wiphy) -- 2.43.0 Allow to track and check CAC state from user mode by simple check phy channels eg. using iw phy1 channels command. This is done for regular CAC and background CAC. It is important for background CAC while we can start it from any app (eg. iw or hostapd). Signed-off-by: Janusz Dziedzic --- include/net/cfg80211.h | 3 +++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/chan.c | 27 +++++++++++++++++++++++++++ net/wireless/core.h | 4 ++++ net/wireless/mlme.c | 7 +++++++ net/wireless/nl80211.c | 7 +++++++ 6 files changed, 54 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fc01de19c798..e00045c150e7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -190,6 +190,8 @@ enum ieee80211_channel_flags { * on this channel. * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered. * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels. + * @cac_start_time: timestamp (CLOCK_BOOTTIME, nanoseconds) when CAC was + * started on this channel. Zero when CAC is not in progress. * @psd: power spectral density (in dBm) */ struct ieee80211_channel { @@ -207,6 +209,7 @@ struct ieee80211_channel { enum nl80211_dfs_state dfs_state; unsigned long dfs_state_entered; unsigned int dfs_cac_ms; + u64 cac_start_time; s8 psd; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b63f71850906..c75aa039f096 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4480,6 +4480,10 @@ enum nl80211_wmm_rule { * as a non-primary subchannel. Only applicable to S1G channels. * @NL80211_FREQUENCY_ATTR_NO_UHR: UHR operation is not allowed on this channel * in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_CAC_START_TIME: Channel Availability Check (CAC) + * start time (CLOCK_BOOTTIME, nanoseconds). Only present when CAC is + * currently in progress on this channel. + * @NL80211_FREQUENCY_ATTR_PAD: attribute used for padding for 64-bit alignment * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -4530,6 +4534,8 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_16MHZ, NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY, NL80211_FREQUENCY_ATTR_NO_UHR, + NL80211_FREQUENCY_ATTR_CAC_START_TIME, + NL80211_FREQUENCY_ATTR_PAD, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 68221b1ab45e..dfe319565280 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -642,6 +642,33 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy, } } +void cfg80211_set_cac_state(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + bool cac_ongoing) +{ + struct ieee80211_channel *c; + int width; + u64 cac_time; + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return; + + width = cfg80211_chandef_get_width(chandef); + if (width < 0) + return; + + /* Get the same timestamp for all subchannels */ + cac_time = cac_ongoing ? ktime_get_boottime_ns() : 0; + + for_each_subchan(chandef, freq, cf) { + c = ieee80211_get_channel_khz(wiphy, freq); + if (!c) + continue; + + c->cac_start_time = cac_time; + } +} + static bool cfg80211_dfs_permissive_check_wdev(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, diff --git a/net/wireless/core.h b/net/wireless/core.h index 6ac57b7b2615..6cace846d7a3 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -481,6 +481,10 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state); +void cfg80211_set_cac_state(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + bool cac_ongoing); + void cfg80211_dfs_channels_update_work(struct work_struct *work); void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 212178d04efa..cafb39596a40 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1162,9 +1162,11 @@ void cfg80211_cac_event(struct net_device *netdev, fallthrough; case NL80211_RADAR_CAC_ABORTED: wdev->links[link_id].cac_started = false; + cfg80211_set_cac_state(wiphy, chandef, false); break; case NL80211_RADAR_CAC_STARTED: wdev->links[link_id].cac_started = true; + cfg80211_set_cac_state(wiphy, chandef, true); break; default: WARN_ON(1); @@ -1192,15 +1194,18 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, switch (event) { case NL80211_RADAR_CAC_FINISHED: cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); + cfg80211_set_cac_state(wiphy, chandef, false); memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef)); queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); cfg80211_sched_dfs_chan_update(rdev); break; case NL80211_RADAR_CAC_ABORTED: + cfg80211_set_cac_state(wiphy, chandef, false); if (!cancel_delayed_work(&rdev->background_cac_done_wk)) return; break; case NL80211_RADAR_CAC_STARTED: + cfg80211_set_cac_state(wiphy, chandef, true); break; default: return; @@ -1306,7 +1311,9 @@ void cfg80211_stop_radar_detection(struct wireless_dev *wdev) continue; chandef = *wdev_chandef(wdev, link_id); + wdev->links[link_id].cac_started = false; rdev_end_cac(rdev, wdev->netdev, link_id); + cfg80211_set_cac_state(wiphy, &chandef, false); nl80211_radar_notify(rdev, &chandef, NL80211_RADAR_CAC_ABORTED, wdev->netdev, GFP_KERNEL); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6e58b238a1f8..f9a739d1ebfb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1334,6 +1334,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, if ((chan->flags & IEEE80211_CHAN_NO_UHR) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_UHR)) goto nla_put_failure; + if (chan->cac_start_time && + nla_put_u64_64bit(msg, + NL80211_FREQUENCY_ATTR_CAC_START_TIME, + chan->cac_start_time, + NL80211_FREQUENCY_ATTR_PAD)) + goto nla_put_failure; } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, @@ -11354,6 +11360,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, wdev->links[link_id].cac_started = true; wdev->links[link_id].cac_start_time = jiffies; wdev->links[link_id].cac_time_ms = cac_time_ms; + cfg80211_set_cac_state(wiphy, &chandef, true); return 0; } -- 2.43.0 In case we report radar event add also information this is connected with background one, so user mode application like hostapd, could check it and behave correctly. Signed-off-by: Janusz Dziedzic --- net/wireless/mlme.c | 6 ++++-- net/wireless/nl80211.c | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index cafb39596a40..da3786417713 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1226,11 +1226,12 @@ void cfg80211_background_cac_done_wk(struct work_struct *work) guard(wiphy)(&rdev->wiphy); rdev_set_radar_background(rdev, NULL); - rdev->background_radar_wdev = NULL; __cfg80211_background_cac_event(rdev, rdev->background_radar_wdev, &rdev->background_radar_chandef, NL80211_RADAR_CAC_FINISHED); + + rdev->background_radar_wdev = NULL; } void cfg80211_background_cac_abort_wk(struct work_struct *work) @@ -1330,11 +1331,12 @@ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev) return; rdev_set_radar_background(rdev, NULL); - rdev->background_radar_wdev = NULL; /* Release offchain ownership */ __cfg80211_background_cac_event(rdev, wdev, &rdev->background_radar_chandef, NL80211_RADAR_CAC_ABORTED); + + rdev->background_radar_wdev = NULL; } int cfg80211_assoc_ml_reconf(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f9a739d1ebfb..47b7e0559c73 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -21238,6 +21238,13 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev, goto nla_put_failure; } + if (rdev->background_radar_wdev && + cfg80211_chandef_identical(&rdev->background_radar_chandef, + chandef)) { + if (nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND)) + goto nla_put_failure; + } + if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event)) goto nla_put_failure; -- 2.43.0 Report background CAC support and add allow to cancel background CAC and simulate radar. echo cancel > /sys/kernel/debug/ieee80211/phy2/hwsim/dfs_background_cac echo radar > /sys/kernel/debug/ieee80211/phy2/hwsim/dfs_background_cac Signed-off-by: Janusz Dziedzic --- drivers/net/wireless/virtual/mac80211_hwsim.c | 77 +++++++++++++++++++ drivers/net/wireless/virtual/mac80211_hwsim.h | 2 + 2 files changed, 79 insertions(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 4d9f5f87e814..8b5618c2111f 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -715,6 +715,7 @@ struct mac80211_hwsim_data { } ps; bool ps_poll_pending; struct dentry *debugfs; + struct cfg80211_chan_def radar_background_chandef; atomic_t pending_cookie; struct sk_buff_head pending; /* packets pending */ @@ -936,6 +937,7 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_PMSR_RESULT] = NLA_POLICY_NESTED(hwsim_pmsr_peers_result_policy), [HWSIM_ATTR_MULTI_RADIO] = { .type = NLA_FLAG }, [HWSIM_ATTR_SUPPORT_NAN_DEVICE] = { .type = NLA_FLAG }, + [HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR] = { .type = NLA_FLAG }, }; #if IS_REACHABLE(CONFIG_VIRTIO) @@ -1164,6 +1166,41 @@ static int hwsim_write_simulate_radar(void *dat, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(hwsim_simulate_radar, NULL, hwsim_write_simulate_radar, "%llu\n"); +static ssize_t hwsim_background_cac_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mac80211_hwsim_data *data = file->private_data; + char buf[8] = {}; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + /* Check if background radar channel is configured */ + if (!data->radar_background_chandef.chan) + return -ENOENT; + + if (sysfs_streq(buf, "radar")) + cfg80211_background_radar_event(data->hw->wiphy, + &data->radar_background_chandef, + GFP_KERNEL); + else if (sysfs_streq(buf, "cancel")) + cfg80211_background_cac_abort(data->hw->wiphy); + else + return -EINVAL; + + return count; +} + +static const struct file_operations hwsim_background_cac_ops = { + .write = hwsim_background_cac_write, + .open = simple_open, + .llseek = default_llseek, +}; + static int hwsim_fops_group_read(void *dat, u64 *val) { struct mac80211_hwsim_data *data = dat; @@ -4154,6 +4191,24 @@ static int mac80211_hwsim_change_nan_config(struct ieee80211_hw *hw, return 0; } +static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chan) +{ + struct mac80211_hwsim_data *data = hw->priv; + + if (!wiphy_ext_feature_isset(hw->wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND)) + return -EOPNOTSUPP; + + if (chan) + data->radar_background_chandef = *chan; + else + memset(&data->radar_background_chandef, 0, + sizeof(data->radar_background_chandef)); + + return 0; +} + #ifdef CONFIG_MAC80211_DEBUGFS #define HWSIM_DEBUGFS_OPS \ .link_add_debugfs = mac80211_hwsim_link_add_debugfs, @@ -4189,6 +4244,7 @@ static int mac80211_hwsim_change_nan_config(struct ieee80211_hw *hw, .start_nan = mac80211_hwsim_start_nan, \ .stop_nan = mac80211_hwsim_stop_nan, \ .nan_change_conf = mac80211_hwsim_change_nan_config, \ + .set_radar_background = mac80211_hwsim_set_radar_background, \ HWSIM_DEBUGFS_OPS #define HWSIM_NON_MLO_OPS \ @@ -4255,6 +4311,7 @@ struct hwsim_new_radio_params { bool mlo; const struct cfg80211_pmsr_capabilities *pmsr_capa; bool nan_device; + bool background_radar; }; static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, @@ -4340,6 +4397,12 @@ static int append_radio_msg(struct sk_buff *skb, int id, if (ret < 0) return ret; } + + if (param->background_radar) { + ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR); + if (ret < 0) + return ret; + } return 0; } @@ -5794,6 +5857,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_DFS_CONCURRENT); + if (param->background_radar) + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND); if (param->no_vif) ieee80211_hw_set(hw, NO_AUTO_VIF); @@ -5832,6 +5898,10 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, debugfs_create_file("dfs_simulate_radar", 0222, data->debugfs, data, &hwsim_simulate_radar); + if (param->background_radar) + debugfs_create_file("dfs_background_cac", 0200, + data->debugfs, + data, &hwsim_background_cac_ops); if (param->pmsr_capa) { data->pmsr_capa = *param->pmsr_capa; @@ -5950,6 +6020,9 @@ static int mac80211_hwsim_get_radio(struct sk_buff *skb, param.channels = data->channels; param.hwname = wiphy_name(data->hw->wiphy); param.pmsr_capa = &data->pmsr_capa; + param.background_radar = + wiphy_ext_feature_isset(data->hw->wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND); res = append_radio_msg(skb, data->idx, ¶m); if (res < 0) @@ -6387,6 +6460,9 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) if (info->attrs[HWSIM_ATTR_MULTI_RADIO]) param.multi_radio = true; + if (info->attrs[HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR]) + param.background_radar = true; + if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]) param.reg_alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]); @@ -7165,6 +7241,7 @@ static int __init init_mac80211_hwsim(void) param.p2p_device = support_p2p_device; param.mlo = mlo; param.multi_radio = multi_radio; + param.background_radar = true; param.use_chanctx = channels > 1 || mlo || multi_radio; param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK; if (param.p2p_device) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.h b/drivers/net/wireless/virtual/mac80211_hwsim.h index c2d06cf852a5..a022cd5c0f1c 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim.h @@ -161,6 +161,7 @@ enum hwsim_commands { * Adds one radio for each band. Number of supported channels will be set for * each radio instead of for the wiphy. * @HWSIM_ATTR_SUPPORT_NAN_DEVICE: support NAN Device virtual interface (flag) + * @HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR: background radar/CAC support (flag) * @__HWSIM_ATTR_MAX: enum limit */ enum hwsim_attrs { @@ -195,6 +196,7 @@ enum hwsim_attrs { HWSIM_ATTR_PMSR_RESULT, HWSIM_ATTR_MULTI_RADIO, HWSIM_ATTR_SUPPORT_NAN_DEVICE, + HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) -- 2.43.0