Commit ab5e290a authored by Sergey Ryazanov's avatar Sergey Ryazanov Committed by Kalle Valo

ath5k: fix reset race

To prepare for reset ath5k should finish all asynchronous tasks. At
first, it disables the interrupt generation, then it waits for the
interrupt handler and tasklets completion, and then proceeds to the HW
configuration update. But it does not consider that the interrupt
handler or tasklet re-enables the interrupt generation. And we fall in a
situation when ath5k assumes that interrupts are disabled, but it is
not.

This can lead to different consequences, such as reception of the frame,
when we do not expect it. Under certain circumstances, this can lead to
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

Fix this issue by adding a new status flag, which forbids to re-enable
the interrupt generation until the HW configuration is completed.

Note: previous patch, which reorders the Rx disable code helps to avoid
the above warning, but not fixes the root cause of unexpected frame
receiving.

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 4a2f248f
...@@ -1283,6 +1283,7 @@ struct ath5k_hw { ...@@ -1283,6 +1283,7 @@ struct ath5k_hw {
#define ATH_STAT_PROMISC 1 #define ATH_STAT_PROMISC 1
#define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */ #define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */
#define ATH_STAT_STARTED 3 /* opened & irqs enabled */ #define ATH_STAT_STARTED 3 /* opened & irqs enabled */
#define ATH_STAT_RESET 4 /* hw reset */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int fif_filter_flags; /* Current FIF_* filter flags */ unsigned int fif_filter_flags; /* Current FIF_* filter flags */
......
...@@ -1523,6 +1523,9 @@ ath5k_set_current_imask(struct ath5k_hw *ah) ...@@ -1523,6 +1523,9 @@ ath5k_set_current_imask(struct ath5k_hw *ah)
enum ath5k_int imask; enum ath5k_int imask;
unsigned long flags; unsigned long flags;
if (test_bit(ATH_STAT_RESET, ah->status))
return;
spin_lock_irqsave(&ah->irqlock, flags); spin_lock_irqsave(&ah->irqlock, flags);
imask = ah->imask; imask = ah->imask;
if (ah->rx_pending) if (ah->rx_pending)
...@@ -2862,6 +2865,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, ...@@ -2862,6 +2865,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n"); ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
__set_bit(ATH_STAT_RESET, ah->status);
ath5k_hw_set_imr(ah, 0); ath5k_hw_set_imr(ah, 0);
synchronize_irq(ah->irq); synchronize_irq(ah->irq);
ath5k_stop_tasklets(ah); ath5k_stop_tasklets(ah);
...@@ -2952,6 +2957,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, ...@@ -2952,6 +2957,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
*/ */
/* ath5k_chan_change(ah, c); */ /* ath5k_chan_change(ah, c); */
__clear_bit(ATH_STAT_RESET, ah->status);
ath5k_beacon_config(ah); ath5k_beacon_config(ah);
/* intrs are enabled by ath5k_beacon_config */ /* intrs are enabled by ath5k_beacon_config */
......
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