diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-05-17 05:40:42 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-05-20 14:46:25 -0400 |
commit | 5bb644a0fd25a5e083ecbfaa92a211db99aa6ef7 (patch) | |
tree | d2a6d5ff2323db0c475be15c63bb8fc55482a1e2 | |
parent | cc32abd494c0a8f76f2638e3f3a76e01c68bc9ea (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.c | 29 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 26 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 40 | ||||
-rw-r--r-- | net/mac80211/mesh.h | 12 | ||||
-rw-r--r-- | net/mac80211/mesh_hwmp.c | 8 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 21 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 59 | ||||
-rw-r--r-- | net/mac80211/pm.c | 80 | ||||
-rw-r--r-- | net/mac80211/scan.c | 18 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 3 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 1 | ||||
-rw-r--r-- | net/mac80211/util.c | 43 |
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 | ||
789 | void 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 | |||
798 | void 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 | |||
780 | void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) | 809 | void 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, | |||
937 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | 958 | void 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); |
961 | void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); | ||
962 | void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); | ||
940 | 963 | ||
941 | /* IBSS code */ | 964 | /* IBSS code */ |
942 | void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); | 965 | void 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, | |||
949 | int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | 972 | int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, |
950 | struct cfg80211_ibss_params *params); | 973 | struct cfg80211_ibss_params *params); |
951 | int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); | 974 | int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); |
975 | void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata); | ||
976 | void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata); | ||
952 | 977 | ||
953 | /* scan/BSS handling */ | 978 | /* scan/BSS handling */ |
954 | void ieee80211_scan_work(struct work_struct *work); | 979 | void ieee80211_scan_work(struct work_struct *work); |
@@ -959,6 +984,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, | |||
959 | int ieee80211_scan_results(struct ieee80211_local *local, | 984 | int 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); |
987 | void ieee80211_scan_cancel(struct ieee80211_local *local); | ||
962 | ieee80211_rx_result | 988 | ieee80211_rx_result |
963 | ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, | 989 | ieee80211_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 | |||
24 | int mesh_allocated; | 27 | int mesh_allocated; |
25 | static struct kmem_cache *rm_cache; | 28 | static 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 | ||
442 | void 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 | |||
457 | void 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 | ||
428 | void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) | 468 | void 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); | |||
267 | void mesh_path_flush_by_nexthop(struct sta_info *sta); | 267 | void mesh_path_flush_by_nexthop(struct sta_info *sta); |
268 | void mesh_path_discard_frame(struct sk_buff *skb, | 268 | void mesh_path_discard_frame(struct sk_buff *skb, |
269 | struct ieee80211_sub_if_data *sdata); | 269 | struct ieee80211_sub_if_data *sdata); |
270 | void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); | ||
271 | void mesh_path_restart(struct ieee80211_sub_if_data *sdata); | ||
270 | 272 | ||
271 | #ifdef CONFIG_MAC80211_MESH | 273 | #ifdef CONFIG_MAC80211_MESH |
272 | extern int mesh_allocated; | 274 | extern int mesh_allocated; |
@@ -294,10 +296,20 @@ static inline void mesh_path_activate(struct mesh_path *mpath) | |||
294 | 296 | ||
295 | void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); | 297 | void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); |
296 | 298 | ||
299 | void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); | ||
300 | void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata); | ||
301 | void mesh_plink_quiesce(struct sta_info *sta); | ||
302 | void mesh_plink_restart(struct sta_info *sta); | ||
297 | #else | 303 | #else |
298 | #define mesh_allocated 0 | 304 | #define mesh_allocated 0 |
299 | static inline void | 305 | static inline void |
300 | ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} | 306 | ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} |
307 | static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) | ||
308 | {} | ||
309 | static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) | ||
310 | {} | ||
311 | static inline void mesh_plink_quiesce(struct sta_info *sta) {} | ||
312 | static 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 | ||
331 | void 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 | |||
337 | void 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 | |||
325 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) | 346 | static 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 */ |
41 | static int ecw2cw(int ecw) | 44 | static 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 | ||
2338 | void 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 | |||
2358 | void 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 */ |
2311 | void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | 2370 | void 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 | |||
635 | void 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 | } |