ath5k: update channel in sw state after stopping RX and TX

This fixes a non-theoretical race condition when transmitting and
receiving frames during a scan. If the channel or operating band
changes while processing status descriptors in the tasklets, ath5k
will incorrectly use the new channel and band when reporting the
rates, even if the frame was actually sent on a previous channel.

Typically this will manifest as a beacon found on an incorrect
frequency and/or a warning in the driver while scanning:

[ 4773.891944] cfg80211: Found new beacon on frequency: 5805 MHz (Ch 161) on phy0
[ 4785.461125] ------------[ cut here ]------------
[ 4785.461135] WARNING: at drivers/net/wireless/ath/ath5k/base.c:1141 ath5k_tasklet_rx+0x2ff/0x577 [ath5k]()
[ 4785.461143] Hardware name: MacBook1,1
[ 4785.461148] invalid hw_rix: 1b
[ 4785.461152] Modules linked in: fuse i915 drm af_packet acpi_cpufreq binfmt_misc dm_mirror dm_region_hash dm_log dm_multipath dm_mod arc4 ecb snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_seq_dummy snd_seq_oss snd_seq_midi_event ath5k snd_seq hid_apple usbhid snd_seq_device mac80211 appletouch snd_pcm_oss sky2 ohci1394 snd_mixer_oss ath ieee1394 snd_pcm bitrev snd_timer cfg80211 crc32 snd snd_page_alloc button processor ac ehci_hcd joydev uhci_hcd sg battery thermal sr_mod cdrom applesmc evdev input_polldev unix [last unloaded: microcode]
[ 4785.461296] Pid: 0, comm: swapper Tainted: G W 2.6.30-rc3-wl #112
[ 4785.461302] Call Trace:
[ 4785.461316] [<c012590f>] warn_slowpath+0x76/0xa5
[ 4785.461331] [<c0219839>] ? debug_dma_unmap_page+0x5a/0x62
[ 4785.461357] [<f9982f88>] ath5k_tasklet_rx+0x2ff/0x577 [ath5k]
[ 4785.461371] [<c01446f7>] ? trace_hardirqs_off+0xb/0xd
[ 4785.461381] [<c0129928>] ? __tasklet_schedule+0x6e/0x7c
[ 4785.461392] [<c0129b02>] tasklet_action+0x92/0xe5
[ 4785.461402] [<c0129f91>] __do_softirq+0xb1/0x182
[ 4785.461411] [<c012a092>] do_softirq+0x30/0x48
[ 4785.461428] [<c012a20a>] irq_exit+0x3d/0x74
[ 4785.461435] [<c035a0de>] do_IRQ+0x76/0x8c
[ 4785.461440] [<c010312e>] common_interrupt+0x2e/0x34
[ 4785.461445] [<c014007b>] ? timer_list_show+0x1ab/0x939
[ 4785.461457] [<f85fd25c>] ? acpi_idle_enter_bm+0x27c/0x2b9 [processor]
[ 4785.461463] [<c02d1ed6>] cpuidle_idle_call+0x6a/0x9c
[ 4785.461468] [<c0101cc8>] cpu_idle+0x53/0x87
[ 4785.461473] [<c0346584>] rest_init+0x6c/0x6e
[ 4785.461479] [<c04df74d>] start_kernel+0x286/0x28b
[ 4785.461484] [<c04df037>] __init_begin+0x37/0x3c
[ 4785.461487] ---[ end trace aaf8496ba3679dfb ]---

Signed-off-by: Bob Copeland <me@bobcopeland.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by Bob Copeland and committed by John W. Linville 209d889b 9be6f0d4

+13 -9
+13 -9
drivers/net/wireless/ath5k/base.c
··· 214 * Prototypes - MAC 802.11 stack related functions 215 */ 216 static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb); 217 - static int ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel); 218 static int ath5k_reset_wake(struct ath5k_softc *sc); 219 static int ath5k_start(struct ieee80211_hw *hw); 220 static void ath5k_stop(struct ieee80211_hw *hw); ··· 1038 if (chan->center_freq != sc->curchan->center_freq || 1039 chan->hw_value != sc->curchan->hw_value) { 1040 1041 - sc->curchan = chan; 1042 - sc->curband = &sc->sbands[chan->band]; 1043 - 1044 /* 1045 * To switch channels clear any pending DMA operations; 1046 * wait long enough for the RX fifo to drain, reset the 1047 * hardware at the new frequency, and then re-enable 1048 * the relevant bits of the h/w. 1049 */ 1050 - return ath5k_reset(sc, true, true); 1051 } 1052 1053 return 0; ··· 2311 sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | 2312 AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | 2313 AR5K_INT_FATAL | AR5K_INT_GLOBAL; 2314 - ret = ath5k_reset(sc, false, false); 2315 if (ret) 2316 goto done; 2317 ··· 2596 return NETDEV_TX_OK; 2597 } 2598 2599 static int 2600 - ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel) 2601 { 2602 struct ath5k_hw *ah = sc->ah; 2603 int ret; 2604 2605 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n"); 2606 2607 - if (stop) { 2608 ath5k_hw_set_imr(ah, 0); 2609 ath5k_txq_cleanup(sc); 2610 ath5k_rx_stop(sc); 2611 } 2612 ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); 2613 if (ret) { ··· 2652 { 2653 int ret; 2654 2655 - ret = ath5k_reset(sc, true, true); 2656 if (!ret) 2657 ieee80211_wake_queues(sc->hw); 2658
··· 214 * Prototypes - MAC 802.11 stack related functions 215 */ 216 static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb); 217 + static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan); 218 static int ath5k_reset_wake(struct ath5k_softc *sc); 219 static int ath5k_start(struct ieee80211_hw *hw); 220 static void ath5k_stop(struct ieee80211_hw *hw); ··· 1038 if (chan->center_freq != sc->curchan->center_freq || 1039 chan->hw_value != sc->curchan->hw_value) { 1040 1041 /* 1042 * To switch channels clear any pending DMA operations; 1043 * wait long enough for the RX fifo to drain, reset the 1044 * hardware at the new frequency, and then re-enable 1045 * the relevant bits of the h/w. 1046 */ 1047 + return ath5k_reset(sc, chan); 1048 } 1049 1050 return 0; ··· 2314 sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | 2315 AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | 2316 AR5K_INT_FATAL | AR5K_INT_GLOBAL; 2317 + ret = ath5k_reset(sc, NULL); 2318 if (ret) 2319 goto done; 2320 ··· 2599 return NETDEV_TX_OK; 2600 } 2601 2602 + /* 2603 + * Reset the hardware. If chan is not NULL, then also pause rx/tx 2604 + * and change to the given channel. 2605 + */ 2606 static int 2607 + ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan) 2608 { 2609 struct ath5k_hw *ah = sc->ah; 2610 int ret; 2611 2612 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n"); 2613 2614 + if (chan) { 2615 ath5k_hw_set_imr(ah, 0); 2616 ath5k_txq_cleanup(sc); 2617 ath5k_rx_stop(sc); 2618 + 2619 + sc->curchan = chan; 2620 + sc->curband = &sc->sbands[chan->band]; 2621 } 2622 ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); 2623 if (ret) { ··· 2648 { 2649 int ret; 2650 2651 + ret = ath5k_reset(sc, sc->curchan); 2652 if (!ret) 2653 ieee80211_wake_queues(sc->hw); 2654