From c5d54fbf0ebdfa9e2a6264781548ab81e0eed688 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Mar 2013 21:54:24 +0100 Subject: mac80211: remove ancient reference to master interface The master interface no longer exists ... and hasn't for a few years now, so remove this reference :-) Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211/iface.c') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d646e12e55a..2bdbf14e7b7 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1,5 +1,5 @@ /* - * Interface handling (except master interface) + * Interface handling * * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. -- cgit v1.2.3-70-g09d2 From afdc7c18e9f2a768865b6caa886e605719a6304e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Mar 2013 22:40:22 +0100 Subject: mac80211: remove outdated comment referring to master interface The code now explicitly calls ieee80211_configure_filter() anyway, so nothing needs to be explained. Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'net/mac80211/iface.c') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2bdbf14e7b7..760268e5195 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -718,12 +718,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); - /* - * Don't count this interface for promisc/allmulti while it - * is down. dev_mc_unsync() will invoke set_multicast_list - * on the master interface which will sync these down to the - * hardware as filter flags. - */ + /* don't count this interface for promisc/allmulti while it is down */ if (sdata->flags & IEEE80211_SDATA_ALLMULTI) atomic_dec(&local->iff_allmultis); -- cgit v1.2.3-70-g09d2 From c8f994eec2a966a7a5fb6a3be517e3ede6a3cafa Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Mar 2013 22:49:19 +0100 Subject: mac80211: purge remain-on-channel items when suspending They can't really be executed while suspended and could trigger work warnings, so abort all ROC items. When the system resumes the notifications about this will be delivered to userspace which can then act accordingly (though it will assume they were canceled/finished.) Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/iface.c | 2 +- net/mac80211/offchannel.c | 6 +++--- net/mac80211/pm.c | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'net/mac80211/iface.c') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c783e996bcc..693c1812b7b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1315,7 +1315,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local); void ieee80211_offchannel_return(struct ieee80211_local *local); void ieee80211_roc_setup(struct ieee80211_local *local); void ieee80211_start_next_roc(struct ieee80211_local *local); -void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata); +void ieee80211_roc_purge(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc); void ieee80211_sw_roc_work(struct work_struct *work); void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 760268e5195..75b322f9d94 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -693,7 +693,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, if (sdata->dev) netif_tx_stop_all_queues(sdata->dev); - ieee80211_roc_purge(sdata); + ieee80211_roc_purge(local, sdata); if (sdata->vif.type == NL80211_IFTYPE_STATION) ieee80211_mgd_stop(sdata); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index b01eb7314ec..e19d6cf26dd 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -436,15 +436,15 @@ void ieee80211_roc_setup(struct ieee80211_local *local) INIT_LIST_HEAD(&local->roc_list); } -void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata) +void ieee80211_roc_purge(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { - struct ieee80211_local *local = sdata->local; struct ieee80211_roc_work *roc, *tmp; LIST_HEAD(tmp_list); mutex_lock(&local->mtx); list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { - if (roc->sdata != sdata) + if (sdata && roc->sdata != sdata) continue; if (roc->started && local->ops->remain_on_channel) { diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 3d16f4e6174..b98d927dd0f 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -19,6 +19,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_dfs_cac_cancel(local); + ieee80211_roc_purge(local, NULL); + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { -- cgit v1.2.3-70-g09d2 From 3c3e21e7443bdb948437a6e925fd111e932dc083 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Mar 2013 23:20:27 +0100 Subject: mac80211: destroy virtual monitor interface across suspend It has to be removed from the driver, but completely destroying it helps handle unplug of a device during suspend since then the channel context handling etc. doesn't have to happen later when it's removed. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 4 ++-- net/mac80211/pm.c | 6 ++---- net/mac80211/util.c | 5 +++++ 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'net/mac80211/iface.c') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 693c1812b7b..55fb382a819 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1336,6 +1336,8 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, const int offset); int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up); void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata); +int ieee80211_add_virtual_monitor(struct ieee80211_local *local); +void ieee80211_del_virtual_monitor(struct ieee80211_local *local); bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 75b322f9d94..d0d5f20f1ec 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -346,7 +346,7 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; } -static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) +int ieee80211_add_virtual_monitor(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; int ret = 0; @@ -400,7 +400,7 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) return ret; } -static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) +void ieee80211_del_virtual_monitor(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index b98d927dd0f..d1c021b62fe 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -21,6 +21,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_roc_purge(local, NULL); + ieee80211_del_virtual_monitor(local); + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { @@ -103,10 +105,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) drv_remove_interface(local, sdata); } - sdata = rtnl_dereference(local->monitor_sdata); - if (sdata) - drv_remove_interface(local, sdata); - /* * We disconnected on all interfaces before suspend, all channel * contexts should be released. diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f9581c6378a..43465b6e477 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1461,6 +1461,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* add interfaces */ sdata = rtnl_dereference(local->monitor_sdata); if (sdata) { + /* in HW restart it exists already */ + WARN_ON(local->resuming); res = drv_add_interface(local, sdata); if (WARN_ON(res)) { rcu_assign_pointer(local->monitor_sdata, NULL); @@ -1650,6 +1652,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) local->in_reconfig = false; barrier(); + if (local->monitors == local->open_count && local->monitors > 0) + ieee80211_add_virtual_monitor(local); + /* * Clear the WLAN_STA_BLOCK_BA flag so new aggregation * sessions can be established after a resume. -- cgit v1.2.3-70-g09d2 From b2c0958b203784659e230bde6bd553d7c37bb4d2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Mar 2013 23:24:53 +0100 Subject: mac80211: fix do_stop handling while suspended When a device is unplugged while suspended, mac80211 is de-initialized and all interfaces are removed while no state is actually present in the driver. This can cause warnings and driver confusion. Fix this by reordering the do_stop code to not call the driver when it is suspended, i.e. when there's no state in the driver anyway. The previous patches removed a few corner cases in ROC and virtual monitor interfaces so that now this is safe to do and no state should be left over. Reported-by: Stanislaw Gruszka Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 74 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 25 deletions(-) (limited to 'net/mac80211/iface.c') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d0d5f20f1ec..8c9419595bc 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -739,8 +739,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, sdata->dev->addr_len); spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(sdata->dev); - - ieee80211_configure_filter(local); } del_timer_sync(&local->dynamic_ps_timer); @@ -751,6 +749,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); if (sdata->wdev.cac_started) { + WARN_ON(local->suspended); mutex_lock(&local->iflist_mtx); ieee80211_vif_release_channel(sdata); mutex_unlock(&local->iflist_mtx); @@ -801,14 +800,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, if (local->monitors == 0) { local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; - ieee80211_del_virtual_monitor(local); } ieee80211_adjust_monitor_flags(sdata, -1); - ieee80211_configure_filter(local); - mutex_lock(&local->mtx); - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); break; case NL80211_IFTYPE_P2P_DEVICE: /* relies on synchronize_rcu() below */ @@ -838,27 +832,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, /* fall through */ case NL80211_IFTYPE_AP: skb_queue_purge(&sdata->skb_queue); - - if (going_down) - drv_remove_interface(local, sdata); } sdata->bss = NULL; - ieee80211_recalc_ps(local, -1); - - if (local->open_count == 0) { - ieee80211_clear_tx_pending(local); - ieee80211_stop_device(local); - - /* no reconfiguring after stop! */ - hw_reconf_flags = 0; - } - - /* do after stop to avoid reconfiguring when we stop anyway */ - if (hw_reconf_flags) - ieee80211_hw_config(local, hw_reconf_flags); - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { skb_queue_walk_safe(&local->pending[i], skb, tmp) { @@ -871,7 +848,54 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - if (local->monitors == local->open_count && local->monitors > 0) + if (local->open_count == 0) + ieee80211_clear_tx_pending(local); + + /* + * If the interface goes down while suspended, presumably because + * the device was unplugged and that happens before our resume, + * then the driver is already unconfigured and the remainder of + * this function isn't needed. + * XXX: what about WoWLAN? If the device has software state, e.g. + * memory allocated, it might expect teardown commands from + * mac80211 here? + */ + if (local->suspended) { + WARN_ON(local->wowlan); + WARN_ON(rtnl_dereference(local->monitor_sdata)); + return; + } + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: + break; + case NL80211_IFTYPE_MONITOR: + if (local->monitors == 0) + ieee80211_del_virtual_monitor(local); + + mutex_lock(&local->mtx); + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); + break; + default: + if (going_down) + drv_remove_interface(local, sdata); + } + + ieee80211_recalc_ps(local, -1); + + if (local->open_count == 0) { + ieee80211_stop_device(local); + + /* no reconfiguring after stop! */ + return; + } + + /* do after stop to avoid reconfiguring when we stop anyway */ + ieee80211_configure_filter(local); + ieee80211_hw_config(local, hw_reconf_flags); + + if (local->monitors == local->open_count) ieee80211_add_virtual_monitor(local); } -- cgit v1.2.3-70-g09d2 From a23108248a9d41400e686becddb5584b3a3fec1e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Mar 2013 22:10:02 +0100 Subject: mac80211: replace some dead code by a warning Given the (nested) switch statements, this code can't be reached, so make it warn instead of manipulating the carrier state which seems purposeful. Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/mac80211/iface.c') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8c9419595bc..63b63675aa6 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -581,7 +581,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_P2P_DEVICE: break; default: - netif_carrier_on(dev); + /* not reached */ + WARN_ON(1); } /* -- cgit v1.2.3-70-g09d2 From 2b730daacee6c318bce7b6373c19909e36a74590 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Mar 2013 22:23:20 +0100 Subject: mac80211: don't start new netdev queues if driver stopped If a new netdev (e.g. an AP VLAN) is created while the driver has queues stopped, the new netdev queues will be started even though they shouldn't. This will lead to frames accumulating on the internal mac80211 pending queues instead of properly being held on the netdev queues. Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'net/mac80211/iface.c') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 63b63675aa6..b6abaaa3676 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -639,8 +639,28 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) ieee80211_recalc_ps(local, -1); - if (dev) - netif_tx_start_all_queues(dev); + if (dev) { + unsigned long flags; + int n_acs = IEEE80211_NUM_ACS; + int ac; + + if (local->hw.queues < IEEE80211_NUM_ACS) + n_acs = 1; + + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE || + (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 && + skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) { + for (ac = 0; ac < n_acs; ac++) { + int ac_queue = sdata->vif.hw_queue[ac]; + + if (local->queue_stop_reasons[ac_queue] == 0 && + skb_queue_empty(&local->pending[ac_queue])) + netif_start_subqueue(dev, ac); + } + } + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + } return 0; err_del_interface: -- cgit v1.2.3-70-g09d2