aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorStanislaw Gruszka <sgruszka@redhat.com>2011-06-02 12:17:15 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-06-03 14:22:06 -0400
commit6f213ff1919fab6f8244ceae55631b5d6ef750a7 (patch)
treeed2f6312721b9391c85dfcdc6f44a0a5dd208cfb /drivers/net
parenta99168eece601d2a79ecfcb968ce226f2f30cf98 (diff)
iwlagn: fix channel switch locking
We use priv->mutex to avoid race conditions between iwl_chswitch_done() and iwlagn_mac_channel_switch(), when marking channel switch in progress. But iwl_chswitch_done() can be called in atomic context from iwl_rx_csa() or with mutex already taken from iwlagn_commit_rxon(). These bugs were introduced by: commit 79d07325502e73508f917475bc1617b60979dd94 Author: Wey-Yi Guy <wey-yi.w.guy@intel.com> Date: Thu May 6 08:54:11 2010 -0700 iwlwifi: support channel switch offload in driver To fix remove mutex from iwl_chswitch_done() and use atomic bitops for marking channel switch pending. Also remove iwl2030_hw_channel_switch() since 2000 series adapters are 2.4GHz only devices. Cc: stable@kernel.org # 2.6.36+ Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com> Acked-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-2000.c74
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rxon.c6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h13
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rx.c24
9 files changed, 28 insertions, 119 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c
index 86feec86d130..2282279cffc4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-2000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-2000.c
@@ -177,79 +177,6 @@ static int iwl2000_hw_set_hw_params(struct iwl_priv *priv)
177 return 0; 177 return 0;
178} 178}
179 179
180static int iwl2030_hw_channel_switch(struct iwl_priv *priv,
181 struct ieee80211_channel_switch *ch_switch)
182{
183 /*
184 * MULTI-FIXME
185 * See iwl_mac_channel_switch.
186 */
187 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
188 struct iwl6000_channel_switch_cmd cmd;
189 const struct iwl_channel_info *ch_info;
190 u32 switch_time_in_usec, ucode_switch_time;
191 u16 ch;
192 u32 tsf_low;
193 u8 switch_count;
194 u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval);
195 struct ieee80211_vif *vif = ctx->vif;
196 struct iwl_host_cmd hcmd = {
197 .id = REPLY_CHANNEL_SWITCH,
198 .len = { sizeof(cmd), },
199 .flags = CMD_SYNC,
200 .data = { &cmd, },
201 };
202
203 cmd.band = priv->band == IEEE80211_BAND_2GHZ;
204 ch = ch_switch->channel->hw_value;
205 IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
206 ctx->active.channel, ch);
207 cmd.channel = cpu_to_le16(ch);
208 cmd.rxon_flags = ctx->staging.flags;
209 cmd.rxon_filter_flags = ctx->staging.filter_flags;
210 switch_count = ch_switch->count;
211 tsf_low = ch_switch->timestamp & 0x0ffffffff;
212 /*
213 * calculate the ucode channel switch time
214 * adding TSF as one of the factor for when to switch
215 */
216 if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
217 if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
218 beacon_interval)) {
219 switch_count -= (priv->ucode_beacon_time -
220 tsf_low) / beacon_interval;
221 } else
222 switch_count = 0;
223 }
224 if (switch_count <= 1)
225 cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
226 else {
227 switch_time_in_usec =
228 vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
229 ucode_switch_time = iwl_usecs_to_beacons(priv,
230 switch_time_in_usec,
231 beacon_interval);
232 cmd.switch_time = iwl_add_beacon_time(priv,
233 priv->ucode_beacon_time,
234 ucode_switch_time,
235 beacon_interval);
236 }
237 IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
238 cmd.switch_time);
239 ch_info = iwl_get_channel_info(priv, priv->band, ch);
240 if (ch_info)
241 cmd.expect_beacon = is_channel_radar(ch_info);
242 else {
243 IWL_ERR(priv, "invalid channel switch from %u to %u\n",
244 ctx->active.channel, ch);
245 return -EFAULT;
246 }
247 priv->switch_rxon.channel = cmd.channel;
248 priv->switch_rxon.switch_in_progress = true;
249
250 return iwl_send_cmd_sync(priv, &hcmd);
251}
252
253static struct iwl_lib_ops iwl2000_lib = { 180static struct iwl_lib_ops iwl2000_lib = {
254 .set_hw_params = iwl2000_hw_set_hw_params, 181 .set_hw_params = iwl2000_hw_set_hw_params,
255 .rx_handler_setup = iwlagn_rx_handler_setup, 182 .rx_handler_setup = iwlagn_rx_handler_setup,
@@ -258,7 +185,6 @@ static struct iwl_lib_ops iwl2000_lib = {
258 .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr, 185 .is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
259 .send_tx_power = iwlagn_send_tx_power, 186 .send_tx_power = iwlagn_send_tx_power,
260 .update_chain_flags = iwl_update_chain_flags, 187 .update_chain_flags = iwl_update_chain_flags,
261 .set_channel_switch = iwl2030_hw_channel_switch,
262 .apm_ops = { 188 .apm_ops = {
263 .init = iwl_apm_init, 189 .init = iwl_apm_init,
264 .config = iwl2000_nic_config, 190 .config = iwl2000_nic_config,
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index a70b8cfafda1..5b721c56aee9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -331,8 +331,6 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
331 ctx->active.channel, ch); 331 ctx->active.channel, ch);
332 return -EFAULT; 332 return -EFAULT;
333 } 333 }
334 priv->switch_rxon.channel = cmd.channel;
335 priv->switch_rxon.switch_in_progress = true;
336 334
337 return iwl_send_cmd_sync(priv, &hcmd); 335 return iwl_send_cmd_sync(priv, &hcmd);
338} 336}
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index fda6fe08cf91..fbe565c816e3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -270,8 +270,6 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
270 ctx->active.channel, ch); 270 ctx->active.channel, ch);
271 return -EFAULT; 271 return -EFAULT;
272 } 272 }
273 priv->switch_rxon.channel = cmd.channel;
274 priv->switch_rxon.switch_in_progress = true;
275 273
276 return iwl_send_cmd_sync(priv, &hcmd); 274 return iwl_send_cmd_sync(priv, &hcmd);
277} 275}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
index a95ad84c5377..2532c7db3aff 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
@@ -342,10 +342,10 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
342 * receive commit_rxon request 342 * receive commit_rxon request
343 * abort any previous channel switch if still in process 343 * abort any previous channel switch if still in process
344 */ 344 */
345 if (priv->switch_rxon.switch_in_progress && 345 if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) &&
346 (priv->switch_rxon.channel != ctx->staging.channel)) { 346 (priv->switch_channel != ctx->staging.channel)) {
347 IWL_DEBUG_11H(priv, "abort channel switch on %d\n", 347 IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
348 le16_to_cpu(priv->switch_rxon.channel)); 348 le16_to_cpu(priv->switch_channel));
349 iwl_chswitch_done(priv, false); 349 iwl_chswitch_done(priv, false);
350 } 350 }
351 351
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index a662adcb2adb..8e1942ebd9a0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2843,16 +2843,13 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
2843 goto out; 2843 goto out;
2844 2844
2845 if (test_bit(STATUS_EXIT_PENDING, &priv->status) || 2845 if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
2846 test_bit(STATUS_SCANNING, &priv->status)) 2846 test_bit(STATUS_SCANNING, &priv->status) ||
2847 test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
2847 goto out; 2848 goto out;
2848 2849
2849 if (!iwl_is_associated_ctx(ctx)) 2850 if (!iwl_is_associated_ctx(ctx))
2850 goto out; 2851 goto out;
2851 2852
2852 /* channel switch in progress */
2853 if (priv->switch_rxon.switch_in_progress == true)
2854 goto out;
2855
2856 if (priv->cfg->ops->lib->set_channel_switch) { 2853 if (priv->cfg->ops->lib->set_channel_switch) {
2857 2854
2858 ch = channel->hw_value; 2855 ch = channel->hw_value;
@@ -2901,15 +2898,19 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
2901 * at this point, staging_rxon has the 2898 * at this point, staging_rxon has the
2902 * configuration for channel switch 2899 * configuration for channel switch
2903 */ 2900 */
2901 set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
2902 priv->switch_channel = cpu_to_le16(ch);
2904 if (priv->cfg->ops->lib->set_channel_switch(priv, 2903 if (priv->cfg->ops->lib->set_channel_switch(priv,
2905 ch_switch)) 2904 ch_switch)) {
2906 priv->switch_rxon.switch_in_progress = false; 2905 clear_bit(STATUS_CHANNEL_SWITCH_PENDING,
2906 &priv->status);
2907 priv->switch_channel = 0;
2908 ieee80211_chswitch_done(ctx->vif, false);
2909 }
2907 } 2910 }
2908 } 2911 }
2909out: 2912out:
2910 mutex_unlock(&priv->mutex); 2913 mutex_unlock(&priv->mutex);
2911 if (!priv->switch_rxon.switch_in_progress)
2912 ieee80211_chswitch_done(ctx->vif, false);
2913 IWL_DEBUG_MAC80211(priv, "leave\n"); 2914 IWL_DEBUG_MAC80211(priv, "leave\n");
2914} 2915}
2915 2916
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 4653deada05b..213c80c6a668 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -843,12 +843,8 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success)
843 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 843 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
844 return; 844 return;
845 845
846 if (priv->switch_rxon.switch_in_progress) { 846 if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
847 ieee80211_chswitch_done(ctx->vif, is_success); 847 ieee80211_chswitch_done(ctx->vif, is_success);
848 mutex_lock(&priv->mutex);
849 priv->switch_rxon.switch_in_progress = false;
850 mutex_unlock(&priv->mutex);
851 }
852} 848}
853 849
854#ifdef CONFIG_IWLWIFI_DEBUG 850#ifdef CONFIG_IWLWIFI_DEBUG
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 3bb76f6ea410..a54d416ec345 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -560,6 +560,7 @@ void iwlcore_free_geos(struct iwl_priv *priv);
560#define STATUS_POWER_PMI 16 560#define STATUS_POWER_PMI 16
561#define STATUS_FW_ERROR 17 561#define STATUS_FW_ERROR 17
562#define STATUS_DEVICE_ENABLED 18 562#define STATUS_DEVICE_ENABLED 18
563#define STATUS_CHANNEL_SWITCH_PENDING 19
563 564
564 565
565static inline int iwl_is_ready(struct iwl_priv *priv) 566static inline int iwl_is_ready(struct iwl_priv *priv)
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 22a6e3ec7094..c8de236c141b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -982,17 +982,6 @@ struct traffic_stats {
982}; 982};
983 983
984/* 984/*
985 * iwl_switch_rxon: "channel switch" structure
986 *
987 * @ switch_in_progress: channel switch in progress
988 * @ channel: new channel
989 */
990struct iwl_switch_rxon {
991 bool switch_in_progress;
992 __le16 channel;
993};
994
995/*
996 * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds 985 * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds
997 * to perform continuous uCode event logging operation if enabled 986 * to perform continuous uCode event logging operation if enabled
998 */ 987 */
@@ -1287,7 +1276,7 @@ struct iwl_priv {
1287 1276
1288 struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; 1277 struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX];
1289 1278
1290 struct iwl_switch_rxon switch_rxon; 1279 __le16 switch_channel;
1291 1280
1292 struct { 1281 struct {
1293 u32 error_event_table; 1282 u32 error_event_table;
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 0053e9ea9021..b774517aa9fa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -250,19 +250,19 @@ static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
250 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 250 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
251 struct iwl_rxon_cmd *rxon = (void *)&ctx->active; 251 struct iwl_rxon_cmd *rxon = (void *)&ctx->active;
252 252
253 if (priv->switch_rxon.switch_in_progress) { 253 if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
254 if (!le32_to_cpu(csa->status) && 254 return;
255 (csa->channel == priv->switch_rxon.channel)) { 255
256 rxon->channel = csa->channel; 256 if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) {
257 ctx->staging.channel = csa->channel; 257 rxon->channel = csa->channel;
258 IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", 258 ctx->staging.channel = csa->channel;
259 le16_to_cpu(csa->channel)); 259 IWL_DEBUG_11H(priv, "CSA notif: channel %d\n",
260 iwl_chswitch_done(priv, true);
261 } else {
262 IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
263 le16_to_cpu(csa->channel)); 260 le16_to_cpu(csa->channel));
264 iwl_chswitch_done(priv, false); 261 iwl_chswitch_done(priv, true);
265 } 262 } else {
263 IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
264 le16_to_cpu(csa->channel));
265 iwl_chswitch_done(priv, false);
266 } 266 }
267} 267}
268 268