Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bb644a0 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

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: default avatarLuis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent cc32abd4
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -737,6 +737,9 @@ static void ieee80211_ibss_work(struct work_struct *work)
	struct ieee80211_if_ibss *ifibss;
	struct sk_buff *skb;

	if (WARN_ON(local->suspended))
		return;

	if (!netif_running(sdata->dev))
		return;

@@ -773,10 +776,36 @@ static void ieee80211_ibss_timer(unsigned long data)
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
	struct ieee80211_local *local = sdata->local;

	if (local->quiescing) {
		ifibss->timer_running = true;
		return;
	}

	set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
	queue_work(local->hw.workqueue, &ifibss->work);
}

#ifdef CONFIG_PM
void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata)
{
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;

	cancel_work_sync(&ifibss->work);
	if (del_timer_sync(&ifibss->timer))
		ifibss->timer_running = true;
}

void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata)
{
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;

	if (ifibss->timer_running) {
		add_timer(&ifibss->timer);
		ifibss->timer_running = false;
	}
}
#endif

void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+26 −0
Original line number Diff line number Diff line
@@ -293,6 +293,7 @@ struct ieee80211_if_managed {
	int auth_tries; /* retries for auth req */
	int assoc_tries; /* retries for assoc req */

	unsigned long timers_running; /* used for quiesce/restart */
	bool powersave; /* powersave requested for this iface */

	unsigned long request;
@@ -333,6 +334,9 @@ struct ieee80211_if_ibss {

	unsigned long request;
	unsigned long last_scan_completed;

	bool timer_running;

	bool fixed_bssid;
	bool fixed_channel;

@@ -358,6 +362,8 @@ struct ieee80211_if_mesh {
	struct timer_list mesh_path_timer;
	struct sk_buff_head skb_queue;

	unsigned long timers_running;

	bool housekeeping;

	u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
@@ -609,6 +615,21 @@ struct ieee80211_local {
	unsigned int filter_flags; /* FIF_* */
	struct iw_statistics wstats;
	bool tim_in_locked_section; /* see ieee80211_beacon_get() */

	/*
	 * suspended is true if we finished all the suspend _and_ we have
	 * not yet come up from resume. This is to be used by mac80211
	 * to ensure driver sanity during suspend and mac80211's own
	 * sanity. It can eventually be used for WoW as well.
	 */
	bool suspended;

	/*
	 * quiescing is true during the suspend process _only_ to
	 * ease timer cancelling etc.
	 */
	bool quiescing;

	int tx_headroom; /* required headroom for hardware/radiotap */

	/* 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,
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
				      struct ieee80211_channel_sw_ie *sw_elem,
				      struct ieee80211_bss *bss);
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);

/* IBSS code */
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,
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
			struct cfg80211_ibss_params *params);
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);

/* scan/BSS handling */
void ieee80211_scan_work(struct work_struct *work);
@@ -959,6 +984,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
int ieee80211_scan_results(struct ieee80211_local *local,
			   struct iw_request_info *info,
			   char *buf, size_t len);
void ieee80211_scan_cancel(struct ieee80211_local *local);
ieee80211_rx_result
ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
		  struct sk_buff *skb,
+40 −0
Original line number Diff line number Diff line
@@ -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 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
	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 @@ static void ieee80211_mesh_path_timer(unsigned long data)
	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 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
		  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)
{
+12 −0
Original line number Diff line number Diff line
@@ -267,6 +267,8 @@ void mesh_path_timer(unsigned long data);
void mesh_path_flush_by_nexthop(struct sta_info *sta);
void mesh_path_discard_frame(struct sk_buff *skb,
		struct ieee80211_sub_if_data *sdata);
void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
void mesh_path_restart(struct ieee80211_sub_if_data *sdata);

#ifdef CONFIG_MAC80211_MESH
extern int mesh_allocated;
@@ -294,10 +296,20 @@ static inline void mesh_path_activate(struct mesh_path *mpath)

void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);

void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
void mesh_plink_quiesce(struct sta_info *sta);
void mesh_plink_restart(struct sta_info *sta);
#else
#define mesh_allocated	0
static inline void
ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
{}
static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
{}
static inline void mesh_plink_quiesce(struct sta_info *sta) {}
static inline void mesh_plink_restart(struct sta_info *sta) {}
#endif

#endif /* IEEE80211S_H */
+7 −1
Original line number Diff line number Diff line
@@ -836,8 +836,14 @@ void mesh_path_timer(unsigned long data)
	mpath = rcu_dereference(mpath);
	if (!mpath)
		goto endmpathtimer;
	spin_lock_bh(&mpath->state_lock);
	sdata = mpath->sdata;

	if (sdata->local->quiescing) {
		rcu_read_unlock();
		return;
	}

	spin_lock_bh(&mpath->state_lock);
	if (mpath->flags & MESH_PATH_RESOLVED ||
			(!(mpath->flags & MESH_PATH_RESOLVING)))
		mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
Loading