aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h5
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c17
2 files changed, 14 insertions, 8 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 42b03dc39d14..4ebd9fd8a0a4 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -79,10 +79,6 @@ struct ath_config {
79 sizeof(struct ath_buf_state)); \ 79 sizeof(struct ath_buf_state)); \
80 } while (0) 80 } while (0)
81 81
82#define ATH_RXBUF_RESET(_bf) do { \
83 (_bf)->bf_stale = false; \
84 } while (0)
85
86/** 82/**
87 * enum buffer_type - Buffer type flags 83 * enum buffer_type - Buffer type flags
88 * 84 *
@@ -316,6 +312,7 @@ struct ath_rx {
316 struct ath_descdma rxdma; 312 struct ath_descdma rxdma;
317 struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; 313 struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
318 314
315 struct ath_buf *buf_hold;
319 struct sk_buff *frag; 316 struct sk_buff *frag;
320 317
321 u32 ampdu_ref; 318 u32 ampdu_ref;
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);