diff options
author | Felix Fietkau <nbd@openwrt.org> | 2013-08-10 09:59:15 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-09-26 20:18:15 -0400 |
commit | 8e4d4c932d23091953185297d40315e8ba76837f (patch) | |
tree | 8a121c10864623642dfd206b786ff7bc893f6a49 /drivers/net/wireless/ath/ath9k/recv.c | |
parent | 31f34c79a7e8fb75c7bf7d61d56fc8eeff2a7886 (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.c | 17 |
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 | ||
71 | static 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 | |||
73 | static void ath_setdefantenna(struct ath_softc *sc, u32 antenna) | 79 | static 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); |