diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-03-27 18:24:53 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-04-08 03:17:01 -0400 |
commit | b2c0958b203784659e230bde6bd553d7c37bb4d2 (patch) | |
tree | 22fad9aeaac415c114b4a9dfaf765f147d8852b9 /net/mac80211/iface.c | |
parent | 3c3e21e7443bdb948437a6e925fd111e932dc083 (diff) |
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 <sgruszka@redhat.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r-- | net/mac80211/iface.c | 74 |
1 files changed, 49 insertions, 25 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d0d5f20f1ec4..8c9419595bc8 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, | |||
739 | sdata->dev->addr_len); | 739 | sdata->dev->addr_len); |
740 | spin_unlock_bh(&local->filter_lock); | 740 | spin_unlock_bh(&local->filter_lock); |
741 | netif_addr_unlock_bh(sdata->dev); | 741 | netif_addr_unlock_bh(sdata->dev); |
742 | |||
743 | ieee80211_configure_filter(local); | ||
744 | } | 742 | } |
745 | 743 | ||
746 | del_timer_sync(&local->dynamic_ps_timer); | 744 | del_timer_sync(&local->dynamic_ps_timer); |
@@ -751,6 +749,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
751 | cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); | 749 | cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); |
752 | 750 | ||
753 | if (sdata->wdev.cac_started) { | 751 | if (sdata->wdev.cac_started) { |
752 | WARN_ON(local->suspended); | ||
754 | mutex_lock(&local->iflist_mtx); | 753 | mutex_lock(&local->iflist_mtx); |
755 | ieee80211_vif_release_channel(sdata); | 754 | ieee80211_vif_release_channel(sdata); |
756 | mutex_unlock(&local->iflist_mtx); | 755 | mutex_unlock(&local->iflist_mtx); |
@@ -801,14 +800,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
801 | if (local->monitors == 0) { | 800 | if (local->monitors == 0) { |
802 | local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; | 801 | local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; |
803 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; | 802 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; |
804 | ieee80211_del_virtual_monitor(local); | ||
805 | } | 803 | } |
806 | 804 | ||
807 | ieee80211_adjust_monitor_flags(sdata, -1); | 805 | ieee80211_adjust_monitor_flags(sdata, -1); |
808 | ieee80211_configure_filter(local); | ||
809 | mutex_lock(&local->mtx); | ||
810 | ieee80211_recalc_idle(local); | ||
811 | mutex_unlock(&local->mtx); | ||
812 | break; | 806 | break; |
813 | case NL80211_IFTYPE_P2P_DEVICE: | 807 | case NL80211_IFTYPE_P2P_DEVICE: |
814 | /* relies on synchronize_rcu() below */ | 808 | /* relies on synchronize_rcu() below */ |
@@ -838,27 +832,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
838 | /* fall through */ | 832 | /* fall through */ |
839 | case NL80211_IFTYPE_AP: | 833 | case NL80211_IFTYPE_AP: |
840 | skb_queue_purge(&sdata->skb_queue); | 834 | skb_queue_purge(&sdata->skb_queue); |
841 | |||
842 | if (going_down) | ||
843 | drv_remove_interface(local, sdata); | ||
844 | } | 835 | } |
845 | 836 | ||
846 | sdata->bss = NULL; | 837 | sdata->bss = NULL; |
847 | 838 | ||
848 | ieee80211_recalc_ps(local, -1); | ||
849 | |||
850 | if (local->open_count == 0) { | ||
851 | ieee80211_clear_tx_pending(local); | ||
852 | ieee80211_stop_device(local); | ||
853 | |||
854 | /* no reconfiguring after stop! */ | ||
855 | hw_reconf_flags = 0; | ||
856 | } | ||
857 | |||
858 | /* do after stop to avoid reconfiguring when we stop anyway */ | ||
859 | if (hw_reconf_flags) | ||
860 | ieee80211_hw_config(local, hw_reconf_flags); | ||
861 | |||
862 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | 839 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); |
863 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { | 840 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { |
864 | skb_queue_walk_safe(&local->pending[i], skb, tmp) { | 841 | skb_queue_walk_safe(&local->pending[i], skb, tmp) { |
@@ -871,7 +848,54 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
871 | } | 848 | } |
872 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | 849 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); |
873 | 850 | ||
874 | if (local->monitors == local->open_count && local->monitors > 0) | 851 | if (local->open_count == 0) |
852 | ieee80211_clear_tx_pending(local); | ||
853 | |||
854 | /* | ||
855 | * If the interface goes down while suspended, presumably because | ||
856 | * the device was unplugged and that happens before our resume, | ||
857 | * then the driver is already unconfigured and the remainder of | ||
858 | * this function isn't needed. | ||
859 | * XXX: what about WoWLAN? If the device has software state, e.g. | ||
860 | * memory allocated, it might expect teardown commands from | ||
861 | * mac80211 here? | ||
862 | */ | ||
863 | if (local->suspended) { | ||
864 | WARN_ON(local->wowlan); | ||
865 | WARN_ON(rtnl_dereference(local->monitor_sdata)); | ||
866 | return; | ||
867 | } | ||
868 | |||
869 | switch (sdata->vif.type) { | ||
870 | case NL80211_IFTYPE_AP_VLAN: | ||
871 | break; | ||
872 | case NL80211_IFTYPE_MONITOR: | ||
873 | if (local->monitors == 0) | ||
874 | ieee80211_del_virtual_monitor(local); | ||
875 | |||
876 | mutex_lock(&local->mtx); | ||
877 | ieee80211_recalc_idle(local); | ||
878 | mutex_unlock(&local->mtx); | ||
879 | break; | ||
880 | default: | ||
881 | if (going_down) | ||
882 | drv_remove_interface(local, sdata); | ||
883 | } | ||
884 | |||
885 | ieee80211_recalc_ps(local, -1); | ||
886 | |||
887 | if (local->open_count == 0) { | ||
888 | ieee80211_stop_device(local); | ||
889 | |||
890 | /* no reconfiguring after stop! */ | ||
891 | return; | ||
892 | } | ||
893 | |||
894 | /* do after stop to avoid reconfiguring when we stop anyway */ | ||
895 | ieee80211_configure_filter(local); | ||
896 | ieee80211_hw_config(local, hw_reconf_flags); | ||
897 | |||
898 | if (local->monitors == local->open_count) | ||
875 | ieee80211_add_virtual_monitor(local); | 899 | ieee80211_add_virtual_monitor(local); |
876 | } | 900 | } |
877 | 901 | ||