ath5k: fix reset race
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com> git-svn-id: svn://svn.openwrt.org/openwrt/trunk@44603 3c298f89-4303-0410-b956-a3cf2f4a3e73master
parent
61aa9cc4fb
commit
7b6c0cca40
|
@ -0,0 +1,104 @@
|
|||
From d8d4050dff457b79ad7e9356103cad557c338532 Mon Sep 17 00:00:00 2001
|
||||
From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
|
||||
Date: Wed, 4 Mar 2015 03:16:34 +0300
|
||||
Subject: [PATCH] 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: Christophe Prevotaux <cprevotaux@nltinc.com>
|
||||
Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com>
|
||||
Tested-by: Eric Bree <ebree@nltinc.com>
|
||||
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath5k/ath5k.h | 1 +
|
||||
drivers/net/wireless/ath/ath5k/base.c | 7 +++++++
|
||||
2 files changed, 8 insertions(+)
|
||||
|
||||
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
|
||||
index 1ed7a88..7ca0d6f 100644
|
||||
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
|
||||
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
|
||||
@@ -1283,6 +1283,7 @@ struct ath5k_hw {
|
||||
#define ATH_STAT_PROMISC 1
|
||||
#define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */
|
||||
#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 fif_filter_flags; /* Current FIF_* filter flags */
|
||||
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
|
||||
index 34b2f15..41848e1 100644
|
||||
--- a/drivers/net/wireless/ath/ath5k/base.c
|
||||
+++ b/drivers/net/wireless/ath/ath5k/base.c
|
||||
@@ -1523,6 +1523,9 @@ ath5k_set_current_imask(struct ath5k_hw *ah)
|
||||
enum ath5k_int imask;
|
||||
unsigned long flags;
|
||||
|
||||
+ if (test_bit(ATH_STAT_RESET, ah->status))
|
||||
+ return;
|
||||
+
|
||||
spin_lock_irqsave(&ah->irqlock, flags);
|
||||
imask = ah->imask;
|
||||
if (ah->rx_pending)
|
||||
@@ -2862,6 +2865,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
|
||||
|
||||
ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
|
||||
|
||||
+ __set_bit(ATH_STAT_RESET, ah->status);
|
||||
+
|
||||
ath5k_hw_set_imr(ah, 0);
|
||||
synchronize_irq(ah->irq);
|
||||
ath5k_stop_tasklets(ah);
|
||||
@@ -2952,6 +2957,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
|
||||
*/
|
||||
/* ath5k_chan_change(ah, c); */
|
||||
|
||||
+ __clear_bit(ATH_STAT_RESET, ah->status);
|
||||
+
|
||||
ath5k_beacon_config(ah);
|
||||
/* intrs are enabled by ath5k_beacon_config */
|
||||
|
Loading…
Reference in New Issue