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>
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 9000b01..fc712e6 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -21,6 +21,9 @@
#define CAPAB_OFFSET 17
#define ACCEPT_PLINKS 0x80
+#define TMR_RUNNING_HK 0
+#define TMR_RUNNING_MP 1
+
int mesh_allocated;
static struct kmem_cache *rm_cache;
@@ -45,6 +48,12 @@
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
ifmsh->housekeeping = true;
+
+ if (local->quiescing) {
+ set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
+ return;
+ }
+
queue_work(local->hw.workqueue, &ifmsh->work);
}
@@ -343,6 +352,11 @@
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
+ if (local->quiescing) {
+ set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
+ return;
+ }
+
queue_work(local->hw.workqueue, &ifmsh->work);
}
@@ -424,6 +438,32 @@
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
}
+#ifdef CONFIG_PM
+void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ /* might restart the timer but that doesn't matter */
+ cancel_work_sync(&ifmsh->work);
+
+ /* use atomic bitops in case both timers fire at the same time */
+
+ if (del_timer_sync(&ifmsh->housekeeping_timer))
+ set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
+ if (del_timer_sync(&ifmsh->mesh_path_timer))
+ set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
+}
+
+void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running))
+ add_timer(&ifmsh->housekeeping_timer);
+ if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
+ add_timer(&ifmsh->mesh_path_timer);
+}
+#endif
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
{