aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-05-07 08:23:01 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-05-11 15:23:54 -0400
commit58905ca5b11a0ff3860f55b789cbbf052f7158a7 (patch)
tree7c7a4c937130855dfef41c2302deb7fb0b5a2eb7 /net/mac80211
parent02018b39a75057541c7946a9173561d1a76a0bfe (diff)
mac80211: fix scan channel race
When a software scan starts, it first sets sw_scanning, but leaves the scan_channel "unset" (it currently actually gets initialised to a default). Now, when something else tries to (re)configure the hardware in the window between these two events (after sw_scanning = true, but before scan_channel is set), the current code switches to the (unset!) scan_channel. This causes trouble, especially when switching bands and sending frames on the wrong channel. To work around this, leave scan_channel initialised to NULL and use it to determine whether or not a switch to a different channel should occur (and also use the same condition to check whether to adjust power for scan or not). Additionally, avoid reconfiguring the hardware completely when recalculating idle resulted in no changes, this was the problem that originally led us to discover the race condition in the first place, which was helpfully bisected by Pavel. This part of the patch should not be necessary with the other fixes, but not calling the ieee80211_hw_config function when we know it to be unnecessary is certainly a correct thing to do. Unfortunately, this patch cannot and does not fix the race condition completely, but due to the way the scan code is structured it makes the particular problem Pavel discovered (race while changing channel at the same time as transmitting frames) go away. To fix it completely, more work especially with locking configuration is needed. Bisected-by: Pavel Roskin <proski@gnu.org> Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/iface.c3
-rw-r--r--net/mac80211/main.c14
-rw-r--r--net/mac80211/scan.c1
3 files changed, 11 insertions, 7 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8b6daf0219f4..8c9f1c722cdb 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -964,5 +964,6 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)
964 mutex_lock(&local->iflist_mtx); 964 mutex_lock(&local->iflist_mtx);
965 chg = __ieee80211_recalc_idle(local); 965 chg = __ieee80211_recalc_idle(local);
966 mutex_unlock(&local->iflist_mtx); 966 mutex_unlock(&local->iflist_mtx);
967 ieee80211_hw_config(local, chg); 967 if (chg)
968 ieee80211_hw_config(local, chg);
968} 969}
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index b80bc80e46cf..76df5eabf268 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -154,15 +154,17 @@ static void ieee80211_master_set_multicast_list(struct net_device *dev)
154 154
155int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) 155int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
156{ 156{
157 struct ieee80211_channel *chan; 157 struct ieee80211_channel *chan, *scan_chan;
158 int ret = 0; 158 int ret = 0;
159 int power; 159 int power;
160 enum nl80211_channel_type channel_type; 160 enum nl80211_channel_type channel_type;
161 161
162 might_sleep(); 162 might_sleep();
163 163
164 if (local->sw_scanning) { 164 scan_chan = local->scan_channel;
165 chan = local->scan_channel; 165
166 if (scan_chan) {
167 chan = scan_chan;
166 channel_type = NL80211_CHAN_NO_HT; 168 channel_type = NL80211_CHAN_NO_HT;
167 } else { 169 } else {
168 chan = local->oper_channel; 170 chan = local->oper_channel;
@@ -176,7 +178,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
176 changed |= IEEE80211_CONF_CHANGE_CHANNEL; 178 changed |= IEEE80211_CONF_CHANGE_CHANNEL;
177 } 179 }
178 180
179 if (local->sw_scanning) 181 if (scan_chan)
180 power = chan->max_power; 182 power = chan->max_power;
181 else 183 else
182 power = local->power_constr_level ? 184 power = local->power_constr_level ?
@@ -859,8 +861,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
859 if (!local->oper_channel) { 861 if (!local->oper_channel) {
860 /* init channel we're on */ 862 /* init channel we're on */
861 local->hw.conf.channel = 863 local->hw.conf.channel =
862 local->oper_channel = 864 local->oper_channel = &sband->channels[0];
863 local->scan_channel = &sband->channels[0]; 865 local->hw.conf.channel_type = NL80211_CHAN_NO_HT;
864 } 866 }
865 channels += sband->n_channels; 867 channels += sband->n_channels;
866 868
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index c99ef8d04d3d..e51b99b1473c 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -298,6 +298,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
298 was_hw_scan = local->hw_scanning; 298 was_hw_scan = local->hw_scanning;
299 local->hw_scanning = false; 299 local->hw_scanning = false;
300 local->sw_scanning = false; 300 local->sw_scanning = false;
301 local->scan_channel = NULL;
301 302
302 /* we only have to protect scan_req and hw/sw scan */ 303 /* we only have to protect scan_req and hw/sw scan */
303 mutex_unlock(&local->scan_mtx); 304 mutex_unlock(&local->scan_mtx);