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