diff options
| author | Stanislaw Gruszka <sgruszka@redhat.com> | 2011-06-02 12:17:15 -0400 |
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2011-06-03 14:22:06 -0400 |
| commit | 6f213ff1919fab6f8244ceae55631b5d6ef750a7 (patch) | |
| tree | ed2f6312721b9391c85dfcdc6f44a0a5dd208cfb /drivers | |
| parent | a99168eece601d2a79ecfcb968ce226f2f30cf98 (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')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-2000.c | 74 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-6000.c | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 6 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 19 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 6 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 1 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 13 | ||||
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-rx.c | 24 |
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 | ||
| 180 | static 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 | |||
| 253 | static struct iwl_lib_ops iwl2000_lib = { | 180 | static 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 | } |
| 2909 | out: | 2912 | out: |
| 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 | ||
| 565 | static inline int iwl_is_ready(struct iwl_priv *priv) | 566 | static 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 | */ | ||
| 990 | struct 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 | ||
