mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	mac80211: backport upstream DFS fixes
Mostly MLO related Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		
							parent
							
								
									c1c2b61254
								
							
						
					
					
						commit
						ec61ccc0d3
					
				| @ -1,5 +1,14 @@ | ||||
| --- a/ath10k-6.9/mac.c
 | ||||
| +++ b/ath10k-6.9/mac.c
 | ||||
| @@ -1675,7 +1675,7 @@ static void ath10k_recalc_radar_detectio
 | ||||
|  		 * by indicating that radar was detected. | ||||
|  		 */ | ||||
|  		ath10k_warn(ar, "failed to start CAC: %d\n", ret); | ||||
| -		ieee80211_radar_detected(ar->hw);
 | ||||
| +		ieee80211_radar_detected(ar->hw, NULL);
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -6238,7 +6238,7 @@ err:
 | ||||
|  	return ret; | ||||
|  } | ||||
| @ -9,3 +18,25 @@ | ||||
|  { | ||||
|  	struct ath10k *ar = hw->priv; | ||||
|  	u32 opt; | ||||
| --- a/ath10k-6.9/debug.c
 | ||||
| +++ b/ath10k-6.9/debug.c
 | ||||
| @@ -3319,7 +3319,7 @@ static ssize_t ath10k_write_simulate_rad
 | ||||
|  	if (!arvif->is_started) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	ieee80211_radar_detected(ar->hw);
 | ||||
| +	ieee80211_radar_detected(ar->hw, NULL);
 | ||||
|   | ||||
|  	return count; | ||||
|  } | ||||
| --- a/ath10k-6.9/wmi.c
 | ||||
| +++ b/ath10k-6.9/wmi.c
 | ||||
| @@ -4402,7 +4402,7 @@ static void ath10k_radar_detected(struct
 | ||||
|  	if (ar->dfs_block_radar_events) | ||||
|  		ath10k_info(ar, "DFS Radar detected, but ignored as requested\n"); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(ar->hw);
 | ||||
| +		ieee80211_radar_detected(ar->hw, NULL);
 | ||||
|  } | ||||
|   | ||||
|  static void ath10k_radar_confirmation_work(struct work_struct *work) | ||||
|  | ||||
| @ -0,0 +1,64 @@ | ||||
| From: Issam Hamdi <ih@simonwunderlich.de> | ||||
| Date: Fri, 16 Aug 2024 16:24:18 +0200 | ||||
| Subject: [PATCH] wifi: cfg80211: Set correct chandef when starting CAC | ||||
| 
 | ||||
| When starting CAC in a mode other than AP mode, it return a | ||||
| "WARNING: CPU: 0 PID: 63 at cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211]" | ||||
| caused by the chandef.chan being null at the end of CAC. | ||||
| 
 | ||||
| Solution: Ensure the channel definition is set for the different modes | ||||
| when starting CAC to avoid getting a NULL 'chan' at the end of CAC. | ||||
| 
 | ||||
|  Call Trace: | ||||
|   ? show_regs.part.0+0x14/0x16 | ||||
|   ? __warn+0x67/0xc0 | ||||
|   ? cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211] | ||||
|   ? report_bug+0xa7/0x130 | ||||
|   ? exc_overflow+0x30/0x30 | ||||
|   ? handle_bug+0x27/0x50 | ||||
|   ? exc_invalid_op+0x18/0x60 | ||||
|   ? handle_exception+0xf6/0xf6 | ||||
|   ? exc_overflow+0x30/0x30 | ||||
|   ? cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211] | ||||
|   ? exc_overflow+0x30/0x30 | ||||
|   ? cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211] | ||||
|   ? regulatory_propagate_dfs_state.cold+0x1b/0x4c [cfg80211] | ||||
|   ? cfg80211_propagate_cac_done_wk+0x1a/0x30 [cfg80211] | ||||
|   ? process_one_work+0x165/0x280 | ||||
|   ? worker_thread+0x120/0x3f0 | ||||
|   ? kthread+0xc2/0xf0 | ||||
|   ? process_one_work+0x280/0x280 | ||||
|   ? kthread_complete_and_exit+0x20/0x20 | ||||
|   ? ret_from_fork+0x19/0x24 | ||||
| 
 | ||||
| Reported-by: Kretschmer Mathias <mathias.kretschmer@fit.fraunhofer.de> | ||||
| Signed-off-by: Issam Hamdi <ih@simonwunderlich.de> | ||||
| Link: https://patch.msgid.link/20240816142418.3381951-1-ih@simonwunderlich.de | ||||
| [shorten subject, remove OCB, reorder cases to match previous list] | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/net/wireless/nl80211.c
 | ||||
| +++ b/net/wireless/nl80211.c
 | ||||
| @@ -10144,7 +10144,20 @@ static int nl80211_start_radar_detection
 | ||||
|   | ||||
|  	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms); | ||||
|  	if (!err) { | ||||
| -		wdev->links[0].ap.chandef = chandef;
 | ||||
| +		switch (wdev->iftype) {
 | ||||
| +		case NL80211_IFTYPE_AP:
 | ||||
| +		case NL80211_IFTYPE_P2P_GO:
 | ||||
| +			wdev->links[0].ap.chandef = chandef;
 | ||||
| +			break;
 | ||||
| +		case NL80211_IFTYPE_ADHOC:
 | ||||
| +			wdev->u.ibss.chandef = chandef;
 | ||||
| +			break;
 | ||||
| +		case NL80211_IFTYPE_MESH_POINT:
 | ||||
| +			wdev->u.mesh.chandef = chandef;
 | ||||
| +			break;
 | ||||
| +		default:
 | ||||
| +			break;
 | ||||
| +		}
 | ||||
|  		wdev->cac_started = true; | ||||
|  		wdev->cac_start_time = jiffies; | ||||
|  		wdev->cac_time_ms = cac_time_ms; | ||||
| @ -0,0 +1,136 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:19 +0530 | ||||
| Subject: [PATCH] Revert "wifi: mac80211: move radar detect work to sdata" | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| This reverts commit ce9e660ef32e ("wifi: mac80211: move radar detect work to sdata"). | ||||
| 
 | ||||
| To enable radar detection with MLO, it’s essential to handle it on a | ||||
| per-link basis. This is because when using MLO, multiple links may already | ||||
| be active and beaconing. In this scenario, another link should be able to | ||||
| initiate a radar detection. Also, if underlying links are associated with | ||||
| different hardware devices but grouped together for MLO, they could
 | ||||
| potentially start radar detection simultaneously. Therefore, it makes | ||||
| sense to manage radar detection settings separately for each link by moving | ||||
| them back to a per-link data structure. | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-2-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/net/mac80211/cfg.c
 | ||||
| +++ b/net/mac80211/cfg.c
 | ||||
| @@ -1658,7 +1658,7 @@ static int ieee80211_stop_ap(struct wiph
 | ||||
|   | ||||
|  	if (sdata->wdev.cac_started) { | ||||
|  		chandef = link_conf->chanreq.oper; | ||||
| -		wiphy_delayed_work_cancel(wiphy, &sdata->dfs_cac_timer_work);
 | ||||
| +		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
 | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_ABORTED, | ||||
|  				   GFP_KERNEL); | ||||
| @@ -3482,7 +3482,7 @@ static int ieee80211_start_radar_detecti
 | ||||
|  	if (err) | ||||
|  		goto out_unlock; | ||||
|   | ||||
| -	wiphy_delayed_work_queue(wiphy, &sdata->dfs_cac_timer_work,
 | ||||
| +	wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
 | ||||
|  				 msecs_to_jiffies(cac_time_ms)); | ||||
|   | ||||
|   out_unlock: | ||||
| @@ -3499,7 +3499,7 @@ static void ieee80211_end_cac(struct wip
 | ||||
|   | ||||
|  	list_for_each_entry(sdata, &local->interfaces, list) { | ||||
|  		wiphy_delayed_work_cancel(wiphy, | ||||
| -					  &sdata->dfs_cac_timer_work);
 | ||||
| +					  &sdata->deflink.dfs_cac_timer_work);
 | ||||
|   | ||||
|  		if (sdata->wdev.cac_started) { | ||||
|  			ieee80211_link_release_channel(&sdata->deflink); | ||||
| --- a/net/mac80211/ieee80211_i.h
 | ||||
| +++ b/net/mac80211/ieee80211_i.h
 | ||||
| @@ -1069,6 +1069,7 @@ struct ieee80211_link_data {
 | ||||
|  	int ap_power_level; /* in dBm */ | ||||
|   | ||||
|  	bool radar_required; | ||||
| +	struct wiphy_delayed_work dfs_cac_timer_work;
 | ||||
|   | ||||
|  	union { | ||||
|  		struct ieee80211_link_data_managed mgd; | ||||
| @@ -1167,8 +1168,6 @@ struct ieee80211_sub_if_data {
 | ||||
|  	struct ieee80211_link_data deflink; | ||||
|  	struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; | ||||
|   | ||||
| -	struct wiphy_delayed_work dfs_cac_timer_work;
 | ||||
| -
 | ||||
|  	/* for ieee80211_set_active_links_async() */ | ||||
|  	struct wiphy_work activate_links_work; | ||||
|  	u16 desired_active_links; | ||||
| --- a/net/mac80211/iface.c
 | ||||
| +++ b/net/mac80211/iface.c
 | ||||
| @@ -551,7 +551,7 @@ static void ieee80211_do_stop(struct iee
 | ||||
|  	wiphy_work_cancel(local->hw.wiphy, | ||||
|  			  &sdata->deflink.color_change_finalize_work); | ||||
|  	wiphy_delayed_work_cancel(local->hw.wiphy, | ||||
| -				  &sdata->dfs_cac_timer_work);
 | ||||
| +				  &sdata->deflink.dfs_cac_timer_work);
 | ||||
|   | ||||
|  	if (sdata->wdev.cac_started) { | ||||
|  		chandef = sdata->vif.bss_conf.chanreq.oper; | ||||
| @@ -1744,8 +1744,6 @@ static void ieee80211_setup_sdata(struct
 | ||||
|  	wiphy_work_init(&sdata->work, ieee80211_iface_work); | ||||
|  	wiphy_work_init(&sdata->activate_links_work, | ||||
|  			ieee80211_activate_links_work); | ||||
| -	wiphy_delayed_work_init(&sdata->dfs_cac_timer_work,
 | ||||
| -				ieee80211_dfs_cac_timer_work);
 | ||||
|   | ||||
|  	switch (type) { | ||||
|  	case NL80211_IFTYPE_P2P_GO: | ||||
| --- a/net/mac80211/link.c
 | ||||
| +++ b/net/mac80211/link.c
 | ||||
| @@ -45,6 +45,8 @@ void ieee80211_link_init(struct ieee8021
 | ||||
|  			  ieee80211_color_collision_detection_work); | ||||
|  	INIT_LIST_HEAD(&link->assigned_chanctx_list); | ||||
|  	INIT_LIST_HEAD(&link->reserved_chanctx_list); | ||||
| +	wiphy_delayed_work_init(&link->dfs_cac_timer_work,
 | ||||
| +				ieee80211_dfs_cac_timer_work);
 | ||||
|   | ||||
|  	if (!deflink) { | ||||
|  		switch (sdata->vif.type) { | ||||
| --- a/net/mac80211/mlme.c
 | ||||
| +++ b/net/mac80211/mlme.c
 | ||||
| @@ -3031,15 +3031,16 @@ void ieee80211_dynamic_ps_timer(struct t
 | ||||
|   | ||||
|  void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work) | ||||
|  { | ||||
| -	struct ieee80211_sub_if_data *sdata =
 | ||||
| -		container_of(work, struct ieee80211_sub_if_data,
 | ||||
| +	struct ieee80211_link_data *link =
 | ||||
| +		container_of(work, struct ieee80211_link_data,
 | ||||
|  			     dfs_cac_timer_work.work); | ||||
| -	struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chanreq.oper;
 | ||||
| +	struct cfg80211_chan_def chandef = link->conf->chanreq.oper;
 | ||||
| +	struct ieee80211_sub_if_data *sdata = link->sdata;
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(sdata->local->hw.wiphy); | ||||
|   | ||||
|  	if (sdata->wdev.cac_started) { | ||||
| -		ieee80211_link_release_channel(&sdata->deflink);
 | ||||
| +		ieee80211_link_release_channel(link);
 | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_FINISHED, | ||||
|  				   GFP_KERNEL); | ||||
| --- a/net/mac80211/util.c
 | ||||
| +++ b/net/mac80211/util.c
 | ||||
| @@ -3460,7 +3460,7 @@ void ieee80211_dfs_cac_cancel(struct iee
 | ||||
|   | ||||
|  	list_for_each_entry(sdata, &local->interfaces, list) { | ||||
|  		wiphy_delayed_work_cancel(local->hw.wiphy, | ||||
| -					  &sdata->dfs_cac_timer_work);
 | ||||
| +					  &sdata->deflink.dfs_cac_timer_work);
 | ||||
|   | ||||
|  		if (sdata->wdev.cac_started) { | ||||
|  			chandef = sdata->vif.bss_conf.chanreq.oper; | ||||
| @ -0,0 +1,50 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:20 +0530 | ||||
| Subject: [PATCH] wifi: mac80211: remove label usage in | ||||
|  ieee80211_start_radar_detection() | ||||
| 
 | ||||
| After locks rework [1], ieee80211_start_radar_detection() function is no | ||||
| longer acquiring any lock as such explicitly. Hence, it is not unlocking | ||||
| anything as well. However, label "out_unlock" is still used which creates | ||||
| confusion. Also, now there is no need of goto label as such. | ||||
| 
 | ||||
| Get rid of the goto logic and use direct return statements. | ||||
| 
 | ||||
| [1]: https://lore.kernel.org/all/20230828135928.b1c6efffe9ad.I4aec875e25abc9ef0b5ad1e70b5747fd483fbd3c@changeid/ | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-3-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/net/mac80211/cfg.c
 | ||||
| +++ b/net/mac80211/cfg.c
 | ||||
| @@ -3468,10 +3468,8 @@ static int ieee80211_start_radar_detecti
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(local->hw.wiphy); | ||||
|   | ||||
| -	if (!list_empty(&local->roc_list) || local->scanning) {
 | ||||
| -		err = -EBUSY;
 | ||||
| -		goto out_unlock;
 | ||||
| -	}
 | ||||
| +	if (!list_empty(&local->roc_list) || local->scanning)
 | ||||
| +		return -EBUSY;
 | ||||
|   | ||||
|  	/* whatever, but channel contexts should not complain about that one */ | ||||
|  	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; | ||||
| @@ -3480,13 +3478,12 @@ static int ieee80211_start_radar_detecti
 | ||||
|  	err = ieee80211_link_use_channel(&sdata->deflink, &chanreq, | ||||
|  					 IEEE80211_CHANCTX_SHARED); | ||||
|  	if (err) | ||||
| -		goto out_unlock;
 | ||||
| +		return err;
 | ||||
|   | ||||
|  	wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work, | ||||
|  				 msecs_to_jiffies(cac_time_ms)); | ||||
|   | ||||
| - out_unlock:
 | ||||
| -	return err;
 | ||||
| +	return 0;
 | ||||
|  } | ||||
|   | ||||
|  static void ieee80211_end_cac(struct wiphy *wiphy, | ||||
| @ -0,0 +1,42 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:21 +0530 | ||||
| Subject: [PATCH] wifi: trace: unlink rdev_end_cac trace event from | ||||
|  wiphy_netdev_evt class | ||||
| 
 | ||||
| rdev_end_cac trace event is linked with wiphy_netdev_evt event class. | ||||
| There is no option to pass link ID currently to wiphy_netdev_evt class. | ||||
| A subsequent change would pass link ID to rdev_end_cac event and hence | ||||
| it can no longer derive the event class from wiphy_netdev_evt. | ||||
| 
 | ||||
| Therefore, unlink rdev_end_cac event from wiphy_netdev_evt and define it's | ||||
| own independent trace event. Link ID would be passed in subsequent change. | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-4-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/net/wireless/trace.h
 | ||||
| +++ b/net/wireless/trace.h
 | ||||
| @@ -805,9 +805,18 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flus
 | ||||
|  	TP_ARGS(wiphy, netdev) | ||||
|  ); | ||||
|   | ||||
| -DEFINE_EVENT(wiphy_netdev_evt, rdev_end_cac,
 | ||||
| -	     TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
 | ||||
| -	     TP_ARGS(wiphy, netdev)
 | ||||
| +TRACE_EVENT(rdev_end_cac,
 | ||||
| +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
 | ||||
| +	TP_ARGS(wiphy, netdev),
 | ||||
| +	TP_STRUCT__entry(
 | ||||
| +		WIPHY_ENTRY
 | ||||
| +		NETDEV_ENTRY
 | ||||
| +	),
 | ||||
| +	TP_fast_assign(
 | ||||
| +		WIPHY_ASSIGN;
 | ||||
| +		NETDEV_ASSIGN;
 | ||||
| +	),
 | ||||
| +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
 | ||||
|  ); | ||||
|   | ||||
|  DECLARE_EVENT_CLASS(station_add_change, | ||||
| @ -0,0 +1,309 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:22 +0530 | ||||
| Subject: [PATCH] wifi: cfg80211: move DFS related members to links[] in | ||||
|  wireless_dev | ||||
| 
 | ||||
| A few members related to DFS handling are currently under per wireless | ||||
| device data structure. However, in order to support DFS with MLO, there is | ||||
| a need to have them on a per-link manner. | ||||
| 
 | ||||
| Hence, as a preliminary step, move members cac_started, cac_start_time | ||||
| and cac_time_ms to be on a per-link basis. | ||||
| 
 | ||||
| Since currently, link ID is not known at all places, use default value of | ||||
| 0 for now. | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-5-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/wireless/marvell/mwifiex/11h.c
 | ||||
| +++ b/drivers/net/wireless/marvell/mwifiex/11h.c
 | ||||
| @@ -117,7 +117,7 @@ void mwifiex_dfs_cac_work_queue(struct w
 | ||||
|  				     dfs_cac_work); | ||||
|   | ||||
|  	chandef = priv->dfs_chandef; | ||||
| -	if (priv->wdev.cac_started) {
 | ||||
| +	if (priv->wdev.links[0].cac_started) {
 | ||||
|  		mwifiex_dbg(priv->adapter, MSG, | ||||
|  			    "CAC timer finished; No radar detected\n"); | ||||
|  		cfg80211_cac_event(priv->netdev, &chandef, | ||||
| @@ -174,7 +174,7 @@ int mwifiex_stop_radar_detection(struct
 | ||||
|   */ | ||||
|  void mwifiex_abort_cac(struct mwifiex_private *priv) | ||||
|  { | ||||
| -	if (priv->wdev.cac_started) {
 | ||||
| +	if (priv->wdev.links[0].cac_started) {
 | ||||
|  		if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) | ||||
|  			mwifiex_dbg(priv->adapter, ERROR, | ||||
|  				    "failed to stop CAC in FW\n"); | ||||
| --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
 | ||||
| +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
 | ||||
| @@ -1880,7 +1880,7 @@ mwifiex_cfg80211_del_station(struct wiph
 | ||||
|  	struct mwifiex_sta_node *sta_node; | ||||
|  	u8 deauth_mac[ETH_ALEN]; | ||||
|   | ||||
| -	if (!priv->bss_started && priv->wdev.cac_started) {
 | ||||
| +	if (!priv->bss_started && priv->wdev.links[0].cac_started) {
 | ||||
|  		mwifiex_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__); | ||||
|  		mwifiex_abort_cac(priv); | ||||
|  	} | ||||
| @@ -3978,7 +3978,7 @@ mwifiex_cfg80211_channel_switch(struct w
 | ||||
|  		return -EBUSY; | ||||
|  	} | ||||
|   | ||||
| -	if (priv->wdev.cac_started)
 | ||||
| +	if (priv->wdev.links[0].cac_started)
 | ||||
|  		return -EBUSY; | ||||
|   | ||||
|  	if (cfg80211_chandef_identical(¶ms->chandef, | ||||
| --- a/drivers/net/wireless/quantenna/qtnfmac/event.c
 | ||||
| +++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
 | ||||
| @@ -520,21 +520,21 @@ static int qtnf_event_handle_radar(struc
 | ||||
|  		cfg80211_radar_event(wiphy, &chandef, GFP_KERNEL); | ||||
|  		break; | ||||
|  	case QLINK_RADAR_CAC_FINISHED: | ||||
| -		if (!vif->wdev.cac_started)
 | ||||
| +		if (!vif->wdev.links[0].cac_started)
 | ||||
|  			break; | ||||
|   | ||||
|  		cfg80211_cac_event(vif->netdev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); | ||||
|  		break; | ||||
|  	case QLINK_RADAR_CAC_ABORTED: | ||||
| -		if (!vif->wdev.cac_started)
 | ||||
| +		if (!vif->wdev.links[0].cac_started)
 | ||||
|  			break; | ||||
|   | ||||
|  		cfg80211_cac_event(vif->netdev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); | ||||
|  		break; | ||||
|  	case QLINK_RADAR_CAC_STARTED: | ||||
| -		if (vif->wdev.cac_started)
 | ||||
| +		if (vif->wdev.links[0].cac_started)
 | ||||
|  			break; | ||||
|   | ||||
|  		if (!wiphy_ext_feature_isset(wiphy, | ||||
| --- a/include/net/cfg80211.h
 | ||||
| +++ b/include/net/cfg80211.h
 | ||||
| @@ -6198,9 +6198,6 @@ enum ieee80211_ap_reg_power {
 | ||||
|   * @address: The address for this device, valid only if @netdev is %NULL | ||||
|   * @is_running: true if this is a non-netdev device that has been started, e.g. | ||||
|   *	the P2P Device. | ||||
| - * @cac_started: true if DFS channel availability check has been started
 | ||||
| - * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
 | ||||
| - * @cac_time_ms: CAC time in ms
 | ||||
|   * @ps: powersave mode is enabled | ||||
|   * @ps_timeout: dynamic powersave timeout | ||||
|   * @ap_unexpected_nlportid: (private) netlink port ID of application | ||||
| @@ -6224,6 +6221,11 @@ enum ieee80211_ap_reg_power {
 | ||||
|   *	unprotected beacon report | ||||
|   * @links: array of %IEEE80211_MLD_MAX_NUM_LINKS elements containing @addr | ||||
|   *	@ap and @client for each link | ||||
| + * @links[].cac_started: true if DFS channel availability check has been
 | ||||
| + *	started
 | ||||
| + * @links[].cac_start_time: timestamp (jiffies) when the dfs state was
 | ||||
| + *	entered.
 | ||||
| + * @links[].cac_time_ms: CAC time in ms
 | ||||
|   * @valid_links: bitmap describing what elements of @links are valid | ||||
|   */ | ||||
|  struct wireless_dev { | ||||
| @@ -6265,11 +6267,6 @@ struct wireless_dev {
 | ||||
|  	u32 owner_nlportid; | ||||
|  	bool nl_owner_dead; | ||||
|   | ||||
| -	/* FIXME: need to rework radar detection for MLO */
 | ||||
| -	bool cac_started;
 | ||||
| -	unsigned long cac_start_time;
 | ||||
| -	unsigned int cac_time_ms;
 | ||||
| -
 | ||||
|  #ifdef CPTCFG_CFG80211_WEXT | ||||
|  	/* wext data */ | ||||
|  	struct { | ||||
| @@ -6336,6 +6333,10 @@ struct wireless_dev {
 | ||||
|  				struct cfg80211_internal_bss *current_bss; | ||||
|  			} client; | ||||
|  		}; | ||||
| +
 | ||||
| +		bool cac_started;
 | ||||
| +		unsigned long cac_start_time;
 | ||||
| +		unsigned int cac_time_ms;
 | ||||
|  	} links[IEEE80211_MLD_MAX_NUM_LINKS]; | ||||
|  	u16 valid_links; | ||||
|  }; | ||||
| --- a/net/mac80211/cfg.c
 | ||||
| +++ b/net/mac80211/cfg.c
 | ||||
| @@ -1656,7 +1656,7 @@ static int ieee80211_stop_ap(struct wiph
 | ||||
|  	ieee80211_link_info_change_notify(sdata, link, | ||||
|  					  BSS_CHANGED_BEACON_ENABLED); | ||||
|   | ||||
| -	if (sdata->wdev.cac_started) {
 | ||||
| +	if (sdata->wdev.links[0].cac_started) {
 | ||||
|  		chandef = link_conf->chanreq.oper; | ||||
|  		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work); | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
| @@ -3498,9 +3498,9 @@ static void ieee80211_end_cac(struct wip
 | ||||
|  		wiphy_delayed_work_cancel(wiphy, | ||||
|  					  &sdata->deflink.dfs_cac_timer_work); | ||||
|   | ||||
| -		if (sdata->wdev.cac_started) {
 | ||||
| +		if (sdata->wdev.links[0].cac_started) {
 | ||||
|  			ieee80211_link_release_channel(&sdata->deflink); | ||||
| -			sdata->wdev.cac_started = false;
 | ||||
| +			sdata->wdev.links[0].cac_started = false;
 | ||||
|  		} | ||||
|  	} | ||||
|  } | ||||
| @@ -3955,7 +3955,7 @@ __ieee80211_channel_switch(struct wiphy
 | ||||
|  	if (!list_empty(&local->roc_list) || local->scanning) | ||||
|  		return -EBUSY; | ||||
|   | ||||
| -	if (sdata->wdev.cac_started)
 | ||||
| +	if (sdata->wdev.links[0].cac_started)
 | ||||
|  		return -EBUSY; | ||||
|   | ||||
|  	if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS)) | ||||
| --- a/net/mac80211/iface.c
 | ||||
| +++ b/net/mac80211/iface.c
 | ||||
| @@ -553,7 +553,7 @@ static void ieee80211_do_stop(struct iee
 | ||||
|  	wiphy_delayed_work_cancel(local->hw.wiphy, | ||||
|  				  &sdata->deflink.dfs_cac_timer_work); | ||||
|   | ||||
| -	if (sdata->wdev.cac_started) {
 | ||||
| +	if (sdata->wdev.links[0].cac_started) {
 | ||||
|  		chandef = sdata->vif.bss_conf.chanreq.oper; | ||||
|  		WARN_ON(local->suspended); | ||||
|  		ieee80211_link_release_channel(&sdata->deflink); | ||||
| --- a/net/mac80211/mlme.c
 | ||||
| +++ b/net/mac80211/mlme.c
 | ||||
| @@ -3039,7 +3039,7 @@ void ieee80211_dfs_cac_timer_work(struct
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(sdata->local->hw.wiphy); | ||||
|   | ||||
| -	if (sdata->wdev.cac_started) {
 | ||||
| +	if (sdata->wdev.links[0].cac_started) {
 | ||||
|  		ieee80211_link_release_channel(link); | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_FINISHED, | ||||
| --- a/net/mac80211/scan.c
 | ||||
| +++ b/net/mac80211/scan.c
 | ||||
| @@ -585,7 +585,7 @@ static bool __ieee80211_can_leave_ch(str
 | ||||
|  		return false; | ||||
|   | ||||
|  	list_for_each_entry(sdata_iter, &local->interfaces, list) { | ||||
| -		if (sdata_iter->wdev.cac_started)
 | ||||
| +		if (sdata_iter->wdev.links[0].cac_started)
 | ||||
|  			return false; | ||||
|  	} | ||||
|   | ||||
| --- a/net/mac80211/util.c
 | ||||
| +++ b/net/mac80211/util.c
 | ||||
| @@ -3462,7 +3462,7 @@ void ieee80211_dfs_cac_cancel(struct iee
 | ||||
|  		wiphy_delayed_work_cancel(local->hw.wiphy, | ||||
|  					  &sdata->deflink.dfs_cac_timer_work); | ||||
|   | ||||
| -		if (sdata->wdev.cac_started) {
 | ||||
| +		if (sdata->wdev.links[0].cac_started) {
 | ||||
|  			chandef = sdata->vif.bss_conf.chanreq.oper; | ||||
|  			ieee80211_link_release_channel(&sdata->deflink); | ||||
|  			cfg80211_cac_event(sdata->dev, | ||||
| --- a/net/wireless/ibss.c
 | ||||
| +++ b/net/wireless/ibss.c
 | ||||
| @@ -94,7 +94,7 @@ int __cfg80211_join_ibss(struct cfg80211
 | ||||
|   | ||||
|  	lockdep_assert_held(&rdev->wiphy.mtx); | ||||
|   | ||||
| -	if (wdev->cac_started)
 | ||||
| +	if (wdev->links[0].cac_started)
 | ||||
|  		return -EBUSY; | ||||
|   | ||||
|  	if (wdev->u.ibss.ssid_len) | ||||
| --- a/net/wireless/mesh.c
 | ||||
| +++ b/net/wireless/mesh.c
 | ||||
| @@ -127,7 +127,7 @@ int __cfg80211_join_mesh(struct cfg80211
 | ||||
|  	if (!rdev->ops->join_mesh) | ||||
|  		return -EOPNOTSUPP; | ||||
|   | ||||
| -	if (wdev->cac_started)
 | ||||
| +	if (wdev->links[0].cac_started)
 | ||||
|  		return -EBUSY; | ||||
|   | ||||
|  	if (!setup->chandef.chan) { | ||||
| --- a/net/wireless/mlme.c
 | ||||
| +++ b/net/wireless/mlme.c
 | ||||
| @@ -1124,13 +1124,14 @@ void cfg80211_cac_event(struct net_devic
 | ||||
|   | ||||
|  	trace_cfg80211_cac_event(netdev, event); | ||||
|   | ||||
| -	if (WARN_ON(!wdev->cac_started && event != NL80211_RADAR_CAC_STARTED))
 | ||||
| +	if (WARN_ON(!wdev->links[0].cac_started &&
 | ||||
| +		    event != NL80211_RADAR_CAC_STARTED))
 | ||||
|  		return; | ||||
|   | ||||
|  	switch (event) { | ||||
|  	case NL80211_RADAR_CAC_FINISHED: | ||||
| -		timeout = wdev->cac_start_time +
 | ||||
| -			  msecs_to_jiffies(wdev->cac_time_ms);
 | ||||
| +		timeout = wdev->links[0].cac_start_time +
 | ||||
| +			  msecs_to_jiffies(wdev->links[0].cac_time_ms);
 | ||||
|  		WARN_ON(!time_after_eq(jiffies, timeout)); | ||||
|  		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); | ||||
|  		memcpy(&rdev->cac_done_chandef, chandef, | ||||
| @@ -1139,10 +1140,10 @@ void cfg80211_cac_event(struct net_devic
 | ||||
|  		cfg80211_sched_dfs_chan_update(rdev); | ||||
|  		fallthrough; | ||||
|  	case NL80211_RADAR_CAC_ABORTED: | ||||
| -		wdev->cac_started = false;
 | ||||
| +		wdev->links[0].cac_started = false;
 | ||||
|  		break; | ||||
|  	case NL80211_RADAR_CAC_STARTED: | ||||
| -		wdev->cac_started = true;
 | ||||
| +		wdev->links[0].cac_started = true;
 | ||||
|  		break; | ||||
|  	default: | ||||
|  		WARN_ON(1); | ||||
| --- a/net/wireless/nl80211.c
 | ||||
| +++ b/net/wireless/nl80211.c
 | ||||
| @@ -6066,7 +6066,7 @@ static int nl80211_start_ap(struct sk_bu
 | ||||
|  	if (!rdev->ops->start_ap) | ||||
|  		return -EOPNOTSUPP; | ||||
|   | ||||
| -	if (wdev->cac_started)
 | ||||
| +	if (wdev->links[0].cac_started)
 | ||||
|  		return -EBUSY; | ||||
|   | ||||
|  	if (wdev->links[link_id].ap.beacon_interval) | ||||
| @@ -10122,7 +10122,7 @@ static int nl80211_start_radar_detection
 | ||||
|  		goto unlock; | ||||
|  	} | ||||
|   | ||||
| -	if (cfg80211_beaconing_iface_active(wdev) || wdev->cac_started) {
 | ||||
| +	if (cfg80211_beaconing_iface_active(wdev) || wdev->links[0].cac_started) {
 | ||||
|  		err = -EBUSY; | ||||
|  		goto unlock; | ||||
|  	} | ||||
| @@ -10158,9 +10158,9 @@ static int nl80211_start_radar_detection
 | ||||
|  		default: | ||||
|  			break; | ||||
|  		} | ||||
| -		wdev->cac_started = true;
 | ||||
| -		wdev->cac_start_time = jiffies;
 | ||||
| -		wdev->cac_time_ms = cac_time_ms;
 | ||||
| +		wdev->links[0].cac_started = true;
 | ||||
| +		wdev->links[0].cac_start_time = jiffies;
 | ||||
| +		wdev->links[0].cac_time_ms = cac_time_ms;
 | ||||
|  	} | ||||
|  unlock: | ||||
|  	wiphy_unlock(wiphy); | ||||
| --- a/net/wireless/reg.c
 | ||||
| +++ b/net/wireless/reg.c
 | ||||
| @@ -4241,7 +4241,7 @@ static void cfg80211_check_and_end_cac(s
 | ||||
|  	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | ||||
|  		struct cfg80211_chan_def *chandef; | ||||
|   | ||||
| -		if (!wdev->cac_started)
 | ||||
| +		if (!wdev->links[0].cac_started)
 | ||||
|  			continue; | ||||
|   | ||||
|  		/* FIXME: radar detection is tied to link 0 for now */ | ||||
| @ -0,0 +1,435 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:23 +0530 | ||||
| Subject: [PATCH] wifi: cfg80211: handle DFS per link | ||||
| 
 | ||||
| Currently, during starting a radar detection, no link id information is | ||||
| parsed and passed down. In order to support starting radar detection | ||||
| during Multi Link Operation, it is required to pass link id as well. | ||||
| 
 | ||||
| Add changes to first parse and then pass link id in the start radar | ||||
| detection path. | ||||
| 
 | ||||
| Additionally, update notification APIs to allow drivers/mac80211 to | ||||
| pass the link ID. | ||||
| 
 | ||||
| However, everything is handled at link 0 only until all API's are ready to | ||||
| handle it per link. | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-6-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/wireless/marvell/mwifiex/11h.c
 | ||||
| +++ b/drivers/net/wireless/marvell/mwifiex/11h.c
 | ||||
| @@ -122,7 +122,7 @@ void mwifiex_dfs_cac_work_queue(struct w
 | ||||
|  			    "CAC timer finished; No radar detected\n"); | ||||
|  		cfg80211_cac_event(priv->netdev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_FINISHED, | ||||
| -				   GFP_KERNEL);
 | ||||
| +				   GFP_KERNEL, 0);
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -182,7 +182,8 @@ void mwifiex_abort_cac(struct mwifiex_pr
 | ||||
|  			    "Aborting delayed work for CAC.\n"); | ||||
|  		cancel_delayed_work_sync(&priv->dfs_cac_work); | ||||
|  		cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, | ||||
| -				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
 | ||||
| +				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL,
 | ||||
| +				   0);
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -221,7 +222,7 @@ int mwifiex_11h_handle_chanrpt_ready(str
 | ||||
|  				cfg80211_cac_event(priv->netdev, | ||||
|  						   &priv->dfs_chandef, | ||||
|  						   NL80211_RADAR_DETECTED, | ||||
| -						   GFP_KERNEL);
 | ||||
| +						   GFP_KERNEL, 0);
 | ||||
|  			} | ||||
|  			break; | ||||
|  		default: | ||||
| --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
 | ||||
| +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
 | ||||
| @@ -4145,7 +4145,7 @@ static int
 | ||||
|  mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, | ||||
|  				       struct net_device *dev, | ||||
|  				       struct cfg80211_chan_def *chandef, | ||||
| -				       u32 cac_time_ms)
 | ||||
| +				       u32 cac_time_ms, int link_id)
 | ||||
|  { | ||||
|  	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||||
|  	struct mwifiex_radar_params radar_params; | ||||
| --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
 | ||||
| +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
 | ||||
| @@ -837,7 +837,7 @@ static int qtnf_channel_switch(struct wi
 | ||||
|  static int qtnf_start_radar_detection(struct wiphy *wiphy, | ||||
|  				      struct net_device *ndev, | ||||
|  				      struct cfg80211_chan_def *chandef, | ||||
| -				      u32 cac_time_ms)
 | ||||
| +				      u32 cac_time_ms, int link_id)
 | ||||
|  { | ||||
|  	struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); | ||||
|  	int ret; | ||||
| --- a/drivers/net/wireless/quantenna/qtnfmac/event.c
 | ||||
| +++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
 | ||||
| @@ -524,14 +524,14 @@ static int qtnf_event_handle_radar(struc
 | ||||
|  			break; | ||||
|   | ||||
|  		cfg80211_cac_event(vif->netdev, &chandef, | ||||
| -				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
 | ||||
| +				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, 0);
 | ||||
|  		break; | ||||
|  	case QLINK_RADAR_CAC_ABORTED: | ||||
|  		if (!vif->wdev.links[0].cac_started) | ||||
|  			break; | ||||
|   | ||||
|  		cfg80211_cac_event(vif->netdev, &chandef, | ||||
| -				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
 | ||||
| +				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0);
 | ||||
|  		break; | ||||
|  	case QLINK_RADAR_CAC_STARTED: | ||||
|  		if (vif->wdev.links[0].cac_started) | ||||
| @@ -542,7 +542,7 @@ static int qtnf_event_handle_radar(struc
 | ||||
|  			break; | ||||
|   | ||||
|  		cfg80211_cac_event(vif->netdev, &chandef, | ||||
| -				   NL80211_RADAR_CAC_STARTED, GFP_KERNEL);
 | ||||
| +				   NL80211_RADAR_CAC_STARTED, GFP_KERNEL, 0);
 | ||||
|  		break; | ||||
|  	default: | ||||
|  		pr_warn("%s: unhandled radar event %u\n", | ||||
| --- a/include/net/cfg80211.h
 | ||||
| +++ b/include/net/cfg80211.h
 | ||||
| @@ -4841,9 +4841,9 @@ struct cfg80211_ops {
 | ||||
|  	int	(*start_radar_detection)(struct wiphy *wiphy, | ||||
|  					 struct net_device *dev, | ||||
|  					 struct cfg80211_chan_def *chandef, | ||||
| -					 u32 cac_time_ms);
 | ||||
| +					 u32 cac_time_ms, int link_id);
 | ||||
|  	void	(*end_cac)(struct wiphy *wiphy, | ||||
| -				struct net_device *dev);
 | ||||
| +			   struct net_device *dev, unsigned int link_id);
 | ||||
|  	int	(*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev, | ||||
|  				 struct cfg80211_update_ft_ies_params *ftie); | ||||
|  	int	(*crit_proto_start)(struct wiphy *wiphy, | ||||
| @@ -8745,6 +8745,7 @@ void cfg80211_sta_opmode_change_notify(s
 | ||||
|   * @chandef: chandef for the current channel | ||||
|   * @event: type of event | ||||
|   * @gfp: context flags | ||||
| + * @link_id: valid link_id for MLO operation or 0 otherwise.
 | ||||
|   * | ||||
|   * This function is called when a Channel availability check (CAC) is finished | ||||
|   * or aborted. This must be called to notify the completion of a CAC process, | ||||
| @@ -8752,7 +8753,8 @@ void cfg80211_sta_opmode_change_notify(s
 | ||||
|   */ | ||||
|  void cfg80211_cac_event(struct net_device *netdev, | ||||
|  			const struct cfg80211_chan_def *chandef, | ||||
| -			enum nl80211_radar_event event, gfp_t gfp);
 | ||||
| +			enum nl80211_radar_event event, gfp_t gfp,
 | ||||
| +			unsigned int link_id);
 | ||||
|   | ||||
|  /** | ||||
|   * cfg80211_background_cac_abort - Channel Availability Check offchan abort event | ||||
| --- a/net/mac80211/cfg.c
 | ||||
| +++ b/net/mac80211/cfg.c
 | ||||
| @@ -1661,7 +1661,7 @@ static int ieee80211_stop_ap(struct wiph
 | ||||
|  		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work); | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_ABORTED, | ||||
| -				   GFP_KERNEL);
 | ||||
| +				   GFP_KERNEL, 0);
 | ||||
|  	} | ||||
|   | ||||
|  	drv_stop_ap(sdata->local, sdata, link_conf); | ||||
| @@ -3459,7 +3459,7 @@ static int ieee80211_set_bitrate_mask(st
 | ||||
|  static int ieee80211_start_radar_detection(struct wiphy *wiphy, | ||||
|  					   struct net_device *dev, | ||||
|  					   struct cfg80211_chan_def *chandef, | ||||
| -					   u32 cac_time_ms)
 | ||||
| +					   u32 cac_time_ms, int link_id)
 | ||||
|  { | ||||
|  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||||
|  	struct ieee80211_chan_req chanreq = { .oper = *chandef }; | ||||
| @@ -3487,7 +3487,7 @@ static int ieee80211_start_radar_detecti
 | ||||
|  } | ||||
|   | ||||
|  static void ieee80211_end_cac(struct wiphy *wiphy, | ||||
| -			      struct net_device *dev)
 | ||||
| +			      struct net_device *dev, unsigned int link_id)
 | ||||
|  { | ||||
|  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
| --- a/net/mac80211/iface.c
 | ||||
| +++ b/net/mac80211/iface.c
 | ||||
| @@ -559,7 +559,7 @@ static void ieee80211_do_stop(struct iee
 | ||||
|  		ieee80211_link_release_channel(&sdata->deflink); | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_ABORTED, | ||||
| -				   GFP_KERNEL);
 | ||||
| +				   GFP_KERNEL, 0);
 | ||||
|  	} | ||||
|   | ||||
|  	if (sdata->vif.type == NL80211_IFTYPE_AP) { | ||||
| --- a/net/mac80211/mlme.c
 | ||||
| +++ b/net/mac80211/mlme.c
 | ||||
| @@ -3043,7 +3043,7 @@ void ieee80211_dfs_cac_timer_work(struct
 | ||||
|  		ieee80211_link_release_channel(link); | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_FINISHED, | ||||
| -				   GFP_KERNEL);
 | ||||
| +				   GFP_KERNEL, 0);
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| --- a/net/mac80211/util.c
 | ||||
| +++ b/net/mac80211/util.c
 | ||||
| @@ -3468,7 +3468,7 @@ void ieee80211_dfs_cac_cancel(struct iee
 | ||||
|  			cfg80211_cac_event(sdata->dev, | ||||
|  					   &chandef, | ||||
|  					   NL80211_RADAR_CAC_ABORTED, | ||||
| -					   GFP_KERNEL);
 | ||||
| +					   GFP_KERNEL, 0);
 | ||||
|  		} | ||||
|  	} | ||||
|  } | ||||
| --- a/net/wireless/mlme.c
 | ||||
| +++ b/net/wireless/mlme.c
 | ||||
| @@ -1111,18 +1111,19 @@ EXPORT_SYMBOL(__cfg80211_radar_event);
 | ||||
|   | ||||
|  void cfg80211_cac_event(struct net_device *netdev, | ||||
|  			const struct cfg80211_chan_def *chandef, | ||||
| -			enum nl80211_radar_event event, gfp_t gfp)
 | ||||
| +			enum nl80211_radar_event event, gfp_t gfp,
 | ||||
| +			unsigned int link_id)
 | ||||
|  { | ||||
|  	struct wireless_dev *wdev = netdev->ieee80211_ptr; | ||||
|  	struct wiphy *wiphy = wdev->wiphy; | ||||
|  	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||||
|  	unsigned long timeout; | ||||
|   | ||||
| -	/* not yet supported */
 | ||||
| -	if (wdev->valid_links)
 | ||||
| +	if (WARN_ON(wdev->valid_links &&
 | ||||
| +		    !(wdev->valid_links & BIT(link_id))))
 | ||||
|  		return; | ||||
|   | ||||
| -	trace_cfg80211_cac_event(netdev, event);
 | ||||
| +	trace_cfg80211_cac_event(netdev, event, link_id);
 | ||||
|   | ||||
|  	if (WARN_ON(!wdev->links[0].cac_started && | ||||
|  		    event != NL80211_RADAR_CAC_STARTED)) | ||||
| --- a/net/wireless/nl80211.c
 | ||||
| +++ b/net/wireless/nl80211.c
 | ||||
| @@ -10122,7 +10122,20 @@ static int nl80211_start_radar_detection
 | ||||
|  		goto unlock; | ||||
|  	} | ||||
|   | ||||
| -	if (cfg80211_beaconing_iface_active(wdev) || wdev->links[0].cac_started) {
 | ||||
| +	if (cfg80211_beaconing_iface_active(wdev)) {
 | ||||
| +		/* During MLO other link(s) can beacon, only the current link
 | ||||
| +		 * can not already beacon
 | ||||
| +		 */
 | ||||
| +		if (wdev->valid_links &&
 | ||||
| +		    !wdev->links[0].ap.beacon_interval) {
 | ||||
| +			/* nothing */
 | ||||
| +		} else {
 | ||||
| +			err = -EBUSY;
 | ||||
| +			goto unlock;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (wdev->links[0].cac_started) {
 | ||||
|  		err = -EBUSY; | ||||
|  		goto unlock; | ||||
|  	} | ||||
| @@ -10142,7 +10155,8 @@ static int nl80211_start_radar_detection
 | ||||
|  	if (WARN_ON(!cac_time_ms)) | ||||
|  		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; | ||||
|   | ||||
| -	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
 | ||||
| +	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms,
 | ||||
| +					 0);
 | ||||
|  	if (!err) { | ||||
|  		switch (wdev->iftype) { | ||||
|  		case NL80211_IFTYPE_AP: | ||||
| @@ -16512,10 +16526,10 @@ nl80211_set_ttlm(struct sk_buff *skb, st
 | ||||
|  	SELECTOR(__sel, NETDEV_UP_NOTMX,		\ | ||||
|  		 NL80211_FLAG_NEED_NETDEV_UP |		\ | ||||
|  		 NL80211_FLAG_NO_WIPHY_MTX)		\ | ||||
| -	SELECTOR(__sel, NETDEV_UP_NOTMX_NOMLO,		\
 | ||||
| +	SELECTOR(__sel, NETDEV_UP_NOTMX_MLO,		\
 | ||||
|  		 NL80211_FLAG_NEED_NETDEV_UP |		\ | ||||
|  		 NL80211_FLAG_NO_WIPHY_MTX |		\ | ||||
| -		 NL80211_FLAG_MLO_UNSUPPORTED)		\
 | ||||
| +		 NL80211_FLAG_MLO_VALID_LINK_ID)	\
 | ||||
|  	SELECTOR(__sel, NETDEV_UP_CLEAR,		\ | ||||
|  		 NL80211_FLAG_NEED_NETDEV_UP |		\ | ||||
|  		 NL80211_FLAG_CLEAR_SKB)		\ | ||||
| @@ -17410,7 +17424,7 @@ static const struct genl_small_ops nl802
 | ||||
|  		.flags = GENL_UNS_ADMIN_PERM, | ||||
|  		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | ||||
|  					 NL80211_FLAG_NO_WIPHY_MTX | | ||||
| -					 NL80211_FLAG_MLO_UNSUPPORTED),
 | ||||
| +					 NL80211_FLAG_MLO_VALID_LINK_ID),
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES, | ||||
| --- a/net/wireless/rdev-ops.h
 | ||||
| +++ b/net/wireless/rdev-ops.h
 | ||||
| @@ -1200,26 +1200,27 @@ static inline int
 | ||||
|  rdev_start_radar_detection(struct cfg80211_registered_device *rdev, | ||||
|  			   struct net_device *dev, | ||||
|  			   struct cfg80211_chan_def *chandef, | ||||
| -			   u32 cac_time_ms)
 | ||||
| +			   u32 cac_time_ms, int link_id)
 | ||||
|  { | ||||
|  	int ret = -EOPNOTSUPP; | ||||
|   | ||||
|  	trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef, | ||||
| -					 cac_time_ms);
 | ||||
| +					 cac_time_ms, link_id);
 | ||||
|  	if (rdev->ops->start_radar_detection) | ||||
|  		ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev, | ||||
| -						       chandef, cac_time_ms);
 | ||||
| +						       chandef, cac_time_ms,
 | ||||
| +						       link_id);
 | ||||
|  	trace_rdev_return_int(&rdev->wiphy, ret); | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
|  static inline void | ||||
|  rdev_end_cac(struct cfg80211_registered_device *rdev, | ||||
| -	     struct net_device *dev)
 | ||||
| +	     struct net_device *dev, unsigned int link_id)
 | ||||
|  { | ||||
| -	trace_rdev_end_cac(&rdev->wiphy, dev);
 | ||||
| +	trace_rdev_end_cac(&rdev->wiphy, dev, link_id);
 | ||||
|  	if (rdev->ops->end_cac) | ||||
| -		rdev->ops->end_cac(&rdev->wiphy, dev);
 | ||||
| +		rdev->ops->end_cac(&rdev->wiphy, dev, link_id);
 | ||||
|  	trace_rdev_return_void(&rdev->wiphy); | ||||
|  } | ||||
|   | ||||
| --- a/net/wireless/reg.c
 | ||||
| +++ b/net/wireless/reg.c
 | ||||
| @@ -4229,6 +4229,8 @@ EXPORT_SYMBOL(regulatory_pre_cac_allowed
 | ||||
|  static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev) | ||||
|  { | ||||
|  	struct wireless_dev *wdev; | ||||
| +	unsigned int link_id;
 | ||||
| +
 | ||||
|  	/* If we finished CAC or received radar, we should end any | ||||
|  	 * CAC running on the same channels. | ||||
|  	 * the check !cfg80211_chandef_dfs_usable contain 2 options: | ||||
| @@ -4241,16 +4243,17 @@ static void cfg80211_check_and_end_cac(s
 | ||||
|  	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | ||||
|  		struct cfg80211_chan_def *chandef; | ||||
|   | ||||
| -		if (!wdev->links[0].cac_started)
 | ||||
| -			continue;
 | ||||
| +		for_each_valid_link(wdev, link_id) {
 | ||||
| +			if (!wdev->links[link_id].cac_started)
 | ||||
| +				continue;
 | ||||
|   | ||||
| -		/* FIXME: radar detection is tied to link 0 for now */
 | ||||
| -		chandef = wdev_chandef(wdev, 0);
 | ||||
| -		if (!chandef)
 | ||||
| -			continue;
 | ||||
| +			chandef = wdev_chandef(wdev, link_id);
 | ||||
| +			if (!chandef)
 | ||||
| +				continue;
 | ||||
|   | ||||
| -		if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
 | ||||
| -			rdev_end_cac(rdev, wdev->netdev);
 | ||||
| +			if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
 | ||||
| +				rdev_end_cac(rdev, wdev->netdev, link_id);
 | ||||
| +		}
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| --- a/net/wireless/trace.h
 | ||||
| +++ b/net/wireless/trace.h
 | ||||
| @@ -806,17 +806,21 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flus
 | ||||
|  ); | ||||
|   | ||||
|  TRACE_EVENT(rdev_end_cac, | ||||
| -	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
 | ||||
| -	TP_ARGS(wiphy, netdev),
 | ||||
| +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
 | ||||
| +		 unsigned int link_id),
 | ||||
| +	TP_ARGS(wiphy, netdev, link_id),
 | ||||
|  	TP_STRUCT__entry( | ||||
|  		WIPHY_ENTRY | ||||
|  		NETDEV_ENTRY | ||||
| +		__field(unsigned int, link_id)
 | ||||
|  	), | ||||
|  	TP_fast_assign( | ||||
|  		WIPHY_ASSIGN; | ||||
|  		NETDEV_ASSIGN; | ||||
| +		__entry->link_id = link_id;
 | ||||
|  	), | ||||
| -	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
 | ||||
| +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d",
 | ||||
| +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id)
 | ||||
|  ); | ||||
|   | ||||
|  DECLARE_EVENT_CLASS(station_add_change, | ||||
| @@ -2661,24 +2665,26 @@ TRACE_EVENT(rdev_external_auth,
 | ||||
|  TRACE_EVENT(rdev_start_radar_detection, | ||||
|  	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, | ||||
|  		 struct cfg80211_chan_def *chandef, | ||||
| -		 u32 cac_time_ms),
 | ||||
| -	TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
 | ||||
| +		 u32 cac_time_ms, int link_id),
 | ||||
| +	TP_ARGS(wiphy, netdev, chandef, cac_time_ms, link_id),
 | ||||
|  	TP_STRUCT__entry( | ||||
|  		WIPHY_ENTRY | ||||
|  		NETDEV_ENTRY | ||||
|  		CHAN_DEF_ENTRY | ||||
|  		__field(u32, cac_time_ms) | ||||
| +		__field(int, link_id)
 | ||||
|  	), | ||||
|  	TP_fast_assign( | ||||
|  		WIPHY_ASSIGN; | ||||
|  		NETDEV_ASSIGN; | ||||
|  		CHAN_DEF_ASSIGN(chandef); | ||||
|  		__entry->cac_time_ms = cac_time_ms; | ||||
| +		__entry->link_id = link_id;
 | ||||
|  	), | ||||
|  	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT | ||||
| -		  ", cac_time_ms=%u",
 | ||||
| +		  ", cac_time_ms=%u, link_id=%d",
 | ||||
|  		  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG, | ||||
| -		  __entry->cac_time_ms)
 | ||||
| +		  __entry->cac_time_ms, __entry->link_id)
 | ||||
|  ); | ||||
|   | ||||
|  TRACE_EVENT(rdev_set_mcast_rate, | ||||
| @@ -3492,18 +3498,21 @@ TRACE_EVENT(cfg80211_radar_event,
 | ||||
|  ); | ||||
|   | ||||
|  TRACE_EVENT(cfg80211_cac_event, | ||||
| -	TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt),
 | ||||
| -	TP_ARGS(netdev, evt),
 | ||||
| +	TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt,
 | ||||
| +		 unsigned int link_id),
 | ||||
| +	TP_ARGS(netdev, evt, link_id),
 | ||||
|  	TP_STRUCT__entry( | ||||
|  		NETDEV_ENTRY | ||||
|  		__field(enum nl80211_radar_event, evt) | ||||
| +		__field(unsigned int, link_id)
 | ||||
|  	), | ||||
|  	TP_fast_assign( | ||||
|  		NETDEV_ASSIGN; | ||||
|  		__entry->evt = evt; | ||||
| +		__entry->link_id = link_id;
 | ||||
|  	), | ||||
| -	TP_printk(NETDEV_PR_FMT ",  event: %d",
 | ||||
| -		  NETDEV_PR_ARG, __entry->evt)
 | ||||
| +	TP_printk(NETDEV_PR_FMT ",  event: %d, link_id=%u",
 | ||||
| +		  NETDEV_PR_ARG, __entry->evt, __entry->link_id)
 | ||||
|  ); | ||||
|   | ||||
|  DECLARE_EVENT_CLASS(cfg80211_rx_evt, | ||||
| @ -0,0 +1,134 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:24 +0530 | ||||
| Subject: [PATCH] wifi: mac80211: handle DFS per link | ||||
| 
 | ||||
| In order to support DFS with MLO, handle the link ID now passed from | ||||
| cfg80211, adjust the code to do everything per link and call the | ||||
| notifications to cfg80211 correctly. | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-7-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/net/mac80211/cfg.c
 | ||||
| +++ b/net/mac80211/cfg.c
 | ||||
| @@ -3464,6 +3464,7 @@ static int ieee80211_start_radar_detecti
 | ||||
|  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||||
|  	struct ieee80211_chan_req chanreq = { .oper = *chandef }; | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
| +	struct ieee80211_link_data *link_data;
 | ||||
|  	int err; | ||||
|   | ||||
|  	lockdep_assert_wiphy(local->hw.wiphy); | ||||
| @@ -3471,16 +3472,20 @@ static int ieee80211_start_radar_detecti
 | ||||
|  	if (!list_empty(&local->roc_list) || local->scanning) | ||||
|  		return -EBUSY; | ||||
|   | ||||
| +	link_data = sdata_dereference(sdata->link[link_id], sdata);
 | ||||
| +	if (!link_data)
 | ||||
| +		return -ENOLINK;
 | ||||
| +
 | ||||
|  	/* whatever, but channel contexts should not complain about that one */ | ||||
| -	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
 | ||||
| -	sdata->deflink.needed_rx_chains = local->rx_chains;
 | ||||
| +	link_data->smps_mode = IEEE80211_SMPS_OFF;
 | ||||
| +	link_data->needed_rx_chains = local->rx_chains;
 | ||||
|   | ||||
| -	err = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
 | ||||
| +	err = ieee80211_link_use_channel(link_data, &chanreq,
 | ||||
|  					 IEEE80211_CHANCTX_SHARED); | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| -	wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
 | ||||
| +	wiphy_delayed_work_queue(wiphy, &link_data->dfs_cac_timer_work,
 | ||||
|  				 msecs_to_jiffies(cac_time_ms)); | ||||
|   | ||||
|  	return 0; | ||||
| @@ -3491,16 +3496,21 @@ static void ieee80211_end_cac(struct wip
 | ||||
|  { | ||||
|  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
| +	struct ieee80211_link_data *link_data;
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(local->hw.wiphy); | ||||
|   | ||||
|  	list_for_each_entry(sdata, &local->interfaces, list) { | ||||
| +		link_data = sdata_dereference(sdata->link[link_id], sdata);
 | ||||
| +		if (!link_data)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
|  		wiphy_delayed_work_cancel(wiphy, | ||||
| -					  &sdata->deflink.dfs_cac_timer_work);
 | ||||
| +					  &link_data->dfs_cac_timer_work);
 | ||||
|   | ||||
| -		if (sdata->wdev.links[0].cac_started) {
 | ||||
| -			ieee80211_link_release_channel(&sdata->deflink);
 | ||||
| -			sdata->wdev.links[0].cac_started = false;
 | ||||
| +		if (sdata->wdev.links[link_id].cac_started) {
 | ||||
| +			ieee80211_link_release_channel(link_data);
 | ||||
| +			sdata->wdev.links[link_id].cac_started = false;
 | ||||
|  		} | ||||
|  	} | ||||
|  } | ||||
| --- a/net/mac80211/link.c
 | ||||
| +++ b/net/mac80211/link.c
 | ||||
| @@ -77,6 +77,16 @@ void ieee80211_link_stop(struct ieee8021
 | ||||
|  			  &link->color_change_finalize_work); | ||||
|  	wiphy_work_cancel(link->sdata->local->hw.wiphy, | ||||
|  			  &link->csa.finalize_work); | ||||
| +
 | ||||
| +	if (link->sdata->wdev.links[link->link_id].cac_started) {
 | ||||
| +		wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy,
 | ||||
| +					  &link->dfs_cac_timer_work);
 | ||||
| +		cfg80211_cac_event(link->sdata->dev,
 | ||||
| +				   &link->conf->chanreq.oper,
 | ||||
| +				   NL80211_RADAR_CAC_ABORTED,
 | ||||
| +				   GFP_KERNEL, link->link_id);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	ieee80211_link_release_channel(link); | ||||
|  } | ||||
|   | ||||
| --- a/net/mac80211/util.c
 | ||||
| +++ b/net/mac80211/util.c
 | ||||
| @@ -3455,20 +3455,30 @@ void ieee80211_dfs_cac_cancel(struct iee
 | ||||
|  { | ||||
|  	struct ieee80211_sub_if_data *sdata; | ||||
|  	struct cfg80211_chan_def chandef; | ||||
| +	struct ieee80211_link_data *link;
 | ||||
| +	unsigned int link_id;
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(local->hw.wiphy); | ||||
|   | ||||
|  	list_for_each_entry(sdata, &local->interfaces, list) { | ||||
| -		wiphy_delayed_work_cancel(local->hw.wiphy,
 | ||||
| -					  &sdata->deflink.dfs_cac_timer_work);
 | ||||
| +		for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS;
 | ||||
| +		     link_id++) {
 | ||||
| +			link = sdata_dereference(sdata->link[link_id],
 | ||||
| +						 sdata);
 | ||||
| +			if (!link)
 | ||||
| +				continue;
 | ||||
|   | ||||
| -		if (sdata->wdev.links[0].cac_started) {
 | ||||
| -			chandef = sdata->vif.bss_conf.chanreq.oper;
 | ||||
| -			ieee80211_link_release_channel(&sdata->deflink);
 | ||||
| -			cfg80211_cac_event(sdata->dev,
 | ||||
| -					   &chandef,
 | ||||
| +			wiphy_delayed_work_cancel(local->hw.wiphy,
 | ||||
| +						  &link->dfs_cac_timer_work);
 | ||||
| +
 | ||||
| +			if (!sdata->wdev.links[link_id].cac_started)
 | ||||
| +				continue;
 | ||||
| +
 | ||||
| +			chandef = link->conf->chanreq.oper;
 | ||||
| +			ieee80211_link_release_channel(link);
 | ||||
| +			cfg80211_cac_event(sdata->dev, &chandef,
 | ||||
|  					   NL80211_RADAR_CAC_ABORTED, | ||||
| -					   GFP_KERNEL, 0);
 | ||||
| +					   GFP_KERNEL, link_id);
 | ||||
|  		} | ||||
|  	} | ||||
|  } | ||||
| @ -0,0 +1,168 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:25 +0530 | ||||
| Subject: [PATCH] wifi: cfg80211/mac80211: use proper link ID for DFS | ||||
| 
 | ||||
| Now that all APIs have support to handle DFS per link, use proper link ID | ||||
| instead of 0. | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-8-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/net/mac80211/cfg.c
 | ||||
| +++ b/net/mac80211/cfg.c
 | ||||
| @@ -1656,12 +1656,12 @@ static int ieee80211_stop_ap(struct wiph
 | ||||
|  	ieee80211_link_info_change_notify(sdata, link, | ||||
|  					  BSS_CHANGED_BEACON_ENABLED); | ||||
|   | ||||
| -	if (sdata->wdev.links[0].cac_started) {
 | ||||
| +	if (sdata->wdev.links[link_id].cac_started) {
 | ||||
|  		chandef = link_conf->chanreq.oper; | ||||
|  		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work); | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_ABORTED, | ||||
| -				   GFP_KERNEL, 0);
 | ||||
| +				   GFP_KERNEL, link_id);
 | ||||
|  	} | ||||
|   | ||||
|  	drv_stop_ap(sdata->local, sdata, link_conf); | ||||
| @@ -3965,7 +3965,7 @@ __ieee80211_channel_switch(struct wiphy
 | ||||
|  	if (!list_empty(&local->roc_list) || local->scanning) | ||||
|  		return -EBUSY; | ||||
|   | ||||
| -	if (sdata->wdev.links[0].cac_started)
 | ||||
| +	if (sdata->wdev.links[link_id].cac_started)
 | ||||
|  		return -EBUSY; | ||||
|   | ||||
|  	if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS)) | ||||
| --- a/net/mac80211/mlme.c
 | ||||
| +++ b/net/mac80211/mlme.c
 | ||||
| @@ -3039,11 +3039,11 @@ void ieee80211_dfs_cac_timer_work(struct
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(sdata->local->hw.wiphy); | ||||
|   | ||||
| -	if (sdata->wdev.links[0].cac_started) {
 | ||||
| +	if (sdata->wdev.links[link->link_id].cac_started) {
 | ||||
|  		ieee80211_link_release_channel(link); | ||||
|  		cfg80211_cac_event(sdata->dev, &chandef, | ||||
|  				   NL80211_RADAR_CAC_FINISHED, | ||||
| -				   GFP_KERNEL, 0);
 | ||||
| +				   GFP_KERNEL, link->link_id);
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| --- a/net/mac80211/scan.c
 | ||||
| +++ b/net/mac80211/scan.c
 | ||||
| @@ -575,6 +575,7 @@ static bool __ieee80211_can_leave_ch(str
 | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ieee80211_sub_if_data *sdata_iter; | ||||
| +	unsigned int link_id;
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(local->hw.wiphy); | ||||
|   | ||||
| @@ -585,8 +586,9 @@ static bool __ieee80211_can_leave_ch(str
 | ||||
|  		return false; | ||||
|   | ||||
|  	list_for_each_entry(sdata_iter, &local->interfaces, list) { | ||||
| -		if (sdata_iter->wdev.links[0].cac_started)
 | ||||
| -			return false;
 | ||||
| +		for_each_valid_link(&sdata_iter->wdev, link_id)
 | ||||
| +			if (sdata_iter->wdev.links[link_id].cac_started)
 | ||||
| +				return false;
 | ||||
|  	} | ||||
|   | ||||
|  	return true; | ||||
| --- a/net/wireless/mlme.c
 | ||||
| +++ b/net/wireless/mlme.c
 | ||||
| @@ -1125,14 +1125,14 @@ void cfg80211_cac_event(struct net_devic
 | ||||
|   | ||||
|  	trace_cfg80211_cac_event(netdev, event, link_id); | ||||
|   | ||||
| -	if (WARN_ON(!wdev->links[0].cac_started &&
 | ||||
| +	if (WARN_ON(!wdev->links[link_id].cac_started &&
 | ||||
|  		    event != NL80211_RADAR_CAC_STARTED)) | ||||
|  		return; | ||||
|   | ||||
|  	switch (event) { | ||||
|  	case NL80211_RADAR_CAC_FINISHED: | ||||
| -		timeout = wdev->links[0].cac_start_time +
 | ||||
| -			  msecs_to_jiffies(wdev->links[0].cac_time_ms);
 | ||||
| +		timeout = wdev->links[link_id].cac_start_time +
 | ||||
| +			  msecs_to_jiffies(wdev->links[link_id].cac_time_ms);
 | ||||
|  		WARN_ON(!time_after_eq(jiffies, timeout)); | ||||
|  		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); | ||||
|  		memcpy(&rdev->cac_done_chandef, chandef, | ||||
| @@ -1141,10 +1141,10 @@ void cfg80211_cac_event(struct net_devic
 | ||||
|  		cfg80211_sched_dfs_chan_update(rdev); | ||||
|  		fallthrough; | ||||
|  	case NL80211_RADAR_CAC_ABORTED: | ||||
| -		wdev->links[0].cac_started = false;
 | ||||
| +		wdev->links[link_id].cac_started = false;
 | ||||
|  		break; | ||||
|  	case NL80211_RADAR_CAC_STARTED: | ||||
| -		wdev->links[0].cac_started = true;
 | ||||
| +		wdev->links[link_id].cac_started = true;
 | ||||
|  		break; | ||||
|  	default: | ||||
|  		WARN_ON(1); | ||||
| --- a/net/wireless/nl80211.c
 | ||||
| +++ b/net/wireless/nl80211.c
 | ||||
| @@ -6066,7 +6066,7 @@ static int nl80211_start_ap(struct sk_bu
 | ||||
|  	if (!rdev->ops->start_ap) | ||||
|  		return -EOPNOTSUPP; | ||||
|   | ||||
| -	if (wdev->links[0].cac_started)
 | ||||
| +	if (wdev->links[link_id].cac_started)
 | ||||
|  		return -EBUSY; | ||||
|   | ||||
|  	if (wdev->links[link_id].ap.beacon_interval) | ||||
| @@ -10073,6 +10073,7 @@ static int nl80211_start_radar_detection
 | ||||
|  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||||
|  	struct net_device *dev = info->user_ptr[1]; | ||||
|  	struct wireless_dev *wdev = dev->ieee80211_ptr; | ||||
| +	int link_id = nl80211_link_id(info->attrs);
 | ||||
|  	struct wiphy *wiphy = wdev->wiphy; | ||||
|  	struct cfg80211_chan_def chandef; | ||||
|  	enum nl80211_dfs_regions dfs_region; | ||||
| @@ -10127,7 +10128,7 @@ static int nl80211_start_radar_detection
 | ||||
|  		 * can not already beacon | ||||
|  		 */ | ||||
|  		if (wdev->valid_links && | ||||
| -		    !wdev->links[0].ap.beacon_interval) {
 | ||||
| +		    !wdev->links[link_id].ap.beacon_interval) {
 | ||||
|  			/* nothing */ | ||||
|  		} else { | ||||
|  			err = -EBUSY; | ||||
| @@ -10135,7 +10136,7 @@ static int nl80211_start_radar_detection
 | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| -	if (wdev->links[0].cac_started) {
 | ||||
| +	if (wdev->links[link_id].cac_started) {
 | ||||
|  		err = -EBUSY; | ||||
|  		goto unlock; | ||||
|  	} | ||||
| @@ -10156,7 +10157,7 @@ static int nl80211_start_radar_detection
 | ||||
|  		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; | ||||
|   | ||||
|  	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms, | ||||
| -					 0);
 | ||||
| +					 link_id);
 | ||||
|  	if (!err) { | ||||
|  		switch (wdev->iftype) { | ||||
|  		case NL80211_IFTYPE_AP: | ||||
| @@ -10172,9 +10173,9 @@ static int nl80211_start_radar_detection
 | ||||
|  		default: | ||||
|  			break; | ||||
|  		} | ||||
| -		wdev->links[0].cac_started = true;
 | ||||
| -		wdev->links[0].cac_start_time = jiffies;
 | ||||
| -		wdev->links[0].cac_time_ms = cac_time_ms;
 | ||||
| +		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;
 | ||||
|  	} | ||||
|  unlock: | ||||
|  	wiphy_unlock(wiphy); | ||||
| @ -0,0 +1,360 @@ | ||||
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Date: Fri, 6 Sep 2024 12:14:26 +0530 | ||||
| Subject: [PATCH] wifi: mac80211: handle ieee80211_radar_detected() for MLO | ||||
| 
 | ||||
| Currently DFS works under assumption there could be only one channel | ||||
| context in the hardware. Hence, drivers just calls the function | ||||
| ieee80211_radar_detected() passing the hardware structure. However, with | ||||
| MLO, this obviously will not work since number of channel contexts will be | ||||
| more than one and hence drivers would need to pass the channel information | ||||
| as well on which the radar is detected. | ||||
| 
 | ||||
| Also, when radar is detected in one of the links, other link's CAC should | ||||
| not be cancelled. | ||||
| 
 | ||||
| Hence, in order to support DFS with MLO, do the following changes - | ||||
|   * Add channel context conf pointer as an argument to the function | ||||
|     ieee80211_radar_detected(). During MLO, drivers would have to pass on | ||||
|     which channel context conf radar is detected. Otherwise, drivers could | ||||
|     just pass NULL. | ||||
|   * ieee80211_radar_detected() will iterate over all channel contexts | ||||
|     present and | ||||
|   	* if channel context conf is passed, only mark that as radar | ||||
|   	  detected | ||||
|   	* if NULL is passed, then mark all channel contexts as radar | ||||
|   	  detected | ||||
|   	* Then as usual, schedule the radar detected work. | ||||
|   * In the worker, go over all the contexts again and for all such context | ||||
|     which is marked with radar detected, cancel the ongoing CAC by calling | ||||
|     ieee80211_dfs_cac_cancel() and then notify cfg80211 via | ||||
|     cfg80211_radar_event(). | ||||
|   * To cancel the CAC, pass the channel context as well where radar is | ||||
|     detected to ieee80211_dfs_cac_cancel(). This ensures that CAC is | ||||
|     canceled only on the links using the provided context, leaving other | ||||
|     links unaffected. | ||||
| 
 | ||||
| This would also help in scenarios where there is split phy 5 GHz radio, | ||||
| which is capable of DFS channels in both lower and upper band. In this | ||||
| case, simultaneous radars can be detected. | ||||
| 
 | ||||
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> | ||||
| Link: https://patch.msgid.link/20240906064426.2101315-9-quic_adisi@quicinc.com | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/wireless/ath/ath10k/debug.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath10k/debug.c
 | ||||
| @@ -3,7 +3,7 @@
 | ||||
|   * Copyright (c) 2005-2011 Atheros Communications Inc. | ||||
|   * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. | ||||
|   * Copyright (c) 2018, The Linux Foundation. All rights reserved. | ||||
| - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 | ||||
| + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
 | ||||
|   */ | ||||
|   | ||||
|  #include <linux/module.h> | ||||
| @@ -1774,7 +1774,7 @@ static ssize_t ath10k_write_simulate_rad
 | ||||
|  	if (!arvif->is_started) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	ieee80211_radar_detected(ar->hw);
 | ||||
| +	ieee80211_radar_detected(ar->hw, NULL);
 | ||||
|   | ||||
|  	return count; | ||||
|  } | ||||
| --- a/drivers/net/wireless/ath/ath10k/mac.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath10k/mac.c
 | ||||
| @@ -1437,7 +1437,7 @@ static void ath10k_recalc_radar_detectio
 | ||||
|  		 * by indicating that radar was detected. | ||||
|  		 */ | ||||
|  		ath10k_warn(ar, "failed to start CAC: %d\n", ret); | ||||
| -		ieee80211_radar_detected(ar->hw);
 | ||||
| +		ieee80211_radar_detected(ar->hw, NULL);
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/ath/ath10k/wmi.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath10k/wmi.c
 | ||||
| @@ -3990,7 +3990,7 @@ static void ath10k_radar_detected(struct
 | ||||
|  	if (ar->dfs_block_radar_events) | ||||
|  		ath10k_info(ar, "DFS Radar detected, but ignored as requested\n"); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(ar->hw);
 | ||||
| +		ieee80211_radar_detected(ar->hw, NULL);
 | ||||
|  } | ||||
|   | ||||
|  static void ath10k_radar_confirmation_work(struct work_struct *work) | ||||
| --- a/drivers/net/wireless/ath/ath11k/wmi.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath11k/wmi.c
 | ||||
| @@ -8358,7 +8358,7 @@ ath11k_wmi_pdev_dfs_radar_detected_event
 | ||||
|  	if (ar->dfs_block_radar_events) | ||||
|  		ath11k_info(ab, "DFS Radar detected, but ignored as requested\n"); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(ar->hw);
 | ||||
| +		ieee80211_radar_detected(ar->hw, NULL);
 | ||||
|   | ||||
|  exit: | ||||
|  	rcu_read_unlock(); | ||||
| --- a/drivers/net/wireless/ath/ath12k/wmi.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath12k/wmi.c
 | ||||
| @@ -6789,7 +6789,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event
 | ||||
|  	if (ar->dfs_block_radar_events) | ||||
|  		ath12k_info(ab, "DFS Radar detected, but ignored as requested\n"); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(ath12k_ar_to_hw(ar));
 | ||||
| +		ieee80211_radar_detected(ath12k_ar_to_hw(ar), NULL);
 | ||||
|   | ||||
|  exit: | ||||
|  	rcu_read_unlock(); | ||||
| --- a/drivers/net/wireless/ath/ath9k/dfs.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/dfs.c
 | ||||
| @@ -280,7 +280,7 @@ ath9k_dfs_process_radar_pulse(struct ath
 | ||||
|  	if (!pd->add_pulse(pd, pe, NULL)) | ||||
|  		return; | ||||
|  	DFS_STAT_INC(sc, radar_detected); | ||||
| -	ieee80211_radar_detected(sc->hw);
 | ||||
| +	ieee80211_radar_detected(sc->hw, NULL);
 | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c
 | ||||
| @@ -116,7 +116,7 @@ static ssize_t write_file_simulate_radar
 | ||||
|  { | ||||
|  	struct ath_softc *sc = file->private_data; | ||||
|   | ||||
| -	ieee80211_radar_detected(sc->hw);
 | ||||
| +	ieee80211_radar_detected(sc->hw, NULL);
 | ||||
|   | ||||
|  	return count; | ||||
|  } | ||||
| --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
 | ||||
| +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
 | ||||
| @@ -394,7 +394,7 @@ mt7615_mcu_rx_radar_detected(struct mt76
 | ||||
|  	if (mt76_phy_dfs_state(mphy) < MT_DFS_STATE_CAC) | ||||
|  		return; | ||||
|   | ||||
| -	ieee80211_radar_detected(mphy->hw);
 | ||||
| +	ieee80211_radar_detected(mphy->hw, NULL);
 | ||||
|  	dev->hw_pattern++; | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
 | ||||
| +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
 | ||||
| @@ -630,7 +630,7 @@ static void mt76x02_dfs_tasklet(struct t
 | ||||
|  		radar_detected = mt76x02_dfs_check_detection(dev); | ||||
|  		if (radar_detected) { | ||||
|  			/* sw detector rx radar pattern */ | ||||
| -			ieee80211_radar_detected(dev->mt76.hw);
 | ||||
| +			ieee80211_radar_detected(dev->mt76.hw, NULL);
 | ||||
|  			mt76x02_dfs_detector_reset(dev); | ||||
|   | ||||
|  			return; | ||||
| @@ -658,7 +658,7 @@ static void mt76x02_dfs_tasklet(struct t
 | ||||
|   | ||||
|  		/* hw detector rx radar pattern */ | ||||
|  		dfs_pd->stats[i].hw_pattern++; | ||||
| -		ieee80211_radar_detected(dev->mt76.hw);
 | ||||
| +		ieee80211_radar_detected(dev->mt76.hw, NULL);
 | ||||
|  		mt76x02_dfs_detector_reset(dev); | ||||
|   | ||||
|  		return; | ||||
| --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
 | ||||
| +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
 | ||||
| @@ -293,7 +293,7 @@ mt7915_mcu_rx_radar_detected(struct mt79
 | ||||
|  						&dev->rdd2_chandef, | ||||
|  						GFP_ATOMIC); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(mphy->hw);
 | ||||
| +		ieee80211_radar_detected(mphy->hw, NULL);
 | ||||
|  	dev->hw_pattern++; | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
 | ||||
| +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
 | ||||
| @@ -371,7 +371,7 @@ mt7996_mcu_rx_radar_detected(struct mt79
 | ||||
|  						&dev->rdd2_chandef, | ||||
|  						GFP_ATOMIC); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(mphy->hw);
 | ||||
| +		ieee80211_radar_detected(mphy->hw, NULL);
 | ||||
|  	dev->hw_pattern++; | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/ti/wl18xx/event.c
 | ||||
| +++ b/drivers/net/wireless/ti/wl18xx/event.c
 | ||||
| @@ -142,7 +142,7 @@ int wl18xx_process_mailbox_events(struct
 | ||||
|  			    wl18xx_radar_type_decode(mbox->radar_type)); | ||||
|   | ||||
|  		if (!wl->radar_debug_mode) | ||||
| -			ieee80211_radar_detected(wl->hw);
 | ||||
| +			ieee80211_radar_detected(wl->hw, NULL);
 | ||||
|  	} | ||||
|   | ||||
|  	if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { | ||||
| --- a/drivers/net/wireless/virtual/mac80211_hwsim.c
 | ||||
| +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
 | ||||
| @@ -1146,7 +1146,7 @@ static int hwsim_write_simulate_radar(vo
 | ||||
|  { | ||||
|  	struct mac80211_hwsim_data *data = dat; | ||||
|   | ||||
| -	ieee80211_radar_detected(data->hw);
 | ||||
| +	ieee80211_radar_detected(data->hw, NULL);
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| --- a/include/net/mac80211.h
 | ||||
| +++ b/include/net/mac80211.h
 | ||||
| @@ -6717,8 +6717,11 @@ void ieee80211_cqm_beacon_loss_notify(st
 | ||||
|   * ieee80211_radar_detected - inform that a radar was detected | ||||
|   * | ||||
|   * @hw: pointer as obtained from ieee80211_alloc_hw() | ||||
| + * @chanctx_conf: Channel context on which radar is detected. Mandatory to
 | ||||
| + *	pass a valid pointer during MLO. For non-MLO %NULL can be passed
 | ||||
|   */ | ||||
| -void ieee80211_radar_detected(struct ieee80211_hw *hw);
 | ||||
| +void ieee80211_radar_detected(struct ieee80211_hw *hw,
 | ||||
| +			      struct ieee80211_chanctx_conf *chanctx_conf);
 | ||||
|   | ||||
|  /** | ||||
|   * ieee80211_chswitch_done - Complete channel switch process | ||||
| --- a/net/mac80211/chan.c
 | ||||
| +++ b/net/mac80211/chan.c
 | ||||
| @@ -681,6 +681,7 @@ ieee80211_alloc_chanctx(struct ieee80211
 | ||||
|  	ctx->mode = mode; | ||||
|  	ctx->conf.radar_enabled = false; | ||||
|  	ctx->conf.radio_idx = radio_idx; | ||||
| +	ctx->radar_detected = false;
 | ||||
|  	_ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false); | ||||
|   | ||||
|  	return ctx; | ||||
| --- a/net/mac80211/ieee80211_i.h
 | ||||
| +++ b/net/mac80211/ieee80211_i.h
 | ||||
| @@ -895,6 +895,8 @@ struct ieee80211_chanctx {
 | ||||
|  	struct ieee80211_chan_req req; | ||||
|   | ||||
|  	struct ieee80211_chanctx_conf conf; | ||||
| +
 | ||||
| +	bool radar_detected;
 | ||||
|  }; | ||||
|   | ||||
|  struct mac80211_qos_map { | ||||
| @@ -2632,7 +2634,8 @@ void ieee80211_recalc_chanctx_min_def(st
 | ||||
|  bool ieee80211_is_radar_required(struct ieee80211_local *local); | ||||
|   | ||||
|  void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work); | ||||
| -void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 | ||||
| +void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
 | ||||
| +			      struct ieee80211_chanctx *chanctx);
 | ||||
|  void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy, | ||||
|  				       struct wiphy_work *work); | ||||
|  int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, | ||||
| --- a/net/mac80211/pm.c
 | ||||
| +++ b/net/mac80211/pm.c
 | ||||
| @@ -32,7 +32,7 @@ int __ieee80211_suspend(struct ieee80211
 | ||||
|   | ||||
|  	ieee80211_scan_cancel(local); | ||||
|   | ||||
| -	ieee80211_dfs_cac_cancel(local);
 | ||||
| +	ieee80211_dfs_cac_cancel(local, NULL);
 | ||||
|   | ||||
|  	ieee80211_roc_purge(local, NULL); | ||||
|   | ||||
| --- a/net/mac80211/util.c
 | ||||
| +++ b/net/mac80211/util.c
 | ||||
| @@ -3451,11 +3451,16 @@ u64 ieee80211_calculate_rx_timestamp(str
 | ||||
|  	return ts; | ||||
|  } | ||||
|   | ||||
| -void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
 | ||||
| +/* Cancel CAC for the interfaces under the specified @local. If @ctx is
 | ||||
| + * also provided, only the interfaces using that ctx will be canceled.
 | ||||
| + */
 | ||||
| +void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
 | ||||
| +			      struct ieee80211_chanctx *ctx)
 | ||||
|  { | ||||
|  	struct ieee80211_sub_if_data *sdata; | ||||
|  	struct cfg80211_chan_def chandef; | ||||
|  	struct ieee80211_link_data *link; | ||||
| +	struct ieee80211_chanctx_conf *chanctx_conf;
 | ||||
|  	unsigned int link_id; | ||||
|   | ||||
|  	lockdep_assert_wiphy(local->hw.wiphy); | ||||
| @@ -3468,6 +3473,11 @@ void ieee80211_dfs_cac_cancel(struct iee
 | ||||
|  			if (!link) | ||||
|  				continue; | ||||
|   | ||||
| +			chanctx_conf = sdata_dereference(link->conf->chanctx_conf,
 | ||||
| +							 sdata);
 | ||||
| +			if (ctx && &ctx->conf != chanctx_conf)
 | ||||
| +				continue;
 | ||||
| +
 | ||||
|  			wiphy_delayed_work_cancel(local->hw.wiphy, | ||||
|  						  &link->dfs_cac_timer_work); | ||||
|   | ||||
| @@ -3488,9 +3498,8 @@ void ieee80211_dfs_radar_detected_work(s
 | ||||
|  { | ||||
|  	struct ieee80211_local *local = | ||||
|  		container_of(work, struct ieee80211_local, radar_detected_work); | ||||
| -	struct cfg80211_chan_def chandef = local->hw.conf.chandef;
 | ||||
| +	struct cfg80211_chan_def chandef;
 | ||||
|  	struct ieee80211_chanctx *ctx; | ||||
| -	int num_chanctx = 0;
 | ||||
|   | ||||
|  	lockdep_assert_wiphy(local->hw.wiphy); | ||||
|   | ||||
| @@ -3498,25 +3507,46 @@ void ieee80211_dfs_radar_detected_work(s
 | ||||
|  		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) | ||||
|  			continue; | ||||
|   | ||||
| -		num_chanctx++;
 | ||||
| +		if (!ctx->radar_detected)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		ctx->radar_detected = false;
 | ||||
| +
 | ||||
|  		chandef = ctx->conf.def; | ||||
| +
 | ||||
| +		ieee80211_dfs_cac_cancel(local, ctx);
 | ||||
| +		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
 | ||||
|  	} | ||||
| +}
 | ||||
|   | ||||
| -	ieee80211_dfs_cac_cancel(local);
 | ||||
| +static void
 | ||||
| +ieee80211_radar_mark_chan_ctx_iterator(struct ieee80211_hw *hw,
 | ||||
| +				       struct ieee80211_chanctx_conf *chanctx_conf,
 | ||||
| +				       void *data)
 | ||||
| +{
 | ||||
| +	struct ieee80211_chanctx *ctx =
 | ||||
| +		container_of(chanctx_conf, struct ieee80211_chanctx,
 | ||||
| +			     conf);
 | ||||
|   | ||||
| -	if (num_chanctx > 1)
 | ||||
| -		/* XXX: multi-channel is not supported yet */
 | ||||
| -		WARN_ON(1);
 | ||||
| -	else
 | ||||
| -		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
 | ||||
| +	if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	if (data && data != chanctx_conf)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	ctx->radar_detected = true;
 | ||||
|  } | ||||
|   | ||||
| -void ieee80211_radar_detected(struct ieee80211_hw *hw)
 | ||||
| +void ieee80211_radar_detected(struct ieee80211_hw *hw,
 | ||||
| +			      struct ieee80211_chanctx_conf *chanctx_conf)
 | ||||
|  { | ||||
|  	struct ieee80211_local *local = hw_to_local(hw); | ||||
|   | ||||
|  	trace_api_radar_detected(local); | ||||
|   | ||||
| +	ieee80211_iter_chan_contexts_atomic(hw, ieee80211_radar_mark_chan_ctx_iterator,
 | ||||
| +					    chanctx_conf);
 | ||||
| +
 | ||||
|  	wiphy_work_queue(hw->wiphy, &local->radar_detected_work); | ||||
|  } | ||||
|  EXPORT_SYMBOL(ieee80211_radar_detected); | ||||
| @ -9,3 +9,56 @@ | ||||
|  		head = &wcid->tx_offchannel; | ||||
|  	else | ||||
|  		head = &wcid->tx_pending; | ||||
| --- a/mt7615/mcu.c
 | ||||
| +++ b/mt7615/mcu.c
 | ||||
| @@ -394,7 +394,7 @@ mt7615_mcu_rx_radar_detected(struct mt76
 | ||||
|  	if (mt76_phy_dfs_state(mphy) < MT_DFS_STATE_CAC) | ||||
|  		return; | ||||
|   | ||||
| -	ieee80211_radar_detected(mphy->hw);
 | ||||
| +	ieee80211_radar_detected(mphy->hw, NULL);
 | ||||
|  	dev->hw_pattern++; | ||||
|  } | ||||
|   | ||||
| --- a/mt76x02_dfs.c
 | ||||
| +++ b/mt76x02_dfs.c
 | ||||
| @@ -630,7 +630,7 @@ static void mt76x02_dfs_tasklet(struct t
 | ||||
|  		radar_detected = mt76x02_dfs_check_detection(dev); | ||||
|  		if (radar_detected) { | ||||
|  			/* sw detector rx radar pattern */ | ||||
| -			ieee80211_radar_detected(dev->mt76.hw);
 | ||||
| +			ieee80211_radar_detected(dev->mt76.hw, NULL);
 | ||||
|  			mt76x02_dfs_detector_reset(dev); | ||||
|   | ||||
|  			return; | ||||
| @@ -658,7 +658,7 @@ static void mt76x02_dfs_tasklet(struct t
 | ||||
|   | ||||
|  		/* hw detector rx radar pattern */ | ||||
|  		dfs_pd->stats[i].hw_pattern++; | ||||
| -		ieee80211_radar_detected(dev->mt76.hw);
 | ||||
| +		ieee80211_radar_detected(dev->mt76.hw, NULL);
 | ||||
|  		mt76x02_dfs_detector_reset(dev); | ||||
|   | ||||
|  		return; | ||||
| --- a/mt7915/mcu.c
 | ||||
| +++ b/mt7915/mcu.c
 | ||||
| @@ -297,7 +297,7 @@ mt7915_mcu_rx_radar_detected(struct mt79
 | ||||
|  						&dev->rdd2_chandef, | ||||
|  						GFP_ATOMIC); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(mphy->hw);
 | ||||
| +		ieee80211_radar_detected(mphy->hw, NULL);
 | ||||
|  	dev->hw_pattern++; | ||||
|  } | ||||
|   | ||||
| --- a/mt7996/mcu.c
 | ||||
| +++ b/mt7996/mcu.c
 | ||||
| @@ -383,7 +383,7 @@ mt7996_mcu_rx_radar_detected(struct mt79
 | ||||
|  						&dev->rdd2_chandef, | ||||
|  						GFP_ATOMIC); | ||||
|  	else | ||||
| -		ieee80211_radar_detected(mphy->hw);
 | ||||
| +		ieee80211_radar_detected(mphy->hw, NULL);
 | ||||
|  	dev->hw_pattern++; | ||||
|  } | ||||
|   | ||||
|  | ||||
| @ -68,6 +68,15 @@ | ||||
|  			sta->tdls, | ||||
|  			sta->tdls_initiator, | ||||
|  			sta->wme, | ||||
| @@ -1158,7 +1158,7 @@ static ssize_t mwl_debugfs_dfs_radar_wri
 | ||||
|  	struct mwl_priv *priv = (struct mwl_priv *)file->private_data; | ||||
|   | ||||
|  	wiphy_info(priv->hw->wiphy, "simulate radar detected\n"); | ||||
| -	ieee80211_radar_detected(priv->hw);
 | ||||
| +	ieee80211_radar_detected(priv->hw, NULL);
 | ||||
|   | ||||
|  	return count; | ||||
|  } | ||||
| --- a/hif/fwcmd.c
 | ||||
| +++ b/hif/fwcmd.c
 | ||||
| @@ -633,11 +633,15 @@ einval:
 | ||||
| @ -491,3 +500,32 @@ | ||||
|   | ||||
|  	switch (format) { | ||||
|  	case TX_RATE_FORMAT_LEGACY: | ||||
| --- a/hif/pcie/pcie.c
 | ||||
| +++ b/hif/pcie/pcie.c
 | ||||
| @@ -546,7 +546,7 @@ static irqreturn_t pcie_isr_8864(struct
 | ||||
|   | ||||
|  		if (int_status & MACREG_A2HRIC_BIT_RADAR_DETECT) { | ||||
|  			wiphy_info(hw->wiphy, "radar detected by firmware\n"); | ||||
| -			ieee80211_radar_detected(hw);
 | ||||
| +			ieee80211_radar_detected(hw, NULL);
 | ||||
|  		} | ||||
|   | ||||
|  		if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH) ieee80211_queue_work(hw, &priv->chnl_switch_handle); | ||||
| @@ -593,7 +593,7 @@ static irqreturn_t pcie_isr_8997(struct
 | ||||
|   | ||||
|  		if (int_status & MACREG_A2HRIC_BIT_RADAR_DETECT) { | ||||
|  			wiphy_info(hw->wiphy, "radar detected by firmware\n"); | ||||
| -			ieee80211_radar_detected(hw);
 | ||||
| +			ieee80211_radar_detected(hw, NULL);
 | ||||
|  		} | ||||
|   | ||||
|  		if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH) | ||||
| @@ -1071,7 +1071,7 @@ static irqreturn_t pcie_isr_ndp(struct i
 | ||||
|   | ||||
|  		if (int_status & MACREG_A2HRIC_NEWDP_DFS) { | ||||
|  			wiphy_info(hw->wiphy, "radar detected by firmware\n"); | ||||
| -			ieee80211_radar_detected(hw);
 | ||||
| +			ieee80211_radar_detected(hw, NULL);
 | ||||
|  		} | ||||
|   | ||||
|  		if (int_status & MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user