diff mbox

[RFC,3/4] cfg80211: implement multi-vif interface combination validation

Message ID 1389194818-7864-4-git-send-email-michal.kazior@tieto.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Michal Kazior Jan. 8, 2014, 3:26 p.m. UTC
This refactors cfg80211_can_use_iftype_chan() so
that it can process multi-interface combination
changes.

With this it will be possible to handle, e.g.
multi-BSS channel switching.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 net/wireless/core.h |  36 ++++++++++--
 net/wireless/util.c | 158 ++++++++++++++++++++++++++++++++--------------------
 2 files changed, 127 insertions(+), 67 deletions(-)

Comments

Johannes Berg Jan. 14, 2014, 4:10 p.m. UTC | #1
On Wed, 2014-01-08 at 16:26 +0100, Michal Kazior wrote:
> This refactors cfg80211_can_use_iftype_chan() so
> that it can process multi-interface combination
> changes.
> 
> With this it will be possible to handle, e.g.
> multi-BSS channel switching.

This seems reasonable, though I'd say that this code:

> +	for (i = 0; i < num_params; i++) {
> +		if (WARN_ON(hweight32(params[i].radar_detect_width) > 1))
> +			return -EINVAL;
> +
> +		/* sanity check - make sure all wdevs in params[] are unique */
> +		for (j = 0; j < num_params; j++)
> +			if (WARN_ON(i != j && params[i].wdev == params[j].wdev))
> +				return -EINVAL;

could then use some refactoring, e.g. by moving the inside of the loop
to another helper function.

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/net/wireless/core.h b/net/wireless/core.h
index 38f74fe..ee19118 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -228,6 +228,14 @@  enum cfg80211_chan_mode {
 	CHAN_MODE_EXCLUSIVE,
 };
 
+struct cfg80211_iftype_chan_param {
+	struct wireless_dev *wdev;
+	enum nl80211_iftype iftype;
+	struct ieee80211_channel *chan;
+	enum cfg80211_chan_mode chanmode;
+	u8 radar_detect_width;
+};
+
 struct cfg80211_beacon_registration {
 	struct list_head list;
 	u32 nlportid;
@@ -374,12 +382,9 @@  int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
 void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
 void cfg80211_process_wdev_events(struct wireless_dev *wdev);
 
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
-				 struct wireless_dev *wdev,
-				 enum nl80211_iftype iftype,
-				 struct ieee80211_channel *chan,
-				 enum cfg80211_chan_mode chanmode,
-				 u8 radar_detect_width);
+int cfg80211_can_use_iftype_chan_params(struct cfg80211_registered_device *rdev,
+					const struct cfg80211_iftype_chan_param *params,
+					int num_params);
 
 /**
  * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
@@ -402,6 +407,25 @@  void cfg80211_dfs_channels_update_work(struct work_struct *work);
 
 
 static inline int
+cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
+			     struct wireless_dev *wdev,
+			     enum nl80211_iftype iftype,
+			     struct ieee80211_channel *chan,
+			     enum cfg80211_chan_mode chanmode,
+			     u8 radar_detect_width)
+{
+	struct cfg80211_iftype_chan_param param = {
+		.wdev = wdev,
+		.iftype = iftype,
+		.chan = chan,
+		.chanmode = chanmode,
+		.radar_detect_width = radar_detect_width,
+	};
+
+	return cfg80211_can_use_iftype_chan_params(rdev, &param, 1);
+}
+
+static inline int
 cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
 			      struct wireless_dev *wdev,
 			      enum nl80211_iftype iftype)
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 41013f1..099f1da 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1233,89 +1233,118 @@  int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
 	return res;
 }
 
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
-				 struct wireless_dev *wdev,
-				 enum nl80211_iftype iftype,
-				 struct ieee80211_channel *chan,
-				 enum cfg80211_chan_mode chanmode,
-				 u8 radar_detect_width)
+int cfg80211_can_use_iftype_chan_params(struct cfg80211_registered_device *rdev,
+					const struct cfg80211_iftype_chan_param *params,
+					int num_params)
 {
 	struct wireless_dev *wdev_iter;
-	u32 used_iftypes = BIT(iftype);
+	u32 used_iftypes = 0;
 	int num[NUM_NL80211_IFTYPES];
 	struct ieee80211_channel
 			*used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
 	struct ieee80211_channel *ch;
 	enum cfg80211_chan_mode chmode;
 	int num_different_channels = 0;
-	int total = 1;
+	int total = num_params;
 	bool radar_required = false;
+	bool num_software_iftypes = 0;
 	int i, j;
 
 	ASSERT_RTNL();
 
-	if (WARN_ON(hweight32(radar_detect_width) > 1))
-		return -EINVAL;
+	memset(num, 0, sizeof(num));
+	memset(used_channels, 0, sizeof(used_channels));
 
-	switch (iftype) {
-	case NL80211_IFTYPE_ADHOC:
-	case NL80211_IFTYPE_AP:
-	case NL80211_IFTYPE_AP_VLAN:
-	case NL80211_IFTYPE_MESH_POINT:
-	case NL80211_IFTYPE_P2P_GO:
-	case NL80211_IFTYPE_WDS:
-		/* if the interface could potentially choose a DFS channel,
-		 * then mark DFS as required.
-		 */
-		if (!chan) {
-			if (chanmode != CHAN_MODE_UNDEFINED && radar_detect_width)
-				radar_required = true;
+	for (i = 0; i < num_params; i++) {
+		if (WARN_ON(hweight32(params[i].radar_detect_width) > 1))
+			return -EINVAL;
+
+		/* sanity check - make sure all wdevs in params[] are unique */
+		for (j = 0; j < num_params; j++)
+			if (WARN_ON(i != j && params[i].wdev == params[j].wdev))
+				return -EINVAL;
+
+		radar_required = false;
+		used_iftypes |= BIT(params[i].iftype);
+
+		switch (params[i].iftype) {
+		case NL80211_IFTYPE_ADHOC:
+		case NL80211_IFTYPE_AP:
+		case NL80211_IFTYPE_AP_VLAN:
+		case NL80211_IFTYPE_MESH_POINT:
+		case NL80211_IFTYPE_P2P_GO:
+		case NL80211_IFTYPE_WDS:
+			/* if the interface could potentially choose a DFS channel,
+			 * then mark DFS as required.
+			 */
+			if (!params[i].chan) {
+				if (params[i].chanmode != CHAN_MODE_UNDEFINED &&
+				    params[i].radar_detect_width)
+					radar_required = true;
+				break;
+			}
+			radar_required = !!(params[i].chan->flags & IEEE80211_CHAN_RADAR);
+			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
+		case NL80211_IFTYPE_STATION:
+		case NL80211_IFTYPE_P2P_DEVICE:
+		case NL80211_IFTYPE_MONITOR:
 			break;
+		case NUM_NL80211_IFTYPES:
+		case NL80211_IFTYPE_UNSPECIFIED:
+		default:
+			return -EINVAL;
 		}
-		radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
-		break;
-	case NL80211_IFTYPE_P2P_CLIENT:
-	case NL80211_IFTYPE_STATION:
-	case NL80211_IFTYPE_P2P_DEVICE:
-	case NL80211_IFTYPE_MONITOR:
-		break;
-	case NUM_NL80211_IFTYPES:
-	case NL80211_IFTYPE_UNSPECIFIED:
-	default:
-		return -EINVAL;
-	}
-
-	if (radar_required && !radar_detect_width)
-		return -EINVAL;
 
-	/* Always allow software iftypes */
-	if (rdev->wiphy.software_iftypes & BIT(iftype)) {
-		if (radar_detect_width)
+		if (radar_required && !params[i].radar_detect_width)
 			return -EINVAL;
-		return 0;
-	}
 
-	memset(num, 0, sizeof(num));
-	memset(used_channels, 0, sizeof(used_channels));
+		if (rdev->wiphy.software_iftypes & BIT(params[i].iftype)) {
+			num_software_iftypes++;
+			if (params[i].radar_detect_width)
+				return -EINVAL;
+		}
 
-	num[iftype] = 1;
+		num[params[i].iftype]++;
 
-	switch (chanmode) {
-	case CHAN_MODE_UNDEFINED:
-		break;
-	case CHAN_MODE_SHARED:
-		WARN_ON(!chan);
-		used_channels[0] = chan;
-		num_different_channels++;
-		break;
-	case CHAN_MODE_EXCLUSIVE:
-		num_different_channels++;
-		break;
+		switch (params[i].chanmode) {
+		case CHAN_MODE_UNDEFINED:
+			break;
+		case CHAN_MODE_SHARED:
+			if (WARN_ON(!params[i].chan))
+				return -EINVAL;
+
+			for (j = 0; j < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; j++)
+				if (!used_channels[j] ||
+				    used_channels[j] == params[i].chan)
+					break;
+
+			if (j == CFG80211_MAX_NUM_DIFFERENT_CHANNELS)
+				return -EBUSY;
+
+			if (used_channels[j] == NULL) {
+				used_channels[j] = params[i].chan;
+				num_different_channels++;
+			}
+			break;
+		case CHAN_MODE_EXCLUSIVE:
+			num_different_channels++;
+			break;
+		}
 	}
 
+	/* Always allow software iftypes */
+	if (num_params == num_software_iftypes)
+		return 0;
+
 	list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
-		if (wdev_iter == wdev)
+		/* skip wdevs which are in params[] */
+		for (i = 0; i < num_params; i++)
+			if (wdev_iter == params[i].wdev)
+				break;
+		if (i < num_params)
 			continue;
+
 		if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) {
 			if (!wdev_iter->p2p_started)
 				continue;
@@ -1366,12 +1395,13 @@  int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 		used_iftypes |= BIT(wdev_iter->iftype);
 	}
 
-	if (total == 1 && !radar_detect_width)
+	if (total == 1 && num_params == 1 && !params[0].radar_detect_width)
 		return 0;
 
 	for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
 		const struct ieee80211_iface_combination *c;
 		struct ieee80211_iface_limit *limits;
+		enum nl80211_iftype iftype;
 		u32 all_iftypes = 0;
 
 		c = &rdev->wiphy.iface_combinations[i];
@@ -1399,8 +1429,14 @@  int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 			}
 		}
 
-		if (radar_detect_width && !(c->radar_detect_widths & radar_detect_width))
-			goto cont;
+		for (j = 0; j < num_params; j++) {
+			if (!params[j].radar_detect_width)
+				continue;
+
+			if (!(c->radar_detect_widths &
+			      params[j].radar_detect_width))
+				goto cont;
+		}
 
 		/*
 		 * Finally check that all iftypes that we're currently