aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/recv.c
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2013-08-10 09:59:15 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-09-26 20:18:15 -0400
commit8e4d4c932d23091953185297d40315e8ba76837f (patch)
tree8a121c10864623642dfd206b786ff7bc893f6a49 /drivers/net/wireless/ath/ath9k/recv.c
parent31f34c79a7e8fb75c7bf7d61d56fc8eeff2a7886 (diff)
ath9k: fix rx descriptor related race condition
commit e96542e55a2aacf4bdeccfe2f17b77c4895b4df2 upstream. Similar to a race condition that exists in the tx path, the hardware might re-read the 'next' pointer of a descriptor of the last completed frame. This only affects non-EDMA (pre-AR93xx) devices. To deal with this race, defer clearing and re-linking a completed rx descriptor until the next one has been processed. Signed-off-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/recv.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 8be2b5d8c155..f53dbd1133ce 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -42,8 +42,6 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
42 struct ath_desc *ds; 42 struct ath_desc *ds;
43 struct sk_buff *skb; 43 struct sk_buff *skb;
44 44
45 ATH_RXBUF_RESET(bf);
46
47 ds = bf->bf_desc; 45 ds = bf->bf_desc;
48 ds->ds_link = 0; /* link to null */ 46 ds->ds_link = 0; /* link to null */
49 ds->ds_data = bf->bf_buf_addr; 47 ds->ds_data = bf->bf_buf_addr;
@@ -70,6 +68,14 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
70 sc->rx.rxlink = &ds->ds_link; 68 sc->rx.rxlink = &ds->ds_link;
71} 69}
72 70
71static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
72{
73 if (sc->rx.buf_hold)
74 ath_rx_buf_link(sc, sc->rx.buf_hold);
75
76 sc->rx.buf_hold = bf;
77}
78
73static void ath_setdefantenna(struct ath_softc *sc, u32 antenna) 79static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
74{ 80{
75 /* XXX block beacon interrupts */ 81 /* XXX block beacon interrupts */
@@ -117,7 +123,6 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
117 123
118 skb = bf->bf_mpdu; 124 skb = bf->bf_mpdu;
119 125
120 ATH_RXBUF_RESET(bf);
121 memset(skb->data, 0, ah->caps.rx_status_len); 126 memset(skb->data, 0, ah->caps.rx_status_len);
122 dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, 127 dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
123 ah->caps.rx_status_len, DMA_TO_DEVICE); 128 ah->caps.rx_status_len, DMA_TO_DEVICE);
@@ -432,6 +437,7 @@ int ath_startrecv(struct ath_softc *sc)
432 if (list_empty(&sc->rx.rxbuf)) 437 if (list_empty(&sc->rx.rxbuf))
433 goto start_recv; 438 goto start_recv;
434 439
440 sc->rx.buf_hold = NULL;
435 sc->rx.rxlink = NULL; 441 sc->rx.rxlink = NULL;
436 list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) { 442 list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
437 ath_rx_buf_link(sc, bf); 443 ath_rx_buf_link(sc, bf);
@@ -677,6 +683,9 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
677 } 683 }
678 684
679 bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); 685 bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
686 if (bf == sc->rx.buf_hold)
687 return NULL;
688
680 ds = bf->bf_desc; 689 ds = bf->bf_desc;
681 690
682 /* 691 /*
@@ -1378,7 +1387,7 @@ requeue:
1378 if (edma) { 1387 if (edma) {
1379 ath_rx_edma_buf_link(sc, qtype); 1388 ath_rx_edma_buf_link(sc, qtype);
1380 } else { 1389 } else {
1381 ath_rx_buf_link(sc, bf); 1390 ath_rx_buf_relink(sc, bf);
1382 ath9k_hw_rxena(ah); 1391 ath9k_hw_rxena(ah);
1383 } 1392 }
1384 } while (1); 1393 } while (1);