From c39132e506e1b044fe9f222392b09b3c6257c45f Mon Sep 17 00:00:00 2001 From: blogic Date: Thu, 5 Mar 2015 20:24:50 +0000 Subject: [PATCH] ath5k: fix reset race Signed-off-by: Sergey Ryazanov git-svn-id: svn://svn.openwrt.org/openwrt/trunk@44603 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../patches/332-ath5k-fix-reset-race.patch | 104 +++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 package/kernel/mac80211/patches/332-ath5k-fix-reset-race.patch diff --git a/package/kernel/mac80211/patches/332-ath5k-fix-reset-race.patch b/package/kernel/mac80211/patches/332-ath5k-fix-reset-race.patch new file mode 100644 index 0000000000..4a3abaaa56 --- /dev/null +++ b/package/kernel/mac80211/patches/332-ath5k-fix-reset-race.patch @@ -0,0 +1,104 @@ +From d8d4050dff457b79ad7e9356103cad557c338532 Mon Sep 17 00:00:00 2001 +From: Sergey Ryazanov +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 +CC: Nick Kossifidis +CC: Luis R. Rodriguez +Reported-by: Christophe Prevotaux +Tested-by: Christophe Prevotaux +Tested-by: Eric Bree +Signed-off-by: Sergey Ryazanov +--- + 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 */ + -- 2.11.0