aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath5k/base.c
diff options
context:
space:
mode:
authorBob Copeland <me@bobcopeland.com>2010-04-07 23:55:58 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-04-08 15:24:16 -0400
commita05988bbbef5ac2391fe696646f0b80708f33f2e (patch)
tree6c3bce02d933f0de99776104450bd9d442befcf9 /drivers/net/wireless/ath/ath5k/base.c
parent6b5d117eddc09cd976ad8030d715f4350f598a22 (diff)
downloadkernel_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>
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/base.c')
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c11
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;