diff options
author | Bob Copeland <me@bobcopeland.com> | 2010-04-07 23:55:58 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-04-08 15:24:16 -0400 |
commit | a05988bbbef5ac2391fe696646f0b80708f33f2e (patch) | |
tree | 6c3bce02d933f0de99776104450bd9d442befcf9 | |
parent | 6b5d117eddc09cd976ad8030d715f4350f598a22 (diff) | |
download | kernel_samsung_tuna-a05988bbbef5ac2391fe696646f0b80708f33f2e.zip kernel_samsung_tuna-a05988bbbef5ac2391fe696646f0b80708f33f2e.tar.gz kernel_samsung_tuna-a05988bbbef5ac2391fe696646f0b80708f33f2e.tar.bz2 |
ath5k: fix race condition in tx desc processing
As pointed out by Benoit Papillault, there is a potential
race condition between the host and the hardware in reading
the next link in the transmit descriptor list:
cpu0 hw
tx for buf completed
raise tx_ok interrupt
process buf
buf->ds_link = 0
read buf->ds_link
This change checks txdp before processing a descriptor
(if there are any subsequent descriptors) to see if
hardware moved on. We'll then process this descriptor on
the next tasklet.
Signed-off-by: Bob Copeland <me@bobcopeland.com>
Acked-by: Bruno Randolf <br1@einfach.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 38c41e3..f7f57c1 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2083,6 +2083,17 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) list_for_each_entry_safe(bf, bf0, &txq->q, list) { ds = bf->desc; + /* + * It's possible that the hardware can say the buffer is + * completed when it hasn't yet loaded the ds_link from + * host memory and moved on. If there are more TX + * descriptors in the queue, wait for TXDP to change + * before processing this one. + */ + if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr && + !list_is_last(&bf->list, &txq->q)) + break; + ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); if (unlikely(ret == -EINPROGRESS)) break; |