All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Pre-CAC and sharing DFS state across multiple radios
@ 2017-02-20 10:39 Vasanthakumar Thiagarajan
  2017-02-20 10:39 ` [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain Vasanthakumar Thiagarajan
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Vasanthakumar Thiagarajan @ 2017-02-20 10:39 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Vasanthakumar Thiagarajan

Currently irrespective of dfs domain and radar detection activity
pre-CAC results for a wiphy are retained till the wiphy is detroyed.
This may not be preferred in non-ETSI dfs domain where pre-CAC is not
explicitly mentioned in the respective DFS requirement spec. This patch
set modifies the current behaviour of pre-CAC for non-ETSI domain by
giving 2 seconds grace period for dfs master interface to start operating
on the CAC completed channel.

This patch set also adds support to share dfs channel state across
multiple radios of the same regulatory configuration.

Vasanthakumar Thiagarajan (3):
  cfg80211: Make pre-CAC results valid only for ETSI domain
  cfg80211: Disallow moving out of operating DFS channel in non-ETSI
  cfg80211: Share Channel DFS state across wiphys of same DFS domain

 include/uapi/linux/nl80211.h |   5 ++
 net/wireless/ap.c            |   5 ++
 net/wireless/chan.c          | 117 ++++++++++++++++++++++++++++++++++
 net/wireless/core.c          |  37 +++++++++++
 net/wireless/core.h          |  16 +++++
 net/wireless/ibss.c          |   1 +
 net/wireless/mesh.c          |   1 +
 net/wireless/mlme.c          |  50 ++++++++++++---
 net/wireless/nl80211.c       |  52 ++++++++++++++++
 net/wireless/reg.c           | 145 +++++++++++++++++++++++++++++++++++++++++++
 net/wireless/reg.h           |  36 +++++++++++
 11 files changed, 456 insertions(+), 9 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain
  2017-02-20 10:39 [PATCH 0/3] Pre-CAC and sharing DFS state across multiple radios Vasanthakumar Thiagarajan
@ 2017-02-20 10:39 ` Vasanthakumar Thiagarajan
  2017-02-27 14:38   ` Johannes Berg
  2017-02-20 10:39 ` [PATCH 2/3] cfg80211: Disallow moving out of operating DFS channel in non-ETSI Vasanthakumar Thiagarajan
  2017-02-20 10:39 ` [PATCH 3/3] cfg80211: Share Channel DFS state across wiphys of same DFS domain Vasanthakumar Thiagarajan
  2 siblings, 1 reply; 6+ messages in thread
From: Vasanthakumar Thiagarajan @ 2017-02-20 10:39 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Vasanthakumar Thiagarajan

DFS requirement for ETSI domain (section 4.7.1.4 in
ETSI EN 301 893 V1.8.1) is the only one which explicitly
states that once DFS channel is marked as available afer
the CAC, this channel will remain in available state even
moving to a different operating channel. But the same is
not explicitly stated in FCC DFS requirement. Also, Pre-CAC
requriements are not explicitly mentioned in FCC requirement.
Current implementation in keeping DFS channel in available
state is same as described in ETSI domain.

For ETSI DFS domain, this patch gives a grace period of 2 seconds
since the completion of successful CAC before moving the channel's
DFS state to 'usable' from 'available' state. The same grace period
is checked against the channel's dfs_state_entered timestamp while
deciding if a DFS channel is available for operation. There is a new
radar event, NL80211_RADAR_PRE_CAC_EXPIRED, reported when DFS channel
is moved from available to usable state after the grace period. Also
make sure the DFS channel state is reset to usable once the beaconing
operation on that channel is brought down (like stop_ap, leave_ibss
and leave_mesh) in non-ETSI domain.

Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
---
 include/uapi/linux/nl80211.h |   5 +++
 net/wireless/ap.c            |   5 +++
 net/wireless/chan.c          | 101 +++++++++++++++++++++++++++++++++++++++++++
 net/wireless/core.h          |  10 +++++
 net/wireless/ibss.c          |   1 +
 net/wireless/mesh.c          |   1 +
 net/wireless/mlme.c          |  40 +++++++++++++----
 net/wireless/reg.c           |  28 ++++++++++++
 net/wireless/reg.h           |  14 ++++++
 9 files changed, 196 insertions(+), 9 deletions(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9a499b1..cd4dfef 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4913,12 +4913,17 @@ enum nl80211_smps_mode {
  *	change to the channel status.
  * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
  *	over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ *	non-operating channel is expired and no longer valid. New CAC must
+ *	be done on this channel before starting the operation. This is not
+ *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
  */
 enum nl80211_radar_event {
 	NL80211_RADAR_DETECTED,
 	NL80211_RADAR_CAC_FINISHED,
 	NL80211_RADAR_CAC_ABORTED,
 	NL80211_RADAR_NOP_FINISHED,
+	NL80211_RADAR_PRE_CAC_EXPIRED,
 };
 
 /**
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index bdad1f9..25666d3 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -32,6 +32,11 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
 		rdev_set_qos_map(rdev, dev, NULL);
 		if (notify)
 			nl80211_send_ap_stopped(wdev);
+
+		/* Should we apply the grace period during beaconing interface
+		 * shutdown also?
+		 */
+		cfg80211_sched_dfs_chan_update(rdev);
 	}
 
 	return err;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 5497d022..099f13c 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -456,6 +456,107 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
 	return (r1 + r2 > 0);
 }
 
+/*
+ * Checks if center frequency of chan falls with in the bandwidth
+ * range of chandef.
+ */
+bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
+			  struct ieee80211_channel *chan)
+{
+	int width;
+	u32 cf_offset, freq;
+
+	if (chandef->chan->center_freq == chan->center_freq)
+		return true;
+
+	width = cfg80211_chandef_get_width(chandef);
+	if (width <= 20)
+		return false;
+
+	cf_offset = width / 2 - 10;
+
+	for (freq = chandef->center_freq1 - width / 2 + 10;
+	     freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) {
+		if (chan->center_freq == freq)
+			return true;
+	}
+
+	if (!chandef->center_freq2)
+		return false;
+
+	for (freq = chandef->center_freq2 - width / 2 + 10;
+	     freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) {
+		if (chan->center_freq == freq)
+			return true;
+	}
+
+	return false;
+}
+
+bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
+{
+	bool active = false;
+
+	ASSERT_WDEV_LOCK(wdev);
+
+	if (!wdev->chandef.chan)
+		return false;
+
+	switch (wdev->iftype) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_P2P_GO:
+		active = wdev->beacon_interval != 0;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		active = wdev->ssid_len != 0;
+		break;
+	case NL80211_IFTYPE_MESH_POINT:
+		active = wdev->mesh_id_len != 0;
+		break;
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_OCB:
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_MONITOR:
+	case NL80211_IFTYPE_AP_VLAN:
+	case NL80211_IFTYPE_WDS:
+	case NL80211_IFTYPE_P2P_DEVICE:
+	/* Can NAN type be considered as beaconing interface? */
+	case NL80211_IFTYPE_NAN:
+		break;
+	case NL80211_IFTYPE_UNSPECIFIED:
+	case NUM_NL80211_IFTYPES:
+		WARN_ON(1);
+	}
+
+	return active;
+}
+
+bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
+				  struct ieee80211_channel *chan)
+{
+	struct wireless_dev *wdev;
+
+	ASSERT_RTNL();
+
+	if (!(chan->flags & IEEE80211_CHAN_RADAR))
+		return false;
+
+	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
+		wdev_lock(wdev);
+		if (!cfg80211_beaconing_iface_active(wdev)) {
+			wdev_unlock(wdev);
+			continue;
+		}
+
+		if (cfg80211_is_sub_chan(&wdev->chandef, chan)) {
+			wdev_unlock(wdev);
+			return true;
+		}
+		wdev_unlock(wdev);
+	}
+
+	return false;
+}
 
 static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
 					     u32 center_freq,
diff --git a/net/wireless/core.h b/net/wireless/core.h
index efa690a..519a29e 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -466,6 +466,16 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
 cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
 			      const struct cfg80211_chan_def *chandef);
 
+void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
+
+bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
+				  struct ieee80211_channel *chan);
+
+bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev);
+
+bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
+			  struct ieee80211_channel *chan);
+
 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 {
 	unsigned long end = jiffies;
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 364f900..10bf040 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -190,6 +190,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
 	if (!nowext)
 		wdev->wext.ibss.ssid_len = 0;
 #endif
+	cfg80211_sched_dfs_chan_update(rdev);
 }
 
 void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 2d8518a..ec0b1c2 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -262,6 +262,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
 		wdev->beacon_interval = 0;
 		memset(&wdev->chandef, 0, sizeof(wdev->chandef));
 		rdev_set_qos_map(rdev, dev, NULL);
+		cfg80211_sched_dfs_chan_update(rdev);
 	}
 
 	return err;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 22b3d99..cd29366 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -745,6 +745,12 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
 }
 EXPORT_SYMBOL(cfg80211_rx_mgmt);
 
+void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev)
+{
+	cancel_delayed_work(&rdev->dfs_update_channels_wk);
+	queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0);
+}
+
 void cfg80211_dfs_channels_update_work(struct work_struct *work)
 {
 	struct delayed_work *delayed_work = to_delayed_work(work);
@@ -755,6 +761,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
 	struct wiphy *wiphy;
 	bool check_again = false;
 	unsigned long timeout, next_time = 0;
+	unsigned long time_dfs_update;
+	enum nl80211_radar_event radar_event;
 	int bandid, i;
 
 	rdev = container_of(delayed_work, struct cfg80211_registered_device,
@@ -770,11 +778,27 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
 		for (i = 0; i < sband->n_channels; i++) {
 			c = &sband->channels[i];
 
-			if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
+			if (!(c->flags & IEEE80211_CHAN_RADAR))
+				continue;
+
+			if (c->dfs_state != NL80211_DFS_UNAVAILABLE &&
+			    c->dfs_state != NL80211_DFS_AVAILABLE)
 				continue;
 
-			timeout = c->dfs_state_entered + msecs_to_jiffies(
-					IEEE80211_DFS_MIN_NOP_TIME_MS);
+			if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
+				time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
+				radar_event = NL80211_RADAR_NOP_FINISHED;
+			} else {
+				if (regulatory_pre_cac_allowed(wiphy) ||
+				    cfg80211_any_wiphy_oper_chan(wiphy, c))
+					continue;
+
+				time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
+				radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
+			}
+
+			timeout = c->dfs_state_entered +
+				  msecs_to_jiffies(time_dfs_update);
 
 			if (time_after_eq(jiffies, timeout)) {
 				c->dfs_state = NL80211_DFS_USABLE;
@@ -784,8 +808,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
 							NL80211_CHAN_NO_HT);
 
 				nl80211_radar_notify(rdev, &chandef,
-						     NL80211_RADAR_NOP_FINISHED,
-						     NULL, GFP_ATOMIC);
+						     radar_event, NULL,
+						     GFP_ATOMIC);
 				continue;
 			}
 
@@ -810,7 +834,6 @@ void cfg80211_radar_event(struct wiphy *wiphy,
 			  gfp_t gfp)
 {
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-	unsigned long timeout;
 
 	trace_cfg80211_radar_event(wiphy, chandef);
 
@@ -820,9 +843,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
 	 */
 	cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
 
-	timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
-	queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
-			   timeout);
+	cfg80211_sched_dfs_chan_update(rdev);
 
 	nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
 }
@@ -851,6 +872,7 @@ void cfg80211_cac_event(struct net_device *netdev,
 			  msecs_to_jiffies(wdev->cac_time_ms);
 		WARN_ON(!time_after_eq(jiffies, timeout));
 		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+		cfg80211_sched_dfs_chan_update(rdev);
 		break;
 	case NL80211_RADAR_CAC_ABORTED:
 		break;
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 753efcd..e59b192 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -3120,6 +3120,34 @@ bool regulatory_indoor_allowed(void)
 	return reg_is_indoor;
 }
 
+bool regulatory_pre_cac_allowed(struct wiphy *wiphy)
+{
+	const struct ieee80211_regdomain *regd = NULL;
+	const struct ieee80211_regdomain *wiphy_regd = NULL;
+	bool pre_cac_allowed = false;
+
+	rcu_read_lock();
+
+	regd = rcu_dereference(cfg80211_regdomain);
+	wiphy_regd = rcu_dereference(wiphy->regd);
+	if (!wiphy_regd) {
+		if (regd->dfs_region == NL80211_DFS_ETSI)
+			pre_cac_allowed = true;
+
+		rcu_read_unlock();
+
+		return pre_cac_allowed;
+	}
+
+	if (regd->dfs_region == wiphy_regd->dfs_region &&
+	    wiphy_regd->dfs_region == NL80211_DFS_ETSI)
+		pre_cac_allowed = true;
+
+	rcu_read_unlock();
+
+	return pre_cac_allowed;
+}
+
 int __init regulatory_init(void)
 {
 	int err = 0;
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index f6ced31..ff078f0 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -143,4 +143,18 @@ void regulatory_hint_country_ie(struct wiphy *wiphy,
  */
 bool regulatory_indoor_allowed(void);
 
+/*
+ * Grace period to timeout pre-CAC results on the dfs channels. This timeout
+ * value is used for Non-ETSI domain.
+ * TODO: May be make this timeout available through regdb?
+ */
+#define REG_PRE_CAC_EXPIRY_GRACE_MS 2000
+
+/**
+ * regulatory_pre_cac_allowed - if pre-CAC allowed in the current dfs domain
+ * @wiphy: wiphy for which pre-CAC capability is checked.
+
+ * Pre-CAC is allowed only in ETSI domain.
+ */
+bool regulatory_pre_cac_allowed(struct wiphy *wiphy);
 #endif  /* __NET_WIRELESS_REG_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] cfg80211: Disallow moving out of operating DFS channel in non-ETSI
  2017-02-20 10:39 [PATCH 0/3] Pre-CAC and sharing DFS state across multiple radios Vasanthakumar Thiagarajan
  2017-02-20 10:39 ` [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain Vasanthakumar Thiagarajan
@ 2017-02-20 10:39 ` Vasanthakumar Thiagarajan
  2017-02-20 10:39 ` [PATCH 3/3] cfg80211: Share Channel DFS state across wiphys of same DFS domain Vasanthakumar Thiagarajan
  2 siblings, 0 replies; 6+ messages in thread
From: Vasanthakumar Thiagarajan @ 2017-02-20 10:39 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Vasanthakumar Thiagarajan

For non-ETSI regulatory domain, CAC result on DFS channel
may not be valid once moving out of that channel (as done
during remain-on-channel, scannning and off-channel tx).
Running CAC on an operating DFS channel after every off-channel
operation will only add complexity and disturb the current
link. Better do not allow any off-channel switch from a DFS
operating channel in non-ETSI domain.

Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
---
 net/wireless/nl80211.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d516527..b15903b 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6556,6 +6556,19 @@ static int nl80211_parse_random_mac(struct nlattr **attrs,
 	return 0;
 }
 
+static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev)
+{
+	ASSERT_WDEV_LOCK(wdev);
+
+	if (!cfg80211_beaconing_iface_active(wdev))
+		return true;
+
+	if (!(wdev->chandef.chan->flags & IEEE80211_CHAN_RADAR))
+		return true;
+
+	return regulatory_pre_cac_allowed(wdev->wiphy);
+}
+
 static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -6681,6 +6694,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
 	request->n_channels = i;
 
+	wdev_lock(wdev);
+	if (!cfg80211_off_channel_oper_allowed(wdev)) {
+		struct ieee80211_channel *chan;
+
+		if (request->n_channels != 1) {
+			wdev_unlock(wdev);
+			err = -EBUSY;
+			goto out_free;
+		}
+
+		chan = request->channels[0];
+		if (chan->center_freq != wdev->chandef.chan->center_freq) {
+			wdev_unlock(wdev);
+			err = -EBUSY;
+			goto out_free;
+		}
+	}
+	wdev_unlock(wdev);
+
 	i = 0;
 	if (n_ssids) {
 		nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
@@ -9103,6 +9135,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
 	struct wireless_dev *wdev = info->user_ptr[1];
 	struct cfg80211_chan_def chandef;
+	const struct cfg80211_chan_def *compat_chandef;
 	struct sk_buff *msg;
 	void *hdr;
 	u64 cookie;
@@ -9131,6 +9164,18 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
 	if (err)
 		return err;
 
+	wdev_lock(wdev);
+	if (!cfg80211_off_channel_oper_allowed(wdev) &&
+	    !cfg80211_chandef_identical(&wdev->chandef, &chandef)) {
+		compat_chandef = cfg80211_chandef_compatible(&wdev->chandef,
+							     &chandef);
+		if (compat_chandef != &chandef) {
+			wdev_unlock(wdev);
+			return -EBUSY;
+		}
+	}
+	wdev_unlock(wdev);
+
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 	if (!msg)
 		return -ENOMEM;
@@ -9306,6 +9351,13 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 	if (!chandef.chan && params.offchan)
 		return -EINVAL;
 
+	wdev_lock(wdev);
+	if (params.offchan && !cfg80211_off_channel_oper_allowed(wdev)) {
+		wdev_unlock(wdev);
+		return -EBUSY;
+	}
+	wdev_unlock(wdev);
+
 	params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
 	params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] cfg80211: Share Channel DFS state across wiphys of same DFS domain
  2017-02-20 10:39 [PATCH 0/3] Pre-CAC and sharing DFS state across multiple radios Vasanthakumar Thiagarajan
  2017-02-20 10:39 ` [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain Vasanthakumar Thiagarajan
  2017-02-20 10:39 ` [PATCH 2/3] cfg80211: Disallow moving out of operating DFS channel in non-ETSI Vasanthakumar Thiagarajan
@ 2017-02-20 10:39 ` Vasanthakumar Thiagarajan
  2 siblings, 0 replies; 6+ messages in thread
From: Vasanthakumar Thiagarajan @ 2017-02-20 10:39 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Vasanthakumar Thiagarajan

Sharing DFS channel state across multiple wiphys (radios) could
be useful with multiple radios on the system. When one radio
completes CAC and markes the channel available another radio
can use this information and start beaconing without really doing
CAC.

Whenever there is a state change in dfs channel associated to
a particular wiphy the the same state change is propagated to
other wiphys having the same DFS reg domain configuration.
Also when a new wiphy is created the dfs channel state of
other existing wiphys of same DFS domain is copied.

Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
---
 net/wireless/chan.c |  30 ++++++++++----
 net/wireless/core.c |  37 +++++++++++++++++
 net/wireless/core.h |   6 +++
 net/wireless/mlme.c |  10 +++++
 net/wireless/reg.c  | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 net/wireless/reg.h  |  22 ++++++++++
 6 files changed, 215 insertions(+), 7 deletions(-)

diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 099f13c..b8aa5a7 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -531,16 +531,11 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
 	return active;
 }
 
-bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
-				  struct ieee80211_channel *chan)
+static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
+					struct ieee80211_channel *chan)
 {
 	struct wireless_dev *wdev;
 
-	ASSERT_RTNL();
-
-	if (!(chan->flags & IEEE80211_CHAN_RADAR))
-		return false;
-
 	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
 		wdev_lock(wdev);
 		if (!cfg80211_beaconing_iface_active(wdev)) {
@@ -558,6 +553,27 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
 	return false;
 }
 
+bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
+				  struct ieee80211_channel *chan)
+{
+	struct cfg80211_registered_device *rdev;
+
+	ASSERT_RTNL();
+
+	if (!(chan->flags & IEEE80211_CHAN_RADAR))
+		return false;
+
+	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+		if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
+			continue;
+
+		if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
+			return true;
+	}
+
+	return false;
+}
+
 static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
 					     u32 center_freq,
 					     u32 bandwidth)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 04143df..a5630e9 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -357,6 +357,38 @@ static void cfg80211_sched_scan_stop_wk(struct work_struct *work)
 	rtnl_unlock();
 }
 
+static void cfg80211_propagate_radar_detect_wk(struct work_struct *work)
+{
+	struct cfg80211_registered_device *rdev;
+
+	rdev = container_of(work, struct cfg80211_registered_device,
+			    propagate_radar_detect_wk);
+
+	rtnl_lock();
+
+	regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef,
+				       NL80211_DFS_UNAVAILABLE,
+				       NL80211_RADAR_DETECTED);
+
+	rtnl_unlock();
+}
+
+static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
+{
+	struct cfg80211_registered_device *rdev;
+
+	rdev = container_of(work, struct cfg80211_registered_device,
+			    propagate_cac_done_wk);
+
+	rtnl_lock();
+
+	regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef,
+				       NL80211_DFS_AVAILABLE,
+				       NL80211_RADAR_CAC_FINISHED);
+
+	rtnl_unlock();
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
@@ -456,6 +488,9 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
 	spin_lock_init(&rdev->destroy_list_lock);
 	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
 	INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
+	INIT_WORK(&rdev->propagate_radar_detect_wk,
+		  cfg80211_propagate_radar_detect_wk);
+	INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
 
 #ifdef CONFIG_CFG80211_DEFAULT_PS
 	rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -915,6 +950,8 @@ void wiphy_unregister(struct wiphy *wiphy)
 	flush_work(&rdev->destroy_work);
 	flush_work(&rdev->sched_scan_stop_wk);
 	flush_work(&rdev->mlme_unreg_wk);
+	flush_work(&rdev->propagate_radar_detect_wk);
+	flush_work(&rdev->propagate_cac_done_wk);
 
 #ifdef CONFIG_PM
 	if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 519a29e..a2fe8fc 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -97,6 +97,12 @@ struct cfg80211_registered_device {
 
 	struct work_struct sched_scan_stop_wk;
 
+	struct cfg80211_chan_def radar_chandef;
+	struct work_struct propagate_radar_detect_wk;
+
+	struct cfg80211_chan_def cac_done_chandef;
+	struct work_struct propagate_cac_done_wk;
+
 	/* must be last because of the way we do wiphy_priv(),
 	 * and it should at least be aligned to NETDEV_ALIGN */
 	struct wiphy wiphy __aligned(NETDEV_ALIGN);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index cd29366..01ce4a6 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -810,6 +810,10 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
 				nl80211_radar_notify(rdev, &chandef,
 						     radar_event, NULL,
 						     GFP_ATOMIC);
+
+				regulatory_propagate_dfs_state(wiphy, &chandef,
+							       c->dfs_state,
+							       radar_event);
 				continue;
 			}
 
@@ -846,6 +850,9 @@ void cfg80211_radar_event(struct wiphy *wiphy,
 	cfg80211_sched_dfs_chan_update(rdev);
 
 	nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
+
+	memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def));
+	queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
 }
 EXPORT_SYMBOL(cfg80211_radar_event);
 
@@ -872,6 +879,9 @@ void cfg80211_cac_event(struct net_device *netdev,
 			  msecs_to_jiffies(wdev->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,
+		       sizeof(struct cfg80211_chan_def));
+		queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
 		cfg80211_sched_dfs_chan_update(rdev);
 		break;
 	case NL80211_RADAR_CAC_ABORTED:
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index e59b192..2ad5de0 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2067,6 +2067,88 @@ static void reg_set_request_processed(void)
 	return REG_REQ_IGNORE;
 }
 
+bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2)
+{
+	const struct ieee80211_regdomain *wiphy1_regd = NULL;
+	const struct ieee80211_regdomain *wiphy2_regd = NULL;
+	const struct ieee80211_regdomain *cfg80211_regd = NULL;
+	bool dfs_domain_same;
+
+	rcu_read_lock();
+
+	cfg80211_regd = rcu_dereference(cfg80211_regdomain);
+	wiphy1_regd = rcu_dereference(wiphy1->regd);
+	if (!wiphy1_regd)
+		wiphy1_regd = cfg80211_regd;
+
+	wiphy2_regd = rcu_dereference(wiphy2->regd);
+	if (!wiphy2_regd)
+		wiphy2_regd = cfg80211_regd;
+
+	dfs_domain_same = wiphy1_regd->dfs_region == wiphy2_regd->dfs_region;
+
+	rcu_read_unlock();
+
+	return dfs_domain_same;
+}
+
+static void reg_copy_dfs_chan_state(struct ieee80211_channel *dst_chan,
+				    struct ieee80211_channel *src_chan)
+{
+	if (!(dst_chan->flags & IEEE80211_CHAN_RADAR) ||
+	    !(src_chan->flags & IEEE80211_CHAN_RADAR))
+		return;
+
+	if (dst_chan->flags & IEEE80211_CHAN_DISABLED ||
+	    src_chan->flags & IEEE80211_CHAN_DISABLED)
+		return;
+
+	if (src_chan->center_freq == dst_chan->center_freq &&
+	    dst_chan->dfs_state == NL80211_DFS_USABLE) {
+		dst_chan->dfs_state = src_chan->dfs_state;
+		dst_chan->dfs_state_entered = src_chan->dfs_state_entered;
+	}
+}
+
+static void wiphy_share_dfs_chan_state(struct wiphy *dst_wiphy,
+				       struct wiphy *src_wiphy)
+{
+	struct ieee80211_supported_band *src_sband, *dst_sband;
+	struct ieee80211_channel *src_chan, *dst_chan;
+	int i, j, band;
+
+	if (!reg_dfs_domain_same(dst_wiphy, src_wiphy))
+		return;
+
+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
+		dst_sband = dst_wiphy->bands[band];
+		src_sband = src_wiphy->bands[band];
+		if (!dst_sband || !src_sband)
+			continue;
+
+		for (i = 0; i < dst_sband->n_channels; i++) {
+			dst_chan = &dst_sband->channels[i];
+			for (j = 0; j < src_sband->n_channels; j++) {
+				src_chan = &src_sband->channels[j];
+				reg_copy_dfs_chan_state(dst_chan, src_chan);
+			}
+		}
+	}
+}
+
+static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy)
+{
+	struct cfg80211_registered_device *rdev;
+
+	ASSERT_RTNL();
+
+	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+		if (wiphy == &rdev->wiphy)
+			continue;
+		wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy);
+	}
+}
+
 /* This processes *all* regulatory hints */
 static void reg_process_hint(struct regulatory_request *reg_request)
 {
@@ -2110,6 +2192,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
 	if (treatment == REG_REQ_ALREADY_SET && wiphy &&
 	    wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
 		wiphy_update_regulatory(wiphy, reg_request->initiator);
+		wiphy_all_share_dfs_chan_state(wiphy);
 		reg_check_channels();
 	}
 
@@ -3061,6 +3144,7 @@ void wiphy_regulatory_register(struct wiphy *wiphy)
 
 	lr = get_last_request();
 	wiphy_update_regulatory(wiphy, lr->initiator);
+	wiphy_all_share_dfs_chan_state(wiphy);
 }
 
 void wiphy_regulatory_deregister(struct wiphy *wiphy)
@@ -3148,6 +3232,39 @@ bool regulatory_pre_cac_allowed(struct wiphy *wiphy)
 	return pre_cac_allowed;
 }
 
+void regulatory_propagate_dfs_state(struct wiphy *wiphy,
+				    struct cfg80211_chan_def *chandef,
+				    enum nl80211_dfs_state dfs_state,
+				    enum nl80211_radar_event event)
+{
+	struct cfg80211_registered_device *rdev;
+
+	ASSERT_RTNL();
+
+	if (WARN_ON(!(chandef->chan->flags & IEEE80211_CHAN_RADAR)))
+		return;
+
+	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+		if (wiphy == &rdev->wiphy)
+			continue;
+
+		if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
+			continue;
+
+		if (!ieee80211_get_channel(&rdev->wiphy,
+					   chandef->chan->center_freq))
+			continue;
+
+		cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state);
+
+		if (event == NL80211_RADAR_DETECTED ||
+		    event == NL80211_RADAR_CAC_FINISHED)
+			cfg80211_sched_dfs_chan_update(rdev);
+
+		nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL);
+	}
+}
+
 int __init regulatory_init(void)
 {
 	int err = 0;
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index ff078f0..ca7fedf 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -157,4 +157,26 @@ void regulatory_hint_country_ie(struct wiphy *wiphy,
  * Pre-CAC is allowed only in ETSI domain.
  */
 bool regulatory_pre_cac_allowed(struct wiphy *wiphy);
+
+/**
+ * regulatory_propagate_dfs_state - Propagate DFS channel state to other wiphys
+ * @wiphy - wiphy on which radar is detected and the event will be propagated
+ *	to other available wiphys having the same DFS domain
+ * @chandef - Channel definition of radar detected channel
+ * @dfs_state - DFS channel state to be set
+ * @event - Type of radar event which triggered this DFS state change
+ *
+ * This function should be called with rtnl lock held.
+ */
+void regulatory_propagate_dfs_state(struct wiphy *wiphy,
+				    struct cfg80211_chan_def *chandef,
+				    enum nl80211_dfs_state dfs_state,
+				    enum nl80211_radar_event event);
+
+/**
+ * reg_dfs_domain_same - Checks if both wiphy have same DFS domain configured
+ * @wiphy1 - wiphy it's dfs_region to be checked against that of wiphy2
+ * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1
+ */
+bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2);
 #endif  /* __NET_WIRELESS_REG_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain
  2017-02-20 10:39 ` [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain Vasanthakumar Thiagarajan
@ 2017-02-27 14:38   ` Johannes Berg
  2017-02-28  4:52     ` Thiagarajan, Vasanthakumar
  0 siblings, 1 reply; 6+ messages in thread
From: Johannes Berg @ 2017-02-27 14:38 UTC (permalink / raw)
  To: Vasanthakumar Thiagarajan; +Cc: linux-wireless

On Mon, 2017-02-20 at 16:09 +0530, Vasanthakumar Thiagarajan wrote:
> DFS requirement for ETSI domain (section 4.7.1.4 in
> ETSI EN 301 893 V1.8.1) is the only one which explicitly
> states that once DFS channel is marked as available afer
> the CAC, this channel will remain in available state even
> moving to a different operating channel. But the same is
> not explicitly stated in FCC DFS requirement. Also, Pre-CAC
> requriements are not explicitly mentioned in FCC requirement.
> Current implementation in keeping DFS channel in available
> state is same as described in ETSI domain.
> 
> For ETSI DFS domain, this patch gives a grace period of 2 seconds

You mean non-ETSI, right?

Just making sure I understood correctly - no need to resend, I can fix
that.

johannes

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain
  2017-02-27 14:38   ` Johannes Berg
@ 2017-02-28  4:52     ` Thiagarajan, Vasanthakumar
  0 siblings, 0 replies; 6+ messages in thread
From: Thiagarajan, Vasanthakumar @ 2017-02-28  4:52 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

T24gTW9uZGF5IDI3IEZlYnJ1YXJ5IDIwMTcgMDg6MDggUE0sIEpvaGFubmVzIEJlcmcgd3JvdGU6
DQo+IE9uIE1vbiwgMjAxNy0wMi0yMCBhdCAxNjowOSArMDUzMCwgVmFzYW50aGFrdW1hciBUaGlh
Z2FyYWphbiB3cm90ZToNCj4+IERGUyByZXF1aXJlbWVudCBmb3IgRVRTSSBkb21haW4gKHNlY3Rp
b24gNC43LjEuNCBpbg0KPj4gRVRTSSBFTiAzMDEgODkzIFYxLjguMSkgaXMgdGhlIG9ubHkgb25l
IHdoaWNoIGV4cGxpY2l0bHkNCj4+IHN0YXRlcyB0aGF0IG9uY2UgREZTIGNoYW5uZWwgaXMgbWFy
a2VkIGFzIGF2YWlsYWJsZSBhZmVyDQo+PiB0aGUgQ0FDLCB0aGlzIGNoYW5uZWwgd2lsbCByZW1h
aW4gaW4gYXZhaWxhYmxlIHN0YXRlIGV2ZW4NCj4+IG1vdmluZyB0byBhIGRpZmZlcmVudCBvcGVy
YXRpbmcgY2hhbm5lbC4gQnV0IHRoZSBzYW1lIGlzDQo+PiBub3QgZXhwbGljaXRseSBzdGF0ZWQg
aW4gRkNDIERGUyByZXF1aXJlbWVudC4gQWxzbywgUHJlLUNBQw0KPj4gcmVxdXJpZW1lbnRzIGFy
ZSBub3QgZXhwbGljaXRseSBtZW50aW9uZWQgaW4gRkNDIHJlcXVpcmVtZW50Lg0KPj4gQ3VycmVu
dCBpbXBsZW1lbnRhdGlvbiBpbiBrZWVwaW5nIERGUyBjaGFubmVsIGluIGF2YWlsYWJsZQ0KPj4g
c3RhdGUgaXMgc2FtZSBhcyBkZXNjcmliZWQgaW4gRVRTSSBkb21haW4uDQo+Pg0KPj4gRm9yIEVU
U0kgREZTIGRvbWFpbiwgdGhpcyBwYXRjaCBnaXZlcyBhIGdyYWNlIHBlcmlvZCBvZiAyIHNlY29u
ZHMNCj4NCj4gWW91IG1lYW4gbm9uLUVUU0ksIHJpZ2h0Pw0KDQpDb3JyZWN0LCBJIG1lYW50IG5v
bi1FVFNJLg0KDQo+DQo+IEp1c3QgbWFraW5nIHN1cmUgSSB1bmRlcnN0b29kIGNvcnJlY3RseSAt
IG5vIG5lZWQgdG8gcmVzZW5kLCBJIGNhbiBmaXgNCj4gdGhhdC4NCg0KVGhhbmtzIGZvciBwb2lu
dGluZyBpdCBvdXQgYW5kIGZpeGluZy4NCg0KVmFzYW50aA0K

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-02-28  4:53 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-20 10:39 [PATCH 0/3] Pre-CAC and sharing DFS state across multiple radios Vasanthakumar Thiagarajan
2017-02-20 10:39 ` [PATCH 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain Vasanthakumar Thiagarajan
2017-02-27 14:38   ` Johannes Berg
2017-02-28  4:52     ` Thiagarajan, Vasanthakumar
2017-02-20 10:39 ` [PATCH 2/3] cfg80211: Disallow moving out of operating DFS channel in non-ETSI Vasanthakumar Thiagarajan
2017-02-20 10:39 ` [PATCH 3/3] cfg80211: Share Channel DFS state across wiphys of same DFS domain Vasanthakumar Thiagarajan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.