aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-05-17 05:40:42 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-05-20 14:46:25 -0400
commit5bb644a0fd25a5e083ecbfaa92a211db99aa6ef7 (patch)
treed2a6d5ff2323db0c475be15c63bb8fc55482a1e2
parentcc32abd494c0a8f76f2638e3f3a76e01c68bc9ea (diff)
mac80211: cancel/restart all timers across suspend/resume
We forgot to cancel all timers in mac80211 when suspending. In particular we forgot to deal with some things that can cause hardware reconfiguration -- while it is down. While at it we go ahead and add a warning in ieee80211_sta_work() if its run while the suspend->resume cycle is in effect. This should not happen and if it does it would indicate there is a bug lurking in either mac80211 or mac80211 drivers. With this now wpa_supplicant doesn't blink when I go to suspend and resume where as before there where issues with some timers running during the suspend->resume cycle. This caused a lot of incorrect assumptions and would at times bring back the device in an incoherent, but mostly recoverable, state. Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--net/mac80211/ibss.c29
-rw-r--r--net/mac80211/ieee80211_i.h26
-rw-r--r--net/mac80211/mesh.c40
-rw-r--r--net/mac80211/mesh.h12
-rw-r--r--net/mac80211/mesh_hwmp.c8
-rw-r--r--net/mac80211/mesh_plink.c21
-rw-r--r--net/mac80211/mlme.c59
-rw-r--r--net/mac80211/pm.c80
-rw-r--r--net/mac80211/scan.c18
-rw-r--r--net/mac80211/sta_info.c3
-rw-r--r--net/mac80211/sta_info.h1
-rw-r--r--net/mac80211/util.c43
12 files changed, 321 insertions, 19 deletions
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index c236079ed38a..ebf7318b610f 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -737,6 +737,9 @@ static void ieee80211_ibss_work(struct work_struct *work)
737 struct ieee80211_if_ibss *ifibss; 737 struct ieee80211_if_ibss *ifibss;
738 struct sk_buff *skb; 738 struct sk_buff *skb;
739 739
740 if (WARN_ON(local->suspended))
741 return;
742
740 if (!netif_running(sdata->dev)) 743 if (!netif_running(sdata->dev))
741 return; 744 return;
742 745
@@ -773,10 +776,36 @@ static void ieee80211_ibss_timer(unsigned long data)
773 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; 776 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
774 struct ieee80211_local *local = sdata->local; 777 struct ieee80211_local *local = sdata->local;
775 778
779 if (local->quiescing) {
780 ifibss->timer_running = true;
781 return;
782 }
783
776 set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); 784 set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
777 queue_work(local->hw.workqueue, &ifibss->work); 785 queue_work(local->hw.workqueue, &ifibss->work);
778} 786}
779 787
788#ifdef CONFIG_PM
789void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata)
790{
791 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
792
793 cancel_work_sync(&ifibss->work);
794 if (del_timer_sync(&ifibss->timer))
795 ifibss->timer_running = true;
796}
797
798void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata)
799{
800 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
801
802 if (ifibss->timer_running) {
803 add_timer(&ifibss->timer);
804 ifibss->timer_running = false;
805 }
806}
807#endif
808
780void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) 809void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
781{ 810{
782 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; 811 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index be9446551cf6..8db8d16d206c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -293,6 +293,7 @@ struct ieee80211_if_managed {
293 int auth_tries; /* retries for auth req */ 293 int auth_tries; /* retries for auth req */
294 int assoc_tries; /* retries for assoc req */ 294 int assoc_tries; /* retries for assoc req */
295 295
296 unsigned long timers_running; /* used for quiesce/restart */
296 bool powersave; /* powersave requested for this iface */ 297 bool powersave; /* powersave requested for this iface */
297 298
298 unsigned long request; 299 unsigned long request;
@@ -333,6 +334,9 @@ struct ieee80211_if_ibss {
333 334
334 unsigned long request; 335 unsigned long request;
335 unsigned long last_scan_completed; 336 unsigned long last_scan_completed;
337
338 bool timer_running;
339
336 bool fixed_bssid; 340 bool fixed_bssid;
337 bool fixed_channel; 341 bool fixed_channel;
338 342
@@ -358,6 +362,8 @@ struct ieee80211_if_mesh {
358 struct timer_list mesh_path_timer; 362 struct timer_list mesh_path_timer;
359 struct sk_buff_head skb_queue; 363 struct sk_buff_head skb_queue;
360 364
365 unsigned long timers_running;
366
361 bool housekeeping; 367 bool housekeeping;
362 368
363 u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; 369 u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
@@ -609,6 +615,21 @@ struct ieee80211_local {
609 unsigned int filter_flags; /* FIF_* */ 615 unsigned int filter_flags; /* FIF_* */
610 struct iw_statistics wstats; 616 struct iw_statistics wstats;
611 bool tim_in_locked_section; /* see ieee80211_beacon_get() */ 617 bool tim_in_locked_section; /* see ieee80211_beacon_get() */
618
619 /*
620 * suspended is true if we finished all the suspend _and_ we have
621 * not yet come up from resume. This is to be used by mac80211
622 * to ensure driver sanity during suspend and mac80211's own
623 * sanity. It can eventually be used for WoW as well.
624 */
625 bool suspended;
626
627 /*
628 * quiescing is true during the suspend process _only_ to
629 * ease timer cancelling etc.
630 */
631 bool quiescing;
632
612 int tx_headroom; /* required headroom for hardware/radiotap */ 633 int tx_headroom; /* required headroom for hardware/radiotap */
613 634
614 /* Tasklet and skb queue to process calls from IRQ mode. All frames 635 /* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -937,6 +958,8 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
937void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 958void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
938 struct ieee80211_channel_sw_ie *sw_elem, 959 struct ieee80211_channel_sw_ie *sw_elem,
939 struct ieee80211_bss *bss); 960 struct ieee80211_bss *bss);
961void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
962void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
940 963
941/* IBSS code */ 964/* IBSS code */
942void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); 965void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -949,6 +972,8 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
949int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, 972int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
950 struct cfg80211_ibss_params *params); 973 struct cfg80211_ibss_params *params);
951int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); 974int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
975void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata);
976void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);
952 977
953/* scan/BSS handling */ 978/* scan/BSS handling */
954void ieee80211_scan_work(struct work_struct *work); 979void ieee80211_scan_work(struct work_struct *work);
@@ -959,6 +984,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
959int ieee80211_scan_results(struct ieee80211_local *local, 984int ieee80211_scan_results(struct ieee80211_local *local,
960 struct iw_request_info *info, 985 struct iw_request_info *info,
961 char *buf, size_t len); 986 char *buf, size_t len);
987void ieee80211_scan_cancel(struct ieee80211_local *local);
962ieee80211_rx_result 988ieee80211_rx_result
963ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, 989ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
964 struct sk_buff *skb, 990 struct sk_buff *skb,
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 9000b01a1671..fc712e60705d 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -21,6 +21,9 @@
21#define CAPAB_OFFSET 17 21#define CAPAB_OFFSET 17
22#define ACCEPT_PLINKS 0x80 22#define ACCEPT_PLINKS 0x80
23 23
24#define TMR_RUNNING_HK 0
25#define TMR_RUNNING_MP 1
26
24int mesh_allocated; 27int mesh_allocated;
25static struct kmem_cache *rm_cache; 28static struct kmem_cache *rm_cache;
26 29
@@ -45,6 +48,12 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
45 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 48 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
46 49
47 ifmsh->housekeeping = true; 50 ifmsh->housekeeping = true;
51
52 if (local->quiescing) {
53 set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
54 return;
55 }
56
48 queue_work(local->hw.workqueue, &ifmsh->work); 57 queue_work(local->hw.workqueue, &ifmsh->work);
49} 58}
50 59
@@ -343,6 +352,11 @@ static void ieee80211_mesh_path_timer(unsigned long data)
343 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 352 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
344 struct ieee80211_local *local = sdata->local; 353 struct ieee80211_local *local = sdata->local;
345 354
355 if (local->quiescing) {
356 set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
357 return;
358 }
359
346 queue_work(local->hw.workqueue, &ifmsh->work); 360 queue_work(local->hw.workqueue, &ifmsh->work);
347} 361}
348 362
@@ -424,6 +438,32 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
424 round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); 438 round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
425} 439}
426 440
441#ifdef CONFIG_PM
442void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
443{
444 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
445
446 /* might restart the timer but that doesn't matter */
447 cancel_work_sync(&ifmsh->work);
448
449 /* use atomic bitops in case both timers fire at the same time */
450
451 if (del_timer_sync(&ifmsh->housekeeping_timer))
452 set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
453 if (del_timer_sync(&ifmsh->mesh_path_timer))
454 set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
455}
456
457void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
458{
459 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
460
461 if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running))
462 add_timer(&ifmsh->housekeeping_timer);
463 if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
464 add_timer(&ifmsh->mesh_path_timer);
465}
466#endif
427 467
428void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) 468void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
429{ 469{
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index d891d7ddccd7..832bb503ca9b 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -267,6 +267,8 @@ void mesh_path_timer(unsigned long data);
267void mesh_path_flush_by_nexthop(struct sta_info *sta); 267void mesh_path_flush_by_nexthop(struct sta_info *sta);
268void mesh_path_discard_frame(struct sk_buff *skb, 268void mesh_path_discard_frame(struct sk_buff *skb,
269 struct ieee80211_sub_if_data *sdata); 269 struct ieee80211_sub_if_data *sdata);
270void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
271void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
270 272
271#ifdef CONFIG_MAC80211_MESH 273#ifdef CONFIG_MAC80211_MESH
272extern int mesh_allocated; 274extern int mesh_allocated;
@@ -294,10 +296,20 @@ static inline void mesh_path_activate(struct mesh_path *mpath)
294 296
295void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); 297void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
296 298
299void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
300void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
301void mesh_plink_quiesce(struct sta_info *sta);
302void mesh_plink_restart(struct sta_info *sta);
297#else 303#else
298#define mesh_allocated 0 304#define mesh_allocated 0
299static inline void 305static inline void
300ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} 306ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
307static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
308{}
309static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
310{}
311static inline void mesh_plink_quiesce(struct sta_info *sta) {}
312static inline void mesh_plink_restart(struct sta_info *sta) {}
301#endif 313#endif
302 314
303#endif /* IEEE80211S_H */ 315#endif /* IEEE80211S_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 60b35accda91..003cb470ac84 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -836,8 +836,14 @@ void mesh_path_timer(unsigned long data)
836 mpath = rcu_dereference(mpath); 836 mpath = rcu_dereference(mpath);
837 if (!mpath) 837 if (!mpath)
838 goto endmpathtimer; 838 goto endmpathtimer;
839 spin_lock_bh(&mpath->state_lock);
840 sdata = mpath->sdata; 839 sdata = mpath->sdata;
840
841 if (sdata->local->quiescing) {
842 rcu_read_unlock();
843 return;
844 }
845
846 spin_lock_bh(&mpath->state_lock);
841 if (mpath->flags & MESH_PATH_RESOLVED || 847 if (mpath->flags & MESH_PATH_RESOLVED ||
842 (!(mpath->flags & MESH_PATH_RESOLVING))) 848 (!(mpath->flags & MESH_PATH_RESOLVING)))
843 mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED); 849 mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index a8bbdeca013a..cb14253587f1 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -266,6 +266,11 @@ static void mesh_plink_timer(unsigned long data)
266 */ 266 */
267 sta = (struct sta_info *) data; 267 sta = (struct sta_info *) data;
268 268
269 if (sta->sdata->local->quiescing) {
270 sta->plink_timer_was_running = true;
271 return;
272 }
273
269 spin_lock_bh(&sta->lock); 274 spin_lock_bh(&sta->lock);
270 if (sta->ignore_plink_timer) { 275 if (sta->ignore_plink_timer) {
271 sta->ignore_plink_timer = false; 276 sta->ignore_plink_timer = false;
@@ -322,6 +327,22 @@ static void mesh_plink_timer(unsigned long data)
322 } 327 }
323} 328}
324 329
330#ifdef CONFIG_PM
331void mesh_plink_quiesce(struct sta_info *sta)
332{
333 if (del_timer_sync(&sta->plink_timer))
334 sta->plink_timer_was_running = true;
335}
336
337void mesh_plink_restart(struct sta_info *sta)
338{
339 if (sta->plink_timer_was_running) {
340 add_timer(&sta->plink_timer);
341 sta->plink_timer_was_running = false;
342 }
343}
344#endif
345
325static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) 346static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
326{ 347{
327 sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); 348 sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 41f3c1f98cc3..b61a7819867e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -37,6 +37,9 @@
37#define IEEE80211_PROBE_IDLE_TIME (60 * HZ) 37#define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
38#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) 38#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
39 39
40#define TMR_RUNNING_TIMER 0
41#define TMR_RUNNING_CHANSW 1
42
40/* utils */ 43/* utils */
41static int ecw2cw(int ecw) 44static int ecw2cw(int ecw)
42{ 45{
@@ -521,6 +524,11 @@ static void ieee80211_chswitch_timer(unsigned long data)
521 (struct ieee80211_sub_if_data *) data; 524 (struct ieee80211_sub_if_data *) data;
522 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 525 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
523 526
527 if (sdata->local->quiescing) {
528 set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
529 return;
530 }
531
524 queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); 532 queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
525} 533}
526 534
@@ -714,6 +722,9 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
714{ 722{
715 struct ieee80211_local *local = (void *) data; 723 struct ieee80211_local *local = (void *) data;
716 724
725 if (local->quiescing)
726 return;
727
717 queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); 728 queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
718} 729}
719 730
@@ -2108,6 +2119,11 @@ static void ieee80211_sta_timer(unsigned long data)
2108 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 2119 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
2109 struct ieee80211_local *local = sdata->local; 2120 struct ieee80211_local *local = sdata->local;
2110 2121
2122 if (local->quiescing) {
2123 set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
2124 return;
2125 }
2126
2111 set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); 2127 set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
2112 queue_work(local->hw.workqueue, &ifmgd->work); 2128 queue_work(local->hw.workqueue, &ifmgd->work);
2113} 2129}
@@ -2240,6 +2256,17 @@ static void ieee80211_sta_work(struct work_struct *work)
2240 2256
2241 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 2257 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
2242 return; 2258 return;
2259
2260 /*
2261 * Nothing should have been stuffed into the workqueue during
2262 * the suspend->resume cycle. If this WARN is seen then there
2263 * is a bug with either the driver suspend or something in
2264 * mac80211 stuffing into the workqueue which we haven't yet
2265 * cleared during mac80211's suspend cycle.
2266 */
2267 if (WARN_ON(local->suspended))
2268 return;
2269
2243 ifmgd = &sdata->u.mgd; 2270 ifmgd = &sdata->u.mgd;
2244 2271
2245 while ((skb = skb_dequeue(&ifmgd->skb_queue))) 2272 while ((skb = skb_dequeue(&ifmgd->skb_queue)))
@@ -2307,6 +2334,38 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
2307 } 2334 }
2308} 2335}
2309 2336
2337#ifdef CONFIG_PM
2338void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
2339{
2340 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
2341
2342 /*
2343 * we need to use atomic bitops for the running bits
2344 * only because both timers might fire at the same
2345 * time -- the code here is properly synchronised.
2346 */
2347
2348 cancel_work_sync(&ifmgd->work);
2349 cancel_work_sync(&ifmgd->beacon_loss_work);
2350 if (del_timer_sync(&ifmgd->timer))
2351 set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
2352
2353 cancel_work_sync(&ifmgd->chswitch_work);
2354 if (del_timer_sync(&ifmgd->chswitch_timer))
2355 set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
2356}
2357
2358void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
2359{
2360 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
2361
2362 if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
2363 add_timer(&ifmgd->timer);
2364 if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
2365 add_timer(&ifmgd->chswitch_timer);
2366}
2367#endif
2368
2310/* interface setup */ 2369/* interface setup */
2311void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) 2370void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
2312{ 2371{
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 9d3d89abbb57..7a549f9deb96 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -2,6 +2,7 @@
2#include <net/rtnetlink.h> 2#include <net/rtnetlink.h>
3 3
4#include "ieee80211_i.h" 4#include "ieee80211_i.h"
5#include "mesh.h"
5#include "driver-ops.h" 6#include "driver-ops.h"
6#include "led.h" 7#include "led.h"
7 8
@@ -13,11 +14,30 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
13 struct sta_info *sta; 14 struct sta_info *sta;
14 unsigned long flags; 15 unsigned long flags;
15 16
17 ieee80211_scan_cancel(local);
18
16 ieee80211_stop_queues_by_reason(hw, 19 ieee80211_stop_queues_by_reason(hw,
17 IEEE80211_QUEUE_STOP_REASON_SUSPEND); 20 IEEE80211_QUEUE_STOP_REASON_SUSPEND);
18 21
22 /* flush out all packets */
23 synchronize_net();
24
25 local->quiescing = true;
26 /* make quiescing visible to timers everywhere */
27 mb();
28
19 flush_workqueue(local->hw.workqueue); 29 flush_workqueue(local->hw.workqueue);
20 30
31 /* Don't try to run timers while suspended. */
32 del_timer_sync(&local->sta_cleanup);
33
34 /*
35 * Note that this particular timer doesn't need to be
36 * restarted at resume.
37 */
38 cancel_work_sync(&local->dynamic_ps_enable_work);
39 del_timer_sync(&local->dynamic_ps_timer);
40
21 /* disable keys */ 41 /* disable keys */
22 list_for_each_entry(sdata, &local->interfaces, list) 42 list_for_each_entry(sdata, &local->interfaces, list)
23 ieee80211_disable_keys(sdata); 43 ieee80211_disable_keys(sdata);
@@ -35,10 +55,20 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
35 55
36 rcu_read_unlock(); 56 rcu_read_unlock();
37 57
58 /* flush again, in case driver queued work */
59 flush_workqueue(local->hw.workqueue);
60
61 /* stop hardware - this must stop RX */
62 if (local->open_count) {
63 ieee80211_led_radio(local, false);
64 drv_stop(local);
65 }
66
38 /* remove STAs */ 67 /* remove STAs */
39 if (local->ops->sta_notify) { 68 spin_lock_irqsave(&local->sta_lock, flags);
40 spin_lock_irqsave(&local->sta_lock, flags); 69 list_for_each_entry(sta, &local->sta_list, list) {
41 list_for_each_entry(sta, &local->sta_list, list) { 70 if (local->ops->sta_notify) {
71 sdata = sta->sdata;
42 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 72 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
43 sdata = container_of(sdata->bss, 73 sdata = container_of(sdata->bss,
44 struct ieee80211_sub_if_data, 74 struct ieee80211_sub_if_data,
@@ -47,29 +77,43 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
47 drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE, 77 drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE,
48 &sta->sta); 78 &sta->sta);
49 } 79 }
50 spin_unlock_irqrestore(&local->sta_lock, flags); 80
81 mesh_plink_quiesce(sta);
51 } 82 }
83 spin_unlock_irqrestore(&local->sta_lock, flags);
52 84
53 /* remove all interfaces */ 85 /* remove all interfaces */
54 list_for_each_entry(sdata, &local->interfaces, list) { 86 list_for_each_entry(sdata, &local->interfaces, list) {
55 if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 87 switch(sdata->vif.type) {
56 sdata->vif.type != NL80211_IFTYPE_MONITOR && 88 case NL80211_IFTYPE_STATION:
57 netif_running(sdata->dev)) { 89 ieee80211_sta_quiesce(sdata);
58 conf.vif = &sdata->vif; 90 break;
59 conf.type = sdata->vif.type; 91 case NL80211_IFTYPE_ADHOC:
60 conf.mac_addr = sdata->dev->dev_addr; 92 ieee80211_ibss_quiesce(sdata);
61 drv_remove_interface(local, &conf); 93 break;
94 case NL80211_IFTYPE_MESH_POINT:
95 ieee80211_mesh_quiesce(sdata);
96 break;
97 case NL80211_IFTYPE_AP_VLAN:
98 case NL80211_IFTYPE_MONITOR:
99 /* don't tell driver about this */
100 continue;
101 default:
102 break;
62 } 103 }
63 }
64 104
65 /* flush again, in case driver queued work */ 105 if (!netif_running(sdata->dev))
66 flush_workqueue(local->hw.workqueue); 106 continue;
67 107
68 /* stop hardware */ 108 conf.vif = &sdata->vif;
69 if (local->open_count) { 109 conf.type = sdata->vif.type;
70 ieee80211_led_radio(local, false); 110 conf.mac_addr = sdata->dev->dev_addr;
71 drv_stop(local); 111 drv_remove_interface(local, &conf);
72 } 112 }
113
114 local->suspended = true;
115 local->quiescing = false;
116
73 return 0; 117 return 0;
74} 118}
75 119
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index e65d74ba404b..2a8d09ad17ff 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -631,3 +631,21 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
631 mutex_unlock(&local->scan_mtx); 631 mutex_unlock(&local->scan_mtx);
632 return ret; 632 return ret;
633} 633}
634
635void ieee80211_scan_cancel(struct ieee80211_local *local)
636{
637 bool swscan;
638
639 cancel_delayed_work_sync(&local->scan_work);
640
641 /*
642 * Only call this function when a scan can't be
643 * queued -- mostly at suspend under RTNL.
644 */
645 mutex_lock(&local->scan_mtx);
646 swscan = local->sw_scanning;
647 mutex_unlock(&local->scan_mtx);
648
649 if (swscan)
650 ieee80211_scan_completed(&local->hw, true);
651}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 3e4348f3b285..d5611d8fd0d6 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -611,6 +611,9 @@ static void sta_info_cleanup(unsigned long data)
611 sta_info_cleanup_expire_buffered(local, sta); 611 sta_info_cleanup_expire_buffered(local, sta);
612 rcu_read_unlock(); 612 rcu_read_unlock();
613 613
614 if (local->quiescing)
615 return;
616
614 local->sta_cleanup.expires = 617 local->sta_cleanup.expires =
615 round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); 618 round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
616 add_timer(&local->sta_cleanup); 619 add_timer(&local->sta_cleanup);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 164b16cbe0a5..f53aa9dc6e97 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -293,6 +293,7 @@ struct sta_info {
293 __le16 reason; 293 __le16 reason;
294 u8 plink_retries; 294 u8 plink_retries;
295 bool ignore_plink_timer; 295 bool ignore_plink_timer;
296 bool plink_timer_was_running;
296 enum plink_state plink_state; 297 enum plink_state plink_state;
297 u32 plink_timeout; 298 u32 plink_timeout;
298 struct timer_list plink_timer; 299 struct timer_list plink_timer;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0689a8fbd1e6..ffb6e88f2ecd 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1034,6 +1034,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1034 struct sta_info *sta; 1034 struct sta_info *sta;
1035 unsigned long flags; 1035 unsigned long flags;
1036 int res; 1036 int res;
1037 bool from_suspend = local->suspended;
1038
1039 /*
1040 * We're going to start the hardware, at that point
1041 * we are no longer suspended and can RX frames.
1042 */
1043 local->suspended = false;
1037 1044
1038 /* restart hardware */ 1045 /* restart hardware */
1039 if (local->open_count) { 1046 if (local->open_count) {
@@ -1058,6 +1065,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1058 if (local->ops->sta_notify) { 1065 if (local->ops->sta_notify) {
1059 spin_lock_irqsave(&local->sta_lock, flags); 1066 spin_lock_irqsave(&local->sta_lock, flags);
1060 list_for_each_entry(sta, &local->sta_list, list) { 1067 list_for_each_entry(sta, &local->sta_list, list) {
1068 sdata = sta->sdata;
1061 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 1069 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
1062 sdata = container_of(sdata->bss, 1070 sdata = container_of(sdata->bss,
1063 struct ieee80211_sub_if_data, 1071 struct ieee80211_sub_if_data,
@@ -1128,5 +1136,40 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1128 ieee80211_wake_queues_by_reason(hw, 1136 ieee80211_wake_queues_by_reason(hw,
1129 IEEE80211_QUEUE_STOP_REASON_SUSPEND); 1137 IEEE80211_QUEUE_STOP_REASON_SUSPEND);
1130 1138
1139 /*
1140 * If this is for hw restart things are still running.
1141 * We may want to change that later, however.
1142 */
1143 if (!from_suspend)
1144 return 0;
1145
1146#ifdef CONFIG_PM
1147 local->suspended = false;
1148
1149 list_for_each_entry(sdata, &local->interfaces, list) {
1150 switch(sdata->vif.type) {
1151 case NL80211_IFTYPE_STATION:
1152 ieee80211_sta_restart(sdata);
1153 break;
1154 case NL80211_IFTYPE_ADHOC:
1155 ieee80211_ibss_restart(sdata);
1156 break;
1157 case NL80211_IFTYPE_MESH_POINT:
1158 ieee80211_mesh_restart(sdata);
1159 break;
1160 default:
1161 break;
1162 }
1163 }
1164
1165 add_timer(&local->sta_cleanup);
1166
1167 spin_lock_irqsave(&local->sta_lock, flags);
1168 list_for_each_entry(sta, &local->sta_list, list)
1169 mesh_plink_restart(sta);
1170 spin_unlock_irqrestore(&local->sta_lock, flags);
1171#else
1172 WARN_ON(1);
1173#endif
1131 return 0; 1174 return 0;
1132} 1175}