linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/6] "runtime" iftype switching
@ 2010-08-25 20:56 Johannes Berg
  2010-08-25 20:56 ` [RFC 1/6] mac80211: clean up ifdown/cleanup paths Johannes Berg
                   ` (6 more replies)
  0 siblings, 7 replies; 11+ messages in thread
From: Johannes Berg @ 2010-08-25 20:56 UTC (permalink / raw)
  To: linux-wireless

Here's some code to allow runtime (while the
interface is UP) interface type switching,
so far only for STA, IBSS and AP because the
others seem a bit trickier and harder to
verify.

Comments welcome. Not tested with HW yet :)

johannes


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

* [RFC 1/6] mac80211: clean up ifdown/cleanup paths
  2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
@ 2010-08-25 20:56 ` Johannes Berg
  2010-08-25 20:56 ` [RFC 2/6] mac80211: switch to ieee80211_sdata_running Johannes Berg
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2010-08-25 20:56 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

There's a lot of redundant code in mac80211's
interface cleanup/down, for example freeing
AP beacons is done both when the interface is
set DOWN as well as when it is torn down, of
which only the former has any effect.

Also, a bunch of things should be closer to
where they matter, like the MLME timers that
we should cancel when disassociating, rather
than only when the interface is set DOWN.

Clean up all this code.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/iface.c |   89 +++++++++++++--------------------------------------
 net/mac80211/mlme.c  |    5 ++
 2 files changed, 28 insertions(+), 66 deletions(-)

--- wireless-testing.orig/net/mac80211/iface.c	2010-08-25 21:58:52.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2010-08-25 21:59:11.000000000 +0200
@@ -370,12 +370,9 @@ static int ieee80211_stop(struct net_dev
 	 * (because if we remove a STA after ops->remove_interface()
 	 * the driver will have removed the vif info already!)
 	 *
-	 * We could relax this and only unlink the stations from the
-	 * hash table and list but keep them on a per-sdata list that
-	 * will be inserted back again when the interface is brought
-	 * up again, but I don't currently see a use case for that,
-	 * except with WDS which gets a STA entry created when it is
-	 * brought up.
+	 * This is relevant only in AP, WDS and mesh modes, since in
+	 * all other modes we've already removed all stations when
+	 * disconnecting etc.
 	 */
 	sta_info_flush(local, sdata);
 
@@ -410,11 +407,21 @@ static int ieee80211_stop(struct net_dev
 		struct ieee80211_sub_if_data *vlan, *tmpsdata;
 		struct beacon_data *old_beacon = sdata->u.ap.beacon;
 
+		/* sdata_running will return false, so this will disable */
+		ieee80211_bss_info_change_notify(sdata,
+						 BSS_CHANGED_BEACON_ENABLED);
+
 		/* remove beacon */
 		rcu_assign_pointer(sdata->u.ap.beacon, NULL);
 		synchronize_rcu();
 		kfree(old_beacon);
 
+		/* free all potentially still buffered bcast frames */
+		while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
+			local->total_ps_buffered--;
+			dev_kfree_skb(skb);
+		}
+
 		/* down all dependent devices, that is VLANs */
 		list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
 					 u.vlan.list)
@@ -454,27 +461,6 @@ static int ieee80211_stop(struct net_dev
 
 		ieee80211_configure_filter(local);
 		break;
-	case NL80211_IFTYPE_STATION:
-		del_timer_sync(&sdata->u.mgd.chswitch_timer);
-		del_timer_sync(&sdata->u.mgd.timer);
-		del_timer_sync(&sdata->u.mgd.conn_mon_timer);
-		del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
-		/*
-		 * If any of the timers fired while we waited for it, it will
-		 * have queued its work. Now the work will be running again
-		 * but will not rearm the timer again because it checks
-		 * whether the interface is running, which, at this point,
-		 * it no longer is.
-		 */
-		cancel_work_sync(&sdata->u.mgd.chswitch_work);
-		cancel_work_sync(&sdata->u.mgd.monitor_work);
-		cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);
-
-		/* fall through */
-	case NL80211_IFTYPE_ADHOC:
-		if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
-			del_timer_sync(&sdata->u.ibss.timer);
-		/* fall through */
 	case NL80211_IFTYPE_MESH_POINT:
 		if (ieee80211_vif_is_mesh(&sdata->vif)) {
 			/* other_bss and allmulti are always set on mesh
@@ -502,17 +488,19 @@ static int ieee80211_stop(struct net_dev
 			ieee80211_scan_cancel(local);
 
 		/*
-		 * Disable beaconing for AP and mesh, IBSS can't
-		 * still be joined to a network at this point.
+		 * Disable beaconing here for mesh only, AP and IBSS
+		 * are already taken care of.
 		 */
-		if (sdata->vif.type == NL80211_IFTYPE_AP ||
-		    sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+		if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
 			ieee80211_bss_info_change_notify(sdata,
 				BSS_CHANGED_BEACON_ENABLED);
-		}
 
-		/* free all remaining keys, there shouldn't be any */
+		/*
+		 * Free all remaining keys, there shouldn't be any,
+		 * except maybe group keys in AP more or WDS?
+		 */
 		ieee80211_free_keys(sdata);
+
 		drv_remove_interface(local, &sdata->vif);
 	}
 
@@ -593,8 +581,6 @@ static void ieee80211_teardown_sdata(str
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = sdata->local;
-	struct beacon_data *beacon;
-	struct sk_buff *skb;
 	int flushed;
 	int i;
 
@@ -607,37 +593,8 @@ static void ieee80211_teardown_sdata(str
 		__skb_queue_purge(&sdata->fragments[i].skb_list);
 	sdata->fragment_next = 0;
 
-	switch (sdata->vif.type) {
-	case NL80211_IFTYPE_AP:
-		beacon = sdata->u.ap.beacon;
-		rcu_assign_pointer(sdata->u.ap.beacon, NULL);
-		synchronize_rcu();
-		kfree(beacon);
-
-		while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
-			local->total_ps_buffered--;
-			dev_kfree_skb(skb);
-		}
-
-		break;
-	case NL80211_IFTYPE_MESH_POINT:
-		if (ieee80211_vif_is_mesh(&sdata->vif))
-			mesh_rmc_free(sdata);
-		break;
-	case NL80211_IFTYPE_ADHOC:
-		if (WARN_ON(sdata->u.ibss.presp))
-			kfree_skb(sdata->u.ibss.presp);
-		break;
-	case NL80211_IFTYPE_STATION:
-	case NL80211_IFTYPE_WDS:
-	case NL80211_IFTYPE_AP_VLAN:
-	case NL80211_IFTYPE_MONITOR:
-		break;
-	case NL80211_IFTYPE_UNSPECIFIED:
-	case NUM_NL80211_IFTYPES:
-		BUG();
-		break;
-	}
+	if (ieee80211_vif_is_mesh(&sdata->vif))
+		mesh_rmc_free(sdata);
 
 	flushed = sta_info_flush(local, sdata);
 	WARN_ON(flushed);
--- wireless-testing.orig/net/mac80211/mlme.c	2010-08-25 21:58:52.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2010-08-25 21:58:56.000000000 +0200
@@ -990,6 +990,11 @@ static void ieee80211_set_disassoc(struc
 
 	if (remove_sta)
 		sta_info_destroy_addr(sdata, bssid);
+
+	del_timer_sync(&sdata->u.mgd.conn_mon_timer);
+	del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
+	del_timer_sync(&sdata->u.mgd.timer);
+	del_timer_sync(&sdata->u.mgd.chswitch_timer);
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,



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

* [RFC 2/6] mac80211: switch to ieee80211_sdata_running
  2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
  2010-08-25 20:56 ` [RFC 1/6] mac80211: clean up ifdown/cleanup paths Johannes Berg
@ 2010-08-25 20:56 ` Johannes Berg
  2010-08-25 20:56 ` [RFC 3/6] mac80211: simplify zero address checks Johannes Berg
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2010-08-25 20:56 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Since the introduction of ieee80211_sdata_running(),
some new code was introduced that uses netif_running()
instead. Switch all these instances over.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/cfg.c  |    6 ++----
 net/mac80211/main.c |    6 +++---
 net/mac80211/util.c |    2 +-
 3 files changed, 6 insertions(+), 8 deletions(-)

--- wireless-testing.orig/net/mac80211/cfg.c	2010-08-25 21:59:45.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c	2010-08-25 22:01:07.000000000 +0200
@@ -81,16 +81,14 @@ static int ieee80211_add_key(struct wiph
 			     u8 key_idx, const u8 *mac_addr,
 			     struct key_params *params)
 {
-	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct sta_info *sta = NULL;
 	struct ieee80211_key *key;
 	int err;
 
-	if (!netif_running(dev))
+	if (!ieee80211_sdata_running(sdata))
 		return -ENETDOWN;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
 	/* reject WEP and TKIP keys if WEP failed to initialize */
 	switch (params->cipher) {
 	case WLAN_CIPHER_SUITE_WEP40:
--- wireless-testing.orig/net/mac80211/main.c	2010-08-25 21:59:57.000000000 +0200
+++ wireless-testing/net/mac80211/main.c	2010-08-25 22:01:25.000000000 +0200
@@ -339,9 +339,6 @@ static int ieee80211_ifa_changed(struct
 	struct ieee80211_if_managed *ifmgd;
 	int c = 0;
 
-	if (!netif_running(ndev))
-		return NOTIFY_DONE;
-
 	/* Make sure it's our interface that got changed */
 	if (!wdev)
 		return NOTIFY_DONE;
@@ -352,6 +349,9 @@ static int ieee80211_ifa_changed(struct
 	sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
 	bss_conf = &sdata->vif.bss_conf;
 
+	if (!ieee80211_sdata_running(sdata))
+		return NOTIFY_DONE;
+
 	/* ARP filtering is only supported in managed mode */
 	if (sdata->vif.type != NL80211_IFTYPE_STATION)
 		return NOTIFY_DONE;
--- wireless-testing.orig/net/mac80211/util.c	2010-08-25 22:00:15.000000000 +0200
+++ wireless-testing/net/mac80211/util.c	2010-08-25 22:00:28.000000000 +0200
@@ -1308,7 +1308,7 @@ void ieee80211_recalc_smps(struct ieee80
 	 */
 
 	list_for_each_entry(sdata, &local->interfaces, list) {
-		if (!netif_running(sdata->dev))
+		if (!ieee80211_sdata_running(sdata))
 			continue;
 		if (sdata->vif.type != NL80211_IFTYPE_STATION)
 			goto set;



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

* [RFC 3/6] mac80211: simplify zero address checks
  2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
  2010-08-25 20:56 ` [RFC 1/6] mac80211: clean up ifdown/cleanup paths Johannes Berg
  2010-08-25 20:56 ` [RFC 2/6] mac80211: switch to ieee80211_sdata_running Johannes Berg
@ 2010-08-25 20:56 ` Johannes Berg
  2010-08-25 20:56 ` [RFC 4/6] mac80211: split out concurrent vif checks Johannes Berg
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2010-08-25 20:56 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

The libertas_tf special code for zero addresses
is a bit too complex, it compares against a stack
value instead of using is_zero_ether_addr() and
tries to update all interfaces even if just the
one that's being brought up needs to be changed.
Additionally, the repeated check for a valid MAC
address need only be done if we actually changed
it on the fly.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/iface.c |   38 +++++++++++++-------------------------
 1 file changed, 13 insertions(+), 25 deletions(-)

--- wireless-testing.orig/net/mac80211/iface.c	2010-08-25 21:59:11.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2010-08-25 22:01:50.000000000 +0200
@@ -103,10 +103,9 @@ static int ieee80211_open(struct net_dev
 	u32 changed = 0;
 	int res;
 	u32 hw_reconf_flags = 0;
-	u8 null_addr[ETH_ALEN] = {0};
 
 	/* fail early if user set an invalid address */
-	if (compare_ether_addr(dev->dev_addr, null_addr) &&
+	if (!is_zero_ether_addr(dev->dev_addr) &&
 	    !is_valid_ether_addr(dev->dev_addr))
 		return -EADDRNOTAVAIL;
 
@@ -195,33 +194,22 @@ static int ieee80211_open(struct net_dev
 	}
 
 	/*
-	 * Check all interfaces and copy the hopefully now-present
-	 * MAC address to those that have the special null one.
+	 * Copy the hopefully now-present MAC address to
+	 * this interface, if it has the special null one.
 	 */
-	list_for_each_entry(nsdata, &local->interfaces, list) {
-		struct net_device *ndev = nsdata->dev;
-
-		/*
-		 * No need to check running since we do not allow
-		 * it to start up with this invalid address.
-		 */
-		if (compare_ether_addr(null_addr, ndev->dev_addr) == 0) {
-			memcpy(ndev->dev_addr,
-			       local->hw.wiphy->perm_addr,
-			       ETH_ALEN);
-			memcpy(ndev->perm_addr, ndev->dev_addr, ETH_ALEN);
+	if (is_zero_ether_addr(dev->dev_addr)) {
+		memcpy(dev->dev_addr,
+		       local->hw.wiphy->perm_addr,
+		       ETH_ALEN);
+		memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);
+
+		if (!is_valid_ether_addr(dev->dev_addr)) {
+			if (!local->open_count)
+				drv_stop(local);
+			return -EADDRNOTAVAIL;
 		}
 	}
 
-	/*
-	 * Validate the MAC address for this device.
-	 */
-	if (!is_valid_ether_addr(dev->dev_addr)) {
-		if (!local->open_count)
-			drv_stop(local);
-		return -EADDRNOTAVAIL;
-	}
-
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_AP_VLAN:
 		/* no need to tell driver */



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

* [RFC 4/6] mac80211: split out concurrent vif checks
  2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
                   ` (2 preceding siblings ...)
  2010-08-25 20:56 ` [RFC 3/6] mac80211: simplify zero address checks Johannes Berg
@ 2010-08-25 20:56 ` Johannes Berg
  2010-08-25 20:56 ` [RFC 5/6] mac80211: support runtime interface type changes Johannes Berg
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2010-08-25 20:56 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Split the concurrent virtual interface checks
into a new function that can be used to check
for any given new interface type.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/iface.c |   43 +++++++++++++++++++++++++++++--------------
 1 file changed, 29 insertions(+), 14 deletions(-)

--- wireless-testing.orig/net/mac80211/iface.c	2010-08-25 22:01:50.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2010-08-25 22:38:19.000000000 +0200
@@ -94,20 +94,14 @@ static inline int identical_mac_addr_all
 			 type2 == NL80211_IFTYPE_AP_VLAN));
 }
 
-static int ieee80211_open(struct net_device *dev)
+static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
+					    enum nl80211_iftype iftype)
 {
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct ieee80211_sub_if_data *nsdata;
 	struct ieee80211_local *local = sdata->local;
-	struct sta_info *sta;
-	u32 changed = 0;
-	int res;
-	u32 hw_reconf_flags = 0;
+	struct ieee80211_sub_if_data *nsdata;
+	struct net_device *dev = sdata->dev;
 
-	/* fail early if user set an invalid address */
-	if (!is_zero_ether_addr(dev->dev_addr) &&
-	    !is_valid_ether_addr(dev->dev_addr))
-		return -EADDRNOTAVAIL;
+	ASSERT_RTNL();
 
 	/* we hold the RTNL here so can safely walk the list */
 	list_for_each_entry(nsdata, &local->interfaces, list) {
@@ -124,7 +118,7 @@ static int ieee80211_open(struct net_dev
 			 * belonging to the same hardware. Then, however, we're
 			 * faced with having to adopt two different TSF timers...
 			 */
-			if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+			if (iftype == NL80211_IFTYPE_ADHOC &&
 			    nsdata->vif.type == NL80211_IFTYPE_ADHOC)
 				return -EBUSY;
 
@@ -138,19 +132,40 @@ static int ieee80211_open(struct net_dev
 			/*
 			 * check whether it may have the same address
 			 */
-			if (!identical_mac_addr_allowed(sdata->vif.type,
+			if (!identical_mac_addr_allowed(iftype,
 							nsdata->vif.type))
 				return -ENOTUNIQ;
 
 			/*
 			 * can only add VLANs to enabled APs
 			 */
-			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+			if (iftype == NL80211_IFTYPE_AP_VLAN &&
 			    nsdata->vif.type == NL80211_IFTYPE_AP)
 				sdata->bss = &nsdata->u.ap;
 		}
 	}
 
+	return 0;
+}
+
+static int ieee80211_open(struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
+	struct sta_info *sta;
+	u32 changed = 0;
+	int res;
+	u32 hw_reconf_flags = 0;
+
+	/* fail early if user set an invalid address */
+	if (!is_zero_ether_addr(dev->dev_addr) &&
+	    !is_valid_ether_addr(dev->dev_addr))
+		return -EADDRNOTAVAIL;
+
+	res = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
+	if (res)
+		return res;
+
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_WDS:
 		if (!is_valid_ether_addr(sdata->u.wds.remote_addr))



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

* [RFC 5/6] mac80211: support runtime interface type changes
  2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
                   ` (3 preceding siblings ...)
  2010-08-25 20:56 ` [RFC 4/6] mac80211: split out concurrent vif checks Johannes Berg
@ 2010-08-25 20:56 ` Johannes Berg
  2010-08-26 14:22   ` [RFC 5/6 v2] " Johannes Berg
  2010-08-25 20:56 ` [RFC 6/6] mac80211_hwsim: support runtime iftype changes Johannes Berg
  2010-08-25 21:10 ` [RFC 0/6] "runtime" iftype switching John W. Linville
  6 siblings, 1 reply; 11+ messages in thread
From: Johannes Berg @ 2010-08-25 20:56 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Add support to mac80211 for changing the interface
type even when the interface is UP, if the driver
supports it.

To achieve this
 * add a new driver callback for switching,
 * split some of the interface up/down code out
   into new functions (do_open/do_stop), and
 * maintain an own __SDATA_RUNNING bit that will
   not be set during interface type, so that any
   other code doesn't use the interface.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/mac80211.h     |    9 ++
 net/mac80211/cfg.c         |    3 
 net/mac80211/ieee80211_i.h |    6 +
 net/mac80211/iface.c       |  146 ++++++++++++++++++++++++++++++++++-----------
 4 files changed, 127 insertions(+), 37 deletions(-)

--- wireless-testing.orig/include/net/mac80211.h	2010-08-25 22:38:19.000000000 +0200
+++ wireless-testing/include/net/mac80211.h	2010-08-25 22:40:04.000000000 +0200
@@ -1537,6 +1537,12 @@ enum ieee80211_ampdu_mlme_action {
  *	negative error code (which will be seen in userspace.)
  *	Must be implemented and can sleep.
  *
+ * @change_interface: Called when a netdevice changes type. This callback
+ *	is optional, but only if it is supported can interface types be
+ *	switched while the interface is UP. The callback may sleep.
+ *	Note that while an interface is being switched, it will not be
+ *	found by the interface iteration callbacks.
+ *
  * @remove_interface: Notifies a driver that an interface is going down.
  *	The @stop callback is called after this if it is the last interface
  *	and no monitor interfaces are present.
@@ -1693,6 +1699,9 @@ struct ieee80211_ops {
 	void (*stop)(struct ieee80211_hw *hw);
 	int (*add_interface)(struct ieee80211_hw *hw,
 			     struct ieee80211_vif *vif);
+	int (*change_interface)(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				enum nl80211_iftype new_type);
 	void (*remove_interface)(struct ieee80211_hw *hw,
 				 struct ieee80211_vif *vif);
 	int (*config)(struct ieee80211_hw *hw, u32 changed);
--- wireless-testing.orig/net/mac80211/iface.c	2010-08-25 22:38:19.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2010-08-25 22:51:54.000000000 +0200
@@ -148,7 +148,12 @@ static int ieee80211_check_concurrent_if
 	return 0;
 }
 
-static int ieee80211_open(struct net_device *dev)
+/*
+ * NOTE: Be very careful when changing this function, it must NOT return
+ * an error on interface type changes that have been pre-checked, so most
+ * checks should be in ieee80211_check_concurrent_iface.
+ */
+static int ieee80211_do_open(struct net_device *dev, bool coming_up)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = sdata->local;
@@ -157,15 +162,6 @@ static int ieee80211_open(struct net_dev
 	int res;
 	u32 hw_reconf_flags = 0;
 
-	/* fail early if user set an invalid address */
-	if (!is_zero_ether_addr(dev->dev_addr) &&
-	    !is_valid_ether_addr(dev->dev_addr))
-		return -EADDRNOTAVAIL;
-
-	res = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
-	if (res)
-		return res;
-
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_WDS:
 		if (!is_valid_ether_addr(sdata->u.wds.remote_addr))
@@ -258,9 +254,11 @@ static int ieee80211_open(struct net_dev
 		netif_carrier_on(dev);
 		break;
 	default:
-		res = drv_add_interface(local, &sdata->vif);
-		if (res)
-			goto err_stop;
+		if (coming_up) {
+			res = drv_add_interface(local, &sdata->vif);
+			if (res)
+				goto err_stop;
+		}
 
 		if (ieee80211_vif_is_mesh(&sdata->vif)) {
 			local->fif_other_bss++;
@@ -316,7 +314,9 @@ static int ieee80211_open(struct net_dev
 	hw_reconf_flags |= __ieee80211_recalc_idle(local);
 	mutex_unlock(&local->mtx);
 
-	local->open_count++;
+	if (coming_up)
+		local->open_count++;
+
 	if (hw_reconf_flags) {
 		ieee80211_hw_config(local, hw_reconf_flags);
 		/*
@@ -331,6 +331,8 @@ static int ieee80211_open(struct net_dev
 
 	netif_tx_start_all_queues(dev);
 
+	set_bit(__SDATA_RUNNING, &sdata->state);
+
 	return 0;
  err_del_interface:
 	drv_remove_interface(local, &sdata->vif);
@@ -344,19 +346,38 @@ static int ieee80211_open(struct net_dev
 	return res;
 }
 
-static int ieee80211_stop(struct net_device *dev)
+static int ieee80211_open(struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	int err;
+
+	/* fail early if user set an invalid address */
+	if (!is_zero_ether_addr(dev->dev_addr) &&
+	    !is_valid_ether_addr(dev->dev_addr))
+		return -EADDRNOTAVAIL;
+
+	err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
+	if (err)
+		return err;
+
+	return ieee80211_do_open(dev, true);
+}
+
+static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
+			      bool going_down)
+{
 	struct ieee80211_local *local = sdata->local;
 	unsigned long flags;
 	struct sk_buff *skb, *tmp;
 	u32 hw_reconf_flags = 0;
 	int i;
 
+	clear_bit(__SDATA_RUNNING, &sdata->state);
+
 	/*
 	 * Stop TX on this interface first.
 	 */
-	netif_tx_stop_all_queues(dev);
+	netif_tx_stop_all_queues(sdata->dev);
 
 	/*
 	 * Purge work for this interface.
@@ -394,11 +415,12 @@ static int ieee80211_stop(struct net_dev
 	if (sdata->vif.type == NL80211_IFTYPE_AP)
 		local->fif_pspoll--;
 
-	netif_addr_lock_bh(dev);
+	netif_addr_lock_bh(sdata->dev);
 	spin_lock_bh(&local->filter_lock);
-	__hw_addr_unsync(&local->mc_list, &dev->mc, dev->addr_len);
+	__hw_addr_unsync(&local->mc_list, &sdata->dev->mc,
+			 sdata->dev->addr_len);
 	spin_unlock_bh(&local->filter_lock);
-	netif_addr_unlock_bh(dev);
+	netif_addr_unlock_bh(sdata->dev);
 
 	ieee80211_configure_filter(local);
 
@@ -432,7 +454,8 @@ static int ieee80211_stop(struct net_dev
 		WARN_ON(!list_empty(&sdata->u.ap.vlans));
 	}
 
-	local->open_count--;
+	if (going_down)
+		local->open_count--;
 
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_AP_VLAN:
@@ -504,7 +527,8 @@ static int ieee80211_stop(struct net_dev
 		 */
 		ieee80211_free_keys(sdata);
 
-		drv_remove_interface(local, &sdata->vif);
+		if (going_down)
+			drv_remove_interface(local, &sdata->vif);
 	}
 
 	sdata->bss = NULL;
@@ -540,6 +564,13 @@ static int ieee80211_stop(struct net_dev
 		}
 	}
 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+static int ieee80211_stop(struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	ieee80211_do_stop(sdata, true);
 
 	return 0;
 }
@@ -854,9 +885,61 @@ static void ieee80211_setup_sdata(struct
 	ieee80211_debugfs_add_netdev(sdata);
 }
 
+static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
+					   enum nl80211_iftype type)
+{
+	struct ieee80211_local *local = sdata->local;
+	int ret;
+
+	ASSERT_RTNL();
+
+	if (!local->ops->change_interface)
+		return -EBUSY;
+
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		/* cfg80211 has already disconnected us */
+		break;
+	default:
+		return -EBUSY;
+	}
+
+	switch (type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		break;
+	default:
+		return -EBUSY;
+	}
+
+	ret = ieee80211_check_concurrent_iface(sdata, type);
+	if (ret)
+		return ret;
+
+	ieee80211_do_stop(sdata, false);
+
+	ieee80211_teardown_sdata(sdata->dev);
+
+	ret = local->ops->change_interface(&local->hw, &sdata->vif, type);
+	if (ret)
+		type = sdata->vif.type;
+
+	ieee80211_setup_sdata(sdata, type);
+
+	ret = ieee80211_do_open(sdata->dev, false);
+	WARN_ON(ret);
+
+	return ret;
+}
+
 int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
 			     enum nl80211_iftype type)
 {
+	int ret;
+
 	ASSERT_RTNL();
 
 	if (type == sdata->vif.type)
@@ -867,18 +950,15 @@ int ieee80211_if_change_type(struct ieee
 	    type == NL80211_IFTYPE_ADHOC)
 		return -EOPNOTSUPP;
 
-	/*
-	 * We could, here, on changes between IBSS/STA/MESH modes,
-	 * invoke an MLME function instead that disassociates etc.
-	 * and goes into the requested mode.
-	 */
-
-	if (ieee80211_sdata_running(sdata))
-		return -EBUSY;
-
-	/* Purge and reset type-dependent state. */
-	ieee80211_teardown_sdata(sdata->dev);
-	ieee80211_setup_sdata(sdata, type);
+	if (ieee80211_sdata_running(sdata)) {
+		ret = ieee80211_runtime_change_iftype(sdata, type);
+		if (ret)
+			return ret;
+	} else {
+		/* Purge and reset type-dependent state. */
+		ieee80211_teardown_sdata(sdata->dev);
+		ieee80211_setup_sdata(sdata, type);
+	}
 
 	/* reset some values that shouldn't be kept across type changes */
 	sdata->vif.bss_conf.basic_rates =
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2010-08-25 22:38:19.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h	2010-08-25 22:40:04.000000000 +0200
@@ -478,6 +478,8 @@ enum ieee80211_sub_if_data_flags {
 	IEEE80211_SDATA_DONT_BRIDGE_PACKETS	= BIT(3),
 };
 
+#define __SDATA_RUNNING		0
+
 struct ieee80211_sub_if_data {
 	struct list_head list;
 
@@ -491,6 +493,8 @@ struct ieee80211_sub_if_data {
 
 	unsigned int flags;
 
+	unsigned long state;
+
 	int drop_unencrypted;
 
 	char name[IFNAMSIZ];
@@ -1083,7 +1087,7 @@ void ieee80211_recalc_idle(struct ieee80
 
 static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
 {
-	return netif_running(sdata->dev);
+	return test_bit(__SDATA_RUNNING, &sdata->state);
 }
 
 /* tx handling */
--- wireless-testing.orig/net/mac80211/cfg.c	2010-08-25 22:49:56.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c	2010-08-25 22:49:58.000000000 +0200
@@ -52,9 +52,6 @@ static int ieee80211_change_iface(struct
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	int ret;
 
-	if (ieee80211_sdata_running(sdata))
-		return -EBUSY;
-
 	ret = ieee80211_if_change_type(sdata, type);
 	if (ret)
 		return ret;



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

* [RFC 6/6] mac80211_hwsim: support runtime iftype changes
  2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
                   ` (4 preceding siblings ...)
  2010-08-25 20:56 ` [RFC 5/6] mac80211: support runtime interface type changes Johannes Berg
@ 2010-08-25 20:56 ` Johannes Berg
  2010-08-25 21:10 ` [RFC 0/6] "runtime" iftype switching John W. Linville
  6 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2010-08-25 20:56 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Add the trivial support for runtime interface
type changes to mac80211_hwsim for testing.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 drivers/net/wireless/mac80211_hwsim.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)

--- wireless-testing.orig/drivers/net/wireless/mac80211_hwsim.c	2010-08-25 22:38:05.000000000 +0200
+++ wireless-testing/drivers/net/wireless/mac80211_hwsim.c	2010-08-25 22:45:07.000000000 +0200
@@ -601,6 +601,18 @@ static int mac80211_hwsim_add_interface(
 }
 
 
+static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw,
+					   struct ieee80211_vif *vif,
+					   enum nl80211_iftype newtype)
+{
+	wiphy_debug(hw->wiphy,
+		    "%s (old type=%d, new type=%d, mac_addr=%pM)\n",
+		    __func__, vif->type, newtype, vif->addr);
+	hwsim_check_magic(vif);
+
+	return 0;
+}
+
 static void mac80211_hwsim_remove_interface(
 	struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
@@ -1027,6 +1039,7 @@ static struct ieee80211_ops mac80211_hws
 	.start = mac80211_hwsim_start,
 	.stop = mac80211_hwsim_stop,
 	.add_interface = mac80211_hwsim_add_interface,
+	.change_interface = mac80211_hwsim_change_interface,
 	.remove_interface = mac80211_hwsim_remove_interface,
 	.config = mac80211_hwsim_config,
 	.configure_filter = mac80211_hwsim_configure_filter,



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

* Re: [RFC 0/6] "runtime" iftype switching
  2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
                   ` (5 preceding siblings ...)
  2010-08-25 20:56 ` [RFC 6/6] mac80211_hwsim: support runtime iftype changes Johannes Berg
@ 2010-08-25 21:10 ` John W. Linville
  2010-08-26  7:23   ` Johannes Berg
  6 siblings, 1 reply; 11+ messages in thread
From: John W. Linville @ 2010-08-25 21:10 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

On Wed, Aug 25, 2010 at 10:56:19PM +0200, Johannes Berg wrote:
> Here's some code to allow runtime (while the
> interface is UP) interface type switching,
> so far only for STA, IBSS and AP because the
> others seem a bit trickier and harder to
> verify.
> 
> Comments welcome. Not tested with HW yet :)

What is driving this?

-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

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

* Re: [RFC 0/6] "runtime" iftype switching
  2010-08-25 21:10 ` [RFC 0/6] "runtime" iftype switching John W. Linville
@ 2010-08-26  7:23   ` Johannes Berg
  2010-08-26 11:58     ` John W. Linville
  0 siblings, 1 reply; 11+ messages in thread
From: Johannes Berg @ 2010-08-26  7:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless

On Wed, 2010-08-25 at 17:10 -0400, John W. Linville wrote:
> On Wed, Aug 25, 2010 at 10:56:19PM +0200, Johannes Berg wrote:
> > Here's some code to allow runtime (while the
> > interface is UP) interface type switching,
> > so far only for STA, IBSS and AP because the
> > others seem a bit trickier and harder to
> > verify.

> What is driving this?

Mostly the fact that it's awkward to set the interface down every time,
because for example every time you do that some IPv6 configuration gets
lost.

Also, "because I can" :)

johannes



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

* Re: [RFC 0/6] "runtime" iftype switching
  2010-08-26  7:23   ` Johannes Berg
@ 2010-08-26 11:58     ` John W. Linville
  0 siblings, 0 replies; 11+ messages in thread
From: John W. Linville @ 2010-08-26 11:58 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

On Thu, Aug 26, 2010 at 09:23:20AM +0200, Johannes Berg wrote:
> On Wed, 2010-08-25 at 17:10 -0400, John W. Linville wrote:
> > On Wed, Aug 25, 2010 at 10:56:19PM +0200, Johannes Berg wrote:
> > > Here's some code to allow runtime (while the
> > > interface is UP) interface type switching,
> > > so far only for STA, IBSS and AP because the
> > > others seem a bit trickier and harder to
> > > verify.
> 
> > What is driving this?
> 
> Mostly the fact that it's awkward to set the interface down every time,
> because for example every time you do that some IPv6 configuration gets
> lost.
> 
> Also, "because I can" :)

Fair enough... :-)

-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

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

* [RFC 5/6 v2] mac80211: support runtime interface type changes
  2010-08-25 20:56 ` [RFC 5/6] mac80211: support runtime interface type changes Johannes Berg
@ 2010-08-26 14:22   ` Johannes Berg
  0 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2010-08-26 14:22 UTC (permalink / raw)
  To: linux-wireless

From: Johannes Berg <johannes.berg@intel.com>

Add support to mac80211 for changing the interface
type even when the interface is UP, if the driver
supports it.

To achieve this
 * add a new driver callback for switching,
 * split some of the interface up/down code out
   into new functions (do_open/do_stop), and
 * maintain an own __SDATA_RUNNING bit that will
   not be set during interface type, so that any
   other code doesn't use the interface.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v2: - bug fix in error case
    - tracing

 include/net/mac80211.h      |    9 ++
 net/mac80211/cfg.c          |    3 
 net/mac80211/driver-ops.h   |   14 ++++
 net/mac80211/driver-trace.h |   25 +++++++
 net/mac80211/ieee80211_i.h  |    6 +
 net/mac80211/iface.c        |  145 +++++++++++++++++++++++++++++++++-----------
 6 files changed, 165 insertions(+), 37 deletions(-)

--- wireless-testing.orig/include/net/mac80211.h	2010-08-26 16:00:29.000000000 +0200
+++ wireless-testing/include/net/mac80211.h	2010-08-26 16:07:12.000000000 +0200
@@ -1537,6 +1537,12 @@ enum ieee80211_ampdu_mlme_action {
  *	negative error code (which will be seen in userspace.)
  *	Must be implemented and can sleep.
  *
+ * @change_interface: Called when a netdevice changes type. This callback
+ *	is optional, but only if it is supported can interface types be
+ *	switched while the interface is UP. The callback may sleep.
+ *	Note that while an interface is being switched, it will not be
+ *	found by the interface iteration callbacks.
+ *
  * @remove_interface: Notifies a driver that an interface is going down.
  *	The @stop callback is called after this if it is the last interface
  *	and no monitor interfaces are present.
@@ -1693,6 +1699,9 @@ struct ieee80211_ops {
 	void (*stop)(struct ieee80211_hw *hw);
 	int (*add_interface)(struct ieee80211_hw *hw,
 			     struct ieee80211_vif *vif);
+	int (*change_interface)(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				enum nl80211_iftype new_type);
 	void (*remove_interface)(struct ieee80211_hw *hw,
 				 struct ieee80211_vif *vif);
 	int (*config)(struct ieee80211_hw *hw, u32 changed);
--- wireless-testing.orig/net/mac80211/iface.c	2010-08-26 16:00:31.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2010-08-26 16:07:13.000000000 +0200
@@ -148,7 +148,12 @@ static int ieee80211_check_concurrent_if
 	return 0;
 }
 
-static int ieee80211_open(struct net_device *dev)
+/*
+ * NOTE: Be very careful when changing this function, it must NOT return
+ * an error on interface type changes that have been pre-checked, so most
+ * checks should be in ieee80211_check_concurrent_iface.
+ */
+static int ieee80211_do_open(struct net_device *dev, bool coming_up)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = sdata->local;
@@ -157,15 +162,6 @@ static int ieee80211_open(struct net_dev
 	int res;
 	u32 hw_reconf_flags = 0;
 
-	/* fail early if user set an invalid address */
-	if (!is_zero_ether_addr(dev->dev_addr) &&
-	    !is_valid_ether_addr(dev->dev_addr))
-		return -EADDRNOTAVAIL;
-
-	res = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
-	if (res)
-		return res;
-
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_WDS:
 		if (!is_valid_ether_addr(sdata->u.wds.remote_addr))
@@ -258,9 +254,11 @@ static int ieee80211_open(struct net_dev
 		netif_carrier_on(dev);
 		break;
 	default:
-		res = drv_add_interface(local, &sdata->vif);
-		if (res)
-			goto err_stop;
+		if (coming_up) {
+			res = drv_add_interface(local, &sdata->vif);
+			if (res)
+				goto err_stop;
+		}
 
 		if (ieee80211_vif_is_mesh(&sdata->vif)) {
 			local->fif_other_bss++;
@@ -316,7 +314,9 @@ static int ieee80211_open(struct net_dev
 	hw_reconf_flags |= __ieee80211_recalc_idle(local);
 	mutex_unlock(&local->mtx);
 
-	local->open_count++;
+	if (coming_up)
+		local->open_count++;
+
 	if (hw_reconf_flags) {
 		ieee80211_hw_config(local, hw_reconf_flags);
 		/*
@@ -331,6 +331,8 @@ static int ieee80211_open(struct net_dev
 
 	netif_tx_start_all_queues(dev);
 
+	set_bit(__SDATA_RUNNING, &sdata->state);
+
 	return 0;
  err_del_interface:
 	drv_remove_interface(local, &sdata->vif);
@@ -344,19 +346,38 @@ static int ieee80211_open(struct net_dev
 	return res;
 }
 
-static int ieee80211_stop(struct net_device *dev)
+static int ieee80211_open(struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	int err;
+
+	/* fail early if user set an invalid address */
+	if (!is_zero_ether_addr(dev->dev_addr) &&
+	    !is_valid_ether_addr(dev->dev_addr))
+		return -EADDRNOTAVAIL;
+
+	err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
+	if (err)
+		return err;
+
+	return ieee80211_do_open(dev, true);
+}
+
+static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
+			      bool going_down)
+{
 	struct ieee80211_local *local = sdata->local;
 	unsigned long flags;
 	struct sk_buff *skb, *tmp;
 	u32 hw_reconf_flags = 0;
 	int i;
 
+	clear_bit(__SDATA_RUNNING, &sdata->state);
+
 	/*
 	 * Stop TX on this interface first.
 	 */
-	netif_tx_stop_all_queues(dev);
+	netif_tx_stop_all_queues(sdata->dev);
 
 	/*
 	 * Purge work for this interface.
@@ -394,11 +415,12 @@ static int ieee80211_stop(struct net_dev
 	if (sdata->vif.type == NL80211_IFTYPE_AP)
 		local->fif_pspoll--;
 
-	netif_addr_lock_bh(dev);
+	netif_addr_lock_bh(sdata->dev);
 	spin_lock_bh(&local->filter_lock);
-	__hw_addr_unsync(&local->mc_list, &dev->mc, dev->addr_len);
+	__hw_addr_unsync(&local->mc_list, &sdata->dev->mc,
+			 sdata->dev->addr_len);
 	spin_unlock_bh(&local->filter_lock);
-	netif_addr_unlock_bh(dev);
+	netif_addr_unlock_bh(sdata->dev);
 
 	ieee80211_configure_filter(local);
 
@@ -432,7 +454,8 @@ static int ieee80211_stop(struct net_dev
 		WARN_ON(!list_empty(&sdata->u.ap.vlans));
 	}
 
-	local->open_count--;
+	if (going_down)
+		local->open_count--;
 
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_AP_VLAN:
@@ -504,7 +527,8 @@ static int ieee80211_stop(struct net_dev
 		 */
 		ieee80211_free_keys(sdata);
 
-		drv_remove_interface(local, &sdata->vif);
+		if (going_down)
+			drv_remove_interface(local, &sdata->vif);
 	}
 
 	sdata->bss = NULL;
@@ -540,6 +564,13 @@ static int ieee80211_stop(struct net_dev
 		}
 	}
 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+static int ieee80211_stop(struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	ieee80211_do_stop(sdata, true);
 
 	return 0;
 }
@@ -854,9 +885,60 @@ static void ieee80211_setup_sdata(struct
 	ieee80211_debugfs_add_netdev(sdata);
 }
 
+static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
+					   enum nl80211_iftype type)
+{
+	struct ieee80211_local *local = sdata->local;
+	int ret, err;
+
+	ASSERT_RTNL();
+
+	if (!local->ops->change_interface)
+		return -EBUSY;
+
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		break;
+	default:
+		return -EBUSY;
+	}
+
+	switch (type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		break;
+	default:
+		return -EBUSY;
+	}
+
+	ret = ieee80211_check_concurrent_iface(sdata, type);
+	if (ret)
+		return ret;
+
+	ieee80211_do_stop(sdata, false);
+
+	ieee80211_teardown_sdata(sdata->dev);
+
+	ret = drv_change_interface(local, sdata, type);
+	if (ret)
+		type = sdata->vif.type;
+
+	ieee80211_setup_sdata(sdata, type);
+
+	err = ieee80211_do_open(sdata->dev, false);
+	WARN_ON(err);
+
+	return ret;
+}
+
 int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
 			     enum nl80211_iftype type)
 {
+	int ret;
+
 	ASSERT_RTNL();
 
 	if (type == sdata->vif.type)
@@ -867,18 +949,15 @@ int ieee80211_if_change_type(struct ieee
 	    type == NL80211_IFTYPE_ADHOC)
 		return -EOPNOTSUPP;
 
-	/*
-	 * We could, here, on changes between IBSS/STA/MESH modes,
-	 * invoke an MLME function instead that disassociates etc.
-	 * and goes into the requested mode.
-	 */
-
-	if (ieee80211_sdata_running(sdata))
-		return -EBUSY;
-
-	/* Purge and reset type-dependent state. */
-	ieee80211_teardown_sdata(sdata->dev);
-	ieee80211_setup_sdata(sdata, type);
+	if (ieee80211_sdata_running(sdata)) {
+		ret = ieee80211_runtime_change_iftype(sdata, type);
+		if (ret)
+			return ret;
+	} else {
+		/* Purge and reset type-dependent state. */
+		ieee80211_teardown_sdata(sdata->dev);
+		ieee80211_setup_sdata(sdata, type);
+	}
 
 	/* reset some values that shouldn't be kept across type changes */
 	sdata->vif.bss_conf.basic_rates =
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2010-08-26 16:00:29.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h	2010-08-26 16:01:13.000000000 +0200
@@ -472,6 +472,8 @@ enum ieee80211_sub_if_data_flags {
 	IEEE80211_SDATA_DONT_BRIDGE_PACKETS	= BIT(3),
 };
 
+#define __SDATA_RUNNING		0
+
 struct ieee80211_sub_if_data {
 	struct list_head list;
 
@@ -485,6 +487,8 @@ struct ieee80211_sub_if_data {
 
 	unsigned int flags;
 
+	unsigned long state;
+
 	int drop_unencrypted;
 
 	char name[IFNAMSIZ];
@@ -1083,7 +1087,7 @@ void ieee80211_recalc_idle(struct ieee80
 
 static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
 {
-	return netif_running(sdata->dev);
+	return test_bit(__SDATA_RUNNING, &sdata->state);
 }
 
 /* tx handling */
--- wireless-testing.orig/net/mac80211/cfg.c	2010-08-26 16:00:30.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c	2010-08-26 16:07:13.000000000 +0200
@@ -52,9 +52,6 @@ static int ieee80211_change_iface(struct
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	int ret;
 
-	if (ieee80211_sdata_running(sdata))
-		return -EBUSY;
-
 	ret = ieee80211_if_change_type(sdata, type);
 	if (ret)
 		return ret;
--- wireless-testing.orig/net/mac80211/driver-ops.h	2010-08-26 16:00:16.000000000 +0200
+++ wireless-testing/net/mac80211/driver-ops.h	2010-08-26 16:07:13.000000000 +0200
@@ -54,6 +54,20 @@ static inline int drv_add_interface(stru
 	return ret;
 }
 
+static inline int drv_change_interface(struct ieee80211_local *local,
+				       struct ieee80211_sub_if_data *sdata,
+				       enum nl80211_iftype type)
+{
+	int ret;
+
+	might_sleep();
+
+	trace_drv_change_interface(local, sdata, type);
+	ret = local->ops->change_interface(&local->hw, &sdata->vif, type);
+	trace_drv_return_int(local, ret);
+	return ret;
+}
+
 static inline void drv_remove_interface(struct ieee80211_local *local,
 					struct ieee80211_vif *vif)
 {
--- wireless-testing.orig/net/mac80211/driver-trace.h	2010-08-26 16:00:16.000000000 +0200
+++ wireless-testing/net/mac80211/driver-trace.h	2010-08-26 16:07:37.000000000 +0200
@@ -136,6 +136,31 @@ TRACE_EVENT(drv_add_interface,
 	)
 );
 
+TRACE_EVENT(drv_change_interface,
+	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sub_if_data *sdata,
+		 enum nl80211_iftype type),
+
+	TP_ARGS(local, sdata, type),
+
+	TP_STRUCT__entry(
+		LOCAL_ENTRY
+		VIF_ENTRY
+		__field(u32, new_type)
+	),
+
+	TP_fast_assign(
+		LOCAL_ASSIGN;
+		VIF_ASSIGN;
+		__entry->new_type = type;
+	),
+
+	TP_printk(
+		LOCAL_PR_FMT  VIF_PR_FMT " new type:%d",
+		LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type
+	)
+);
+
 TRACE_EVENT(drv_remove_interface,
 	TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata),
 




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

end of thread, other threads:[~2010-08-26 14:22 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-25 20:56 [RFC 0/6] "runtime" iftype switching Johannes Berg
2010-08-25 20:56 ` [RFC 1/6] mac80211: clean up ifdown/cleanup paths Johannes Berg
2010-08-25 20:56 ` [RFC 2/6] mac80211: switch to ieee80211_sdata_running Johannes Berg
2010-08-25 20:56 ` [RFC 3/6] mac80211: simplify zero address checks Johannes Berg
2010-08-25 20:56 ` [RFC 4/6] mac80211: split out concurrent vif checks Johannes Berg
2010-08-25 20:56 ` [RFC 5/6] mac80211: support runtime interface type changes Johannes Berg
2010-08-26 14:22   ` [RFC 5/6 v2] " Johannes Berg
2010-08-25 20:56 ` [RFC 6/6] mac80211_hwsim: support runtime iftype changes Johannes Berg
2010-08-25 21:10 ` [RFC 0/6] "runtime" iftype switching John W. Linville
2010-08-26  7:23   ` Johannes Berg
2010-08-26 11:58     ` John W. Linville

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).