diff options
author | Eliad Peller <eliad@wizery.com> | 2015-10-25 11:59:36 +0300 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2015-11-03 12:41:12 +0300 |
commit | 968a76cef3d1bb9a3b4d135cd788056e742859f3 (patch) | |
tree | dfe73df7dae4879fe96b045cdffc0d2d64e829be /net/mac80211/driver-ops.c | |
parent | c189a685b83955a39884dc2bdae531bc0adf3b98 (diff) | |
download | linux-968a76cef3d1bb9a3b4d135cd788056e742859f3.tar.xz |
mac80211: call drv_stop only if driver is started
If drv_start() fails during hw_restart, all the running
interfaces are being closed/stopped, which results in
drv_stop() being called, although the driver was never
started successfully.
This might cause drivers to perform operations on uninitialized
memory (as they assume it was initialized on drv_start)
Consider the local->started flag, and call the driver's stop()
op only if drv_start() succeeded before.
Move drv_start() and drv_stop() to driver-ops.c, as they are no
longer simple wrappers.
Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/driver-ops.c')
-rw-r--r-- | net/mac80211/driver-ops.c | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index a1d54318f16c..9f97343f13fd 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -1,4 +1,6 @@ /* + * Copyright 2015 Intel Deutschland GmbH + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -8,6 +10,48 @@ #include "trace.h" #include "driver-ops.h" +int drv_start(struct ieee80211_local *local) +{ + int ret; + + might_sleep(); + + if (WARN_ON(local->started)) + return -EALREADY; + + trace_drv_start(local); + local->started = true; + /* allow rx frames */ + smp_mb(); + ret = local->ops->start(&local->hw); + trace_drv_return_int(local, ret); + + if (ret) + local->started = false; + + return ret; +} + +void drv_stop(struct ieee80211_local *local) +{ + might_sleep(); + + if (WARN_ON(!local->started)) + return; + + trace_drv_stop(local); + local->ops->stop(&local->hw); + trace_drv_return_void(local); + + /* sync away all work on the tasklet before clearing started */ + tasklet_disable(&local->tasklet); + tasklet_enable(&local->tasklet); + + barrier(); + + local->started = false; +} + int drv_add_interface(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { |