Commit f6837ba8 authored by Johannes Berg's avatar Johannes Berg

mac80211: handle failed restart/resume better

When the driver fails during HW restart or resume, the whole
stack goes into a very confused state with interfaces being
up while the hardware is down etc.

Address this by shutting down everything; we'll run into a
lot of warnings in the process but that's better than having
the whole stack get messed up.
Reviewed-by: default avatarArik Nemtsov <arik@wizery.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent f29f58a9
...@@ -4771,6 +4771,20 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, ...@@ -4771,6 +4771,20 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,
void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
gfp_t gfp); gfp_t gfp);
/**
* cfg80211_shutdown_all_interfaces - shut down all interfaces for a wiphy
* @wiphy: the wiphy to shut down
*
* This function shuts down all interfaces belonging to this wiphy by
* calling dev_close() (and treating non-netdev interfaces as needed).
* It shouldn't really be used unless there are some fatal device errors
* that really can't be recovered in any other way.
*
* Callers must hold the RTNL and be able to deal with callbacks into
* the driver while the function is running.
*/
void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
/* Logging, debugging and troubleshooting/diagnostic helpers. */ /* Logging, debugging and troubleshooting/diagnostic helpers. */
/* wiphy_printk helpers, similar to dev_printk */ /* wiphy_printk helpers, similar to dev_printk */
......
This diff is collapsed.
...@@ -1459,6 +1459,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, ...@@ -1459,6 +1459,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
struct cfg80211_sched_scan_request *req); struct cfg80211_sched_scan_request *req);
int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
void ieee80211_sched_scan_end(struct ieee80211_local *local);
void ieee80211_sched_scan_stopped_work(struct work_struct *work); void ieee80211_sched_scan_stopped_work(struct work_struct *work);
/* off-channel helpers */ /* off-channel helpers */
......
...@@ -1076,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw) ...@@ -1076,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
} }
EXPORT_SYMBOL(ieee80211_sched_scan_results); EXPORT_SYMBOL(ieee80211_sched_scan_results);
void ieee80211_sched_scan_stopped_work(struct work_struct *work) void ieee80211_sched_scan_end(struct ieee80211_local *local)
{ {
struct ieee80211_local *local =
container_of(work, struct ieee80211_local,
sched_scan_stopped_work);
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
if (!rcu_access_pointer(local->sched_scan_sdata)) { if (!rcu_access_pointer(local->sched_scan_sdata)) {
...@@ -1099,6 +1095,15 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) ...@@ -1099,6 +1095,15 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
cfg80211_sched_scan_stopped(local->hw.wiphy); cfg80211_sched_scan_stopped(local->hw.wiphy);
} }
void ieee80211_sched_scan_stopped_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local,
sched_scan_stopped_work);
ieee80211_sched_scan_end(local);
}
void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
......
...@@ -1457,6 +1457,44 @@ void ieee80211_stop_device(struct ieee80211_local *local) ...@@ -1457,6 +1457,44 @@ void ieee80211_stop_device(struct ieee80211_local *local)
drv_stop(local); drv_stop(local);
} }
static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_chanctx *ctx;
/*
* We get here if during resume the device can't be restarted properly.
* We might also get here if this happens during HW reset, which is a
* slightly different situation and we need to drop all connections in
* the latter case.
*
* Ask cfg80211 to turn off all interfaces, this will result in more
* warnings but at least we'll then get into a clean stopped state.
*/
local->resuming = false;
local->suspended = false;
local->started = false;
/* scheduled scan clearly can't be running any more, but tell
* cfg80211 and clear local state
*/
ieee80211_sched_scan_end(local);
list_for_each_entry(sdata, &local->interfaces, list)
sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
/* Mark channel contexts as not being in the driver any more to avoid
* removing them from the driver during the shutdown process...
*/
mutex_lock(&local->chanctx_mtx);
list_for_each_entry(ctx, &local->chanctx_list, list)
ctx->driver_present = false;
mutex_unlock(&local->chanctx_mtx);
cfg80211_shutdown_all_interfaces(local->hw.wiphy);
}
static void ieee80211_assign_chanctx(struct ieee80211_local *local, static void ieee80211_assign_chanctx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata) struct ieee80211_sub_if_data *sdata)
{ {
...@@ -1520,9 +1558,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1520,9 +1558,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
*/ */
res = drv_start(local); res = drv_start(local);
if (res) { if (res) {
WARN(local->suspended, "Hardware became unavailable " if (local->suspended)
"upon resume. This could be a software issue " WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
"prior to suspend or a hardware issue.\n"); else
WARN(1, "Hardware became unavailable during restart.\n");
ieee80211_handle_reconfig_failure(local);
return res; return res;
} }
......
...@@ -210,15 +210,12 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, ...@@ -210,15 +210,12 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
} }
} }
static int cfg80211_rfkill_set_block(void *data, bool blocked) void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
{ {
struct cfg80211_registered_device *rdev = data; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct wireless_dev *wdev; struct wireless_dev *wdev;
if (!blocked) ASSERT_RTNL();
return 0;
rtnl_lock();
list_for_each_entry(wdev, &rdev->wdev_list, list) { list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (wdev->netdev) { if (wdev->netdev) {
...@@ -234,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) ...@@ -234,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
break; break;
} }
} }
}
EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces);
static int cfg80211_rfkill_set_block(void *data, bool blocked)
{
struct cfg80211_registered_device *rdev = data;
if (!blocked)
return 0;
rtnl_lock();
cfg80211_shutdown_all_interfaces(&rdev->wiphy);
rtnl_unlock(); rtnl_unlock();
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