Commit 4a2f248f authored by Sergey Ryazanov's avatar Sergey Ryazanov Committed by Kalle Valo

ath5k: channel change fix

ath5k updates the channel pointer and after that it stops the Rx logic
and apply channel to HW. In case of channel switch, such sequence
creates a small window when a frame, which is received on the old
channel is considered as a frame received on the new one.

The most notable consequence of this situation occurs during the switch
from 2 GHz band (CCK+OFDM) to the 5GHz band (OFDM-only). Frame received
with CCK rate, e.g. beacon received at the 1mbps, causes the following
warning:

  WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]()
  invalid hw_rix: 1a
  [..]
  Call Trace:
  [<802656a8>] show_stack+0x48/0x70
  [<802dd92c>] warn_slowpath_common+0x88/0xbc
  [<802dd98c>] warn_slowpath_fmt+0x2c/0x38
  [<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k]
  [<8028ac64>] tasklet_action+0x8c/0xf0
  [<80075804>] __do_softirq+0x180/0x32c
  [<80196ce8>] irq_exit+0x54/0x70
  [<80041848>] ret_from_irq+0x0/0x4
  [<80182fdc>] ioread32+0x4/0xc
  [<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k]
  [<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k]
  [<81b50900>] ath5k_reset+0xd4/0x310 [ath5k]
  [<81b557e8>] ath5k_config+0x4c/0x104 [ath5k]
  [<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211]
  [<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211]
  [<8022c3f4>] process_one_work+0x28c/0x400
  [<802df8f8>] worker_thread+0x258/0x3c0
  [<801b5710>] kthread+0xe0/0xec
  [<800418a8>] ret_from_kernel_thread+0x14/0x1c

The easiest way to reproduce this warning is to run scan with dualband
NIC in noisy environments, when the channel 11 runs multiple APs. In my
tests if the APs num >= 12, the warning appears in the first few
seconds of scanning.

In order to fix this, the Rx disable code moved to a higher level and
placed before the channel pointer update. This is also makes the code a
bit more symmetrical, since we disable and enable the Rx in the same
function.

In fact, at the pointer update time new frames should not appear,
because interrupt generation at this point should already be disabled.
The next patch should address this issue.

CC: Jiri Slaby <jirislaby@gmail.com>
CC: Nick Kossifidis <mickflemm@gmail.com>
CC: Luis R. Rodriguez <mcgrof@do-not-panic.com>
Reported-by: default avatarChristophe Prevotaux <cprevotaux@nltinc.com>
Tested-by: default avatarChristophe Prevotaux <cprevotaux@nltinc.com>
Tested-by: default avatarEric Bree <ebree@nltinc.com>
Signed-off-by: default avatarSergey Ryazanov <ryazanov.s.a@gmail.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 0cf151bd
...@@ -2858,7 +2858,7 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, ...@@ -2858,7 +2858,7 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
{ {
struct ath_common *common = ath5k_hw_common(ah); struct ath_common *common = ath5k_hw_common(ah);
int ret, ani_mode; int ret, ani_mode;
bool fast; bool fast = chan && modparam_fastchanswitch ? 1 : 0;
ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n"); ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
...@@ -2876,11 +2876,29 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, ...@@ -2876,11 +2876,29 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
* so we should also free any remaining * so we should also free any remaining
* tx buffers */ * tx buffers */
ath5k_drain_tx_buffs(ah); ath5k_drain_tx_buffs(ah);
/* Stop PCU */
ath5k_hw_stop_rx_pcu(ah);
/* Stop DMA
*
* Note: If DMA didn't stop continue
* since only a reset will fix it.
*/
ret = ath5k_hw_dma_stop(ah);
/* RF Bus grant won't work if we have pending
* frames
*/
if (ret && fast) {
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"DMA didn't stop, falling back to normal reset\n");
fast = false;
}
if (chan) if (chan)
ah->curchan = chan; ah->curchan = chan;
fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu); ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
if (ret) { if (ret) {
ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret); ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
......
...@@ -1169,30 +1169,6 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, ...@@ -1169,30 +1169,6 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
if (ah->ah_version == AR5K_AR5212) if (ah->ah_version == AR5K_AR5212)
ath5k_hw_set_sleep_clock(ah, false); ath5k_hw_set_sleep_clock(ah, false);
/*
* Stop PCU
*/
ath5k_hw_stop_rx_pcu(ah);
/*
* Stop DMA
*
* Note: If DMA didn't stop continue
* since only a reset will fix it.
*/
ret = ath5k_hw_dma_stop(ah);
/* RF Bus grant won't work if we have pending
* frames */
if (ret && fast) {
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"DMA didn't stop, falling back to normal reset\n");
fast = false;
/* Non fatal, just continue with
* normal reset */
ret = 0;
}
mode = channel->hw_value; mode = channel->hw_value;
switch (mode) { switch (mode) {
case AR5K_MODE_11A: case AR5K_MODE_11A:
......
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