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

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
...@@ -737,6 +737,9 @@ static void ieee80211_ibss_work(struct work_struct *work) ...@@ -737,6 +737,9 @@ static void ieee80211_ibss_work(struct work_struct *work)
struct ieee80211_if_ibss *ifibss; struct ieee80211_if_ibss *ifibss;
struct sk_buff *skb; struct sk_buff *skb;
if (WARN_ON(local->suspended))
return;
if (!netif_running(sdata->dev)) if (!netif_running(sdata->dev))
return; return;
...@@ -773,10 +776,36 @@ static void ieee80211_ibss_timer(unsigned long data) ...@@ -773,10 +776,36 @@ static void ieee80211_ibss_timer(unsigned long data)
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
if (local->quiescing) {
ifibss->timer_running = true;
return;
}
set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
queue_work(local->hw.workqueue, &ifibss->work); 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) void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
......
...@@ -293,6 +293,7 @@ struct ieee80211_if_managed { ...@@ -293,6 +293,7 @@ struct ieee80211_if_managed {
int auth_tries; /* retries for auth req */ int auth_tries; /* retries for auth req */
int assoc_tries; /* retries for assoc req */ int assoc_tries; /* retries for assoc req */
unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */ bool powersave; /* powersave requested for this iface */
unsigned long request; unsigned long request;
...@@ -333,6 +334,9 @@ struct ieee80211_if_ibss { ...@@ -333,6 +334,9 @@ struct ieee80211_if_ibss {
unsigned long request; unsigned long request;
unsigned long last_scan_completed; unsigned long last_scan_completed;
bool timer_running;
bool fixed_bssid; bool fixed_bssid;
bool fixed_channel; bool fixed_channel;
...@@ -358,6 +362,8 @@ struct ieee80211_if_mesh { ...@@ -358,6 +362,8 @@ struct ieee80211_if_mesh {
struct timer_list mesh_path_timer; struct timer_list mesh_path_timer;
struct sk_buff_head skb_queue; struct sk_buff_head skb_queue;
unsigned long timers_running;
bool housekeeping; bool housekeeping;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
...@@ -609,6 +615,21 @@ struct ieee80211_local { ...@@ -609,6 +615,21 @@ struct ieee80211_local {
unsigned int filter_flags; /* FIF_* */ unsigned int filter_flags; /* FIF_* */
struct iw_statistics wstats; struct iw_statistics wstats;
bool tim_in_locked_section; /* see ieee80211_beacon_get() */ 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 */ int tx_headroom; /* required headroom for hardware/radiotap */
/* Tasklet and skb queue to process calls from IRQ mode. All frames /* 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,6 +958,8 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_sw_ie *sw_elem, struct ieee80211_channel_sw_ie *sw_elem,
struct ieee80211_bss *bss); 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 */ /* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); 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,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, int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params); struct cfg80211_ibss_params *params);
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); 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 */ /* scan/BSS handling */
void ieee80211_scan_work(struct work_struct *work); void ieee80211_scan_work(struct work_struct *work);
...@@ -959,6 +984,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, ...@@ -959,6 +984,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
int ieee80211_scan_results(struct ieee80211_local *local, int ieee80211_scan_results(struct ieee80211_local *local,
struct iw_request_info *info, struct iw_request_info *info,
char *buf, size_t len); char *buf, size_t len);
void ieee80211_scan_cancel(struct ieee80211_local *local);
ieee80211_rx_result ieee80211_rx_result
ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, struct sk_buff *skb,
......
...@@ -21,6 +21,9 @@ ...@@ -21,6 +21,9 @@
#define CAPAB_OFFSET 17 #define CAPAB_OFFSET 17
#define ACCEPT_PLINKS 0x80 #define ACCEPT_PLINKS 0x80
#define TMR_RUNNING_HK 0
#define TMR_RUNNING_MP 1
int mesh_allocated; int mesh_allocated;
static struct kmem_cache *rm_cache; static struct kmem_cache *rm_cache;
...@@ -45,6 +48,12 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) ...@@ -45,6 +48,12 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
ifmsh->housekeeping = true; ifmsh->housekeeping = true;
if (local->quiescing) {
set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
return;
}
queue_work(local->hw.workqueue, &ifmsh->work); queue_work(local->hw.workqueue, &ifmsh->work);
} }
...@@ -343,6 +352,11 @@ static void ieee80211_mesh_path_timer(unsigned long data) ...@@ -343,6 +352,11 @@ static void ieee80211_mesh_path_timer(unsigned long data)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local; 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); queue_work(local->hw.workqueue, &ifmsh->work);
} }
...@@ -424,6 +438,32 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, ...@@ -424,6 +438,32 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); 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) void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
{ {
......
...@@ -267,6 +267,8 @@ void mesh_path_timer(unsigned long data); ...@@ -267,6 +267,8 @@ void mesh_path_timer(unsigned long data);
void mesh_path_flush_by_nexthop(struct sta_info *sta); void mesh_path_flush_by_nexthop(struct sta_info *sta);
void mesh_path_discard_frame(struct sk_buff *skb, void mesh_path_discard_frame(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata); 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 #ifdef CONFIG_MAC80211_MESH
extern int mesh_allocated; extern int mesh_allocated;
...@@ -294,10 +296,20 @@ static inline void mesh_path_activate(struct mesh_path *mpath) ...@@ -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_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 #else
#define mesh_allocated 0 #define mesh_allocated 0
static inline void static inline void
ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} 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
#endif /* IEEE80211S_H */ #endif /* IEEE80211S_H */
...@@ -836,8 +836,14 @@ void mesh_path_timer(unsigned long data) ...@@ -836,8 +836,14 @@ void mesh_path_timer(unsigned long data)
mpath = rcu_dereference(mpath); mpath = rcu_dereference(mpath);
if (!mpath) if (!mpath)
goto endmpathtimer; goto endmpathtimer;
spin_lock_bh(&mpath->state_lock);
sdata = mpath->sdata; sdata = mpath->sdata;
if (sdata->local->quiescing) {
rcu_read_unlock();
return;
}
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_RESOLVED || if (mpath->flags & MESH_PATH_RESOLVED ||
(!(mpath->flags & MESH_PATH_RESOLVING))) (!(mpath->flags & MESH_PATH_RESOLVING)))
mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED); mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
......
...@@ -266,6 +266,11 @@ static void mesh_plink_timer(unsigned long data) ...@@ -266,6 +266,11 @@ static void mesh_plink_timer(unsigned long data)
*/ */
sta = (struct sta_info *) data; sta = (struct sta_info *) data;
if (sta->sdata->local->quiescing) {
sta->plink_timer_was_running = true;
return;
}
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->lock);
if (sta->ignore_plink_timer) { if (sta->ignore_plink_timer) {
sta->ignore_plink_timer = false; sta->ignore_plink_timer = false;
...@@ -322,6 +327,22 @@ static void mesh_plink_timer(unsigned long data) ...@@ -322,6 +327,22 @@ static void mesh_plink_timer(unsigned long data)
} }
} }
#ifdef CONFIG_PM
void mesh_plink_quiesce(struct sta_info *sta)
{
if (del_timer_sync(&sta->plink_timer))
sta->plink_timer_was_running = true;
}
void mesh_plink_restart(struct sta_info *sta)
{
if (sta->plink_timer_was_running) {
add_timer(&sta->plink_timer);
sta->plink_timer_was_running = false;
}
}
#endif
static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
{ {
sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
#define IEEE80211_PROBE_IDLE_TIME (60 * HZ) #define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) #define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1
/* utils */ /* utils */
static int ecw2cw(int ecw) static int ecw2cw(int ecw)
{ {
...@@ -521,6 +524,11 @@ static void ieee80211_chswitch_timer(unsigned long data) ...@@ -521,6 +524,11 @@ static void ieee80211_chswitch_timer(unsigned long data)
(struct ieee80211_sub_if_data *) data; (struct ieee80211_sub_if_data *) data;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (sdata->local->quiescing) {
set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
return;
}
queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
} }
...@@ -714,6 +722,9 @@ void ieee80211_dynamic_ps_timer(unsigned long data) ...@@ -714,6 +722,9 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
{ {
struct ieee80211_local *local = (void *) data; struct ieee80211_local *local = (void *) data;
if (local->quiescing)
return;
queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
} }
...@@ -2108,6 +2119,11 @@ static void ieee80211_sta_timer(unsigned long data) ...@@ -2108,6 +2119,11 @@ static void ieee80211_sta_timer(unsigned long data)
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
if (local->quiescing) {
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
return;
}
set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
queue_work(local->hw.workqueue, &ifmgd->work); queue_work(local->hw.workqueue, &ifmgd->work);
} }
...@@ -2240,6 +2256,17 @@ static void ieee80211_sta_work(struct work_struct *work) ...@@ -2240,6 +2256,17 @@ static void ieee80211_sta_work(struct work_struct *work)
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return; return;
/*
* Nothing should have been stuffed into the workqueue during
* the suspend->resume cycle. If this WARN is seen then there
* is a bug with either the driver suspend or something in
* mac80211 stuffing into the workqueue which we haven't yet
* cleared during mac80211's suspend cycle.
*/
if (WARN_ON(local->suspended))
return;
ifmgd = &sdata->u.mgd; ifmgd = &sdata->u.mgd;
while ((skb = skb_dequeue(&ifmgd->skb_queue))) while ((skb = skb_dequeue(&ifmgd->skb_queue)))
...@@ -2307,6 +2334,38 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) ...@@ -2307,6 +2334,38 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
} }
} }
#ifdef CONFIG_PM
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
/*
* we need to use atomic bitops for the running bits
* only because both timers might fire at the same
* time -- the code here is properly synchronised.
*/
cancel_work_sync(&ifmgd->work);
cancel_work_sync(&ifmgd->beacon_loss_work);
if (del_timer_sync(&ifmgd->timer))
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
cancel_work_sync(&ifmgd->chswitch_work);
if (del_timer_sync(&ifmgd->chswitch_timer))
set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
}
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
add_timer(&ifmgd->timer);
if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
add_timer(&ifmgd->chswitch_timer);
}
#endif
/* interface setup */ /* interface setup */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
{ {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "mesh.h"
#include "driver-ops.h" #include "driver-ops.h"
#include "led.h" #include "led.h"
...@@ -13,11 +14,30 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) ...@@ -13,11 +14,30 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
struct sta_info *sta; struct sta_info *sta;
unsigned long flags; unsigned long flags;
ieee80211_scan_cancel(local);
ieee80211_stop_queues_by_reason(hw, ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND); IEEE80211_QUEUE_STOP_REASON_SUSPEND);
/* flush out all packets */
synchronize_net();
local->quiescing = true;
/* make quiescing visible to timers everywhere */
mb();
flush_workqueue(local->hw.workqueue); flush_workqueue(local->hw.workqueue);
/* Don't try to run timers while suspended. */
del_timer_sync(&local->sta_cleanup);
/*
* Note that this particular timer doesn't need to be
* restarted at resume.
*/
cancel_work_sync(&local->dynamic_ps_enable_work);
del_timer_sync(&local->dynamic_ps_timer);
/* disable keys */ /* disable keys */
list_for_each_entry(sdata, &local->interfaces, list) list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_disable_keys(sdata); ieee80211_disable_keys(sdata);
...@@ -35,10 +55,20 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) ...@@ -35,10 +55,20 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
rcu_read_unlock(); rcu_read_unlock();
/* flush again, in case driver queued work */
flush_workqueue(local->hw.workqueue);
/* stop hardware - this must stop RX */
if (local->open_count) {
ieee80211_led_radio(local, false);
drv_stop(local);
}
/* remove STAs */ /* remove STAs */
if (local->ops->sta_notify) { spin_lock_irqsave(&local->sta_lock, flags);
spin_lock_irqsave(&local->sta_lock, flags); list_for_each_entry(sta, &local->sta_list, list) {
list_for_each_entry(sta, &local->sta_list, list) { if (local->ops->sta_notify) {
sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss, sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data, struct ieee80211_sub_if_data,
...@@ -47,29 +77,43 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) ...@@ -47,29 +77,43 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE, drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE,
&sta->sta); &sta->sta);
} }
spin_unlock_irqrestore(&local->sta_lock, flags);
mesh_plink_quiesce(sta);
} }
spin_unlock_irqrestore(&local->sta_lock, flags);
/* remove all interfaces */ /* remove all interfaces */
list_for_each_entry(sdata, &local->interfaces, list) { list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && switch(sdata->vif.type) {
sdata->vif.type != NL80211_IFTYPE_MONITOR && case NL80211_IFTYPE_STATION:
netif_running(sdata->dev)) { ieee80211_sta_quiesce(sdata);
conf.vif = &sdata->vif; break;
conf.type = sdata->vif.type; case NL80211_IFTYPE_ADHOC:
conf.mac_addr = sdata->dev->dev_addr; ieee80211_ibss_quiesce(sdata);
drv_remove_interface(local, &conf); break;
case NL80211_IFTYPE_MESH_POINT:
ieee80211_mesh_quiesce(sdata);
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
/* don't tell driver about this */
continue;
default:
break;
} }
}
/* flush again, in case driver queued work */ if (!netif_running(sdata->dev))
flush_workqueue(local->hw.workqueue); continue;
/* stop hardware */ conf.vif = &sdata->vif;
if (local->open_count) { conf.type = sdata->vif.type;
ieee80211_led_radio(local, false); conf.mac_addr = sdata->dev->dev_addr;
drv_stop(local); drv_remove_interface(local, &conf);
} }
local->suspended = true;
local->quiescing = false;
return 0; return 0;
} }
......
...@@ -631,3 +631,21 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, ...@@ -631,3 +631,21 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->scan_mtx); mutex_unlock(&local->scan_mtx);
return ret; return ret;
} }
void ieee80211_scan_cancel(struct ieee80211_local *local)
{
bool swscan;
cancel_delayed_work_sync(&local->scan_work);
/*
* Only call this function when a scan can't be
* queued -- mostly at suspend under RTNL.
*/
mutex_lock(&local->scan_mtx);
swscan = local->sw_scanning;
mutex_unlock(&local->scan_mtx);
if (swscan)
ieee80211_scan_completed(&local->hw, true);
}
...@@ -611,6 +611,9 @@ static void sta_info_cleanup(unsigned long data) ...@@ -611,6 +611,9 @@ static void sta_info_cleanup(unsigned long data)
sta_info_cleanup_expire_buffered(local, sta); sta_info_cleanup_expire_buffered(local, sta);
rcu_read_unlock(); rcu_read_unlock();
if (local->quiescing)
return;
local->sta_cleanup.expires = local->sta_cleanup.expires =
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
add_timer(&local->sta_cleanup); add_timer(&local->sta_cleanup);
......
...@@ -293,6 +293,7 @@ struct sta_info { ...@@ -293,6 +293,7 @@ struct sta_info {
__le16 reason; __le16 reason;
u8 plink_retries; u8 plink_retries;
bool ignore_plink_timer; bool ignore_plink_timer;
bool plink_timer_was_running;
enum plink_state plink_state; enum plink_state plink_state;
u32 plink_timeout; u32 plink_timeout;
struct timer_list plink_timer; struct timer_list plink_timer;
......
...@@ -1034,6 +1034,13 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1034,6 +1034,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct sta_info *sta; struct sta_info *sta;
unsigned long flags; unsigned long flags;
int res; int res;
bool from_suspend = local->suspended;
/*
* We're going to start the hardware, at that point
* we are no longer suspended and can RX frames.
*/
local->suspended = false;
/* restart hardware */ /* restart hardware */
if (local->open_count) { if (local->open_count) {
...@@ -1058,6 +1065,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1058,6 +1065,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (local->ops->sta_notify) { if (local->ops->sta_notify) {
spin_lock_irqsave(&local->sta_lock, flags); spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(sta, &local->sta_list, list) { list_for_each_entry(sta, &local->sta_list, list) {
sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss, sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data, struct ieee80211_sub_if_data,
...@@ -1128,5 +1136,40 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1128,5 +1136,40 @@ int ieee80211_reconfig(struct ieee80211_local *local)
ieee80211_wake_queues_by_reason(hw, ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND); IEEE80211_QUEUE_STOP_REASON_SUSPEND);
/*
* If this is for hw restart things are still running.
* We may want to change that later, however.
*/
if (!from_suspend)
return 0;
#ifdef CONFIG_PM
local->suspended = false;
list_for_each_entry(sdata, &local->interfaces, list) {
switch(sdata->vif.type) {
case NL80211_IFTYPE_STATION:
ieee80211_sta_restart(sdata);
break;
case NL80211_IFTYPE_ADHOC:
ieee80211_ibss_restart(sdata);
break;
case NL80211_IFTYPE_MESH_POINT:
ieee80211_mesh_restart(sdata);
break;
default:
break;
}
}
add_timer(&local->sta_cleanup);
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(sta, &local->sta_list, list)
mesh_plink_restart(sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
#else
WARN_ON(1);
#endif
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment