From cb71d9bafb37adab50ddce53bb119a84b4966c06 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 21 Nov 2008 17:41:33 -0800 Subject: ath9k: Handle -ENOMEM on RX gracefully We would get an oops on RX on -ENOMEM by passing NULL to the hardware on ath_rx_buf_link(). The oops would look something like this: ath_rx_tasklet+0x515/0x53b ath9k_tasklet+0x48 tasklet_action __do_softirq irq_exit do_IRQ RIP: ath_rx_buf_link+0x3a We correct this by handling the requeue directly on the ath_rx_tasklet() and trying to allocate an skb *prior* to sending up the last hardware processed skb. If we run out of memory this gauranteees we have skbs to work with while it simply drops new received frames. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/recv.c | 57 ++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 20f8377..6eae254 100644 --- a/drivers/net/wireless/ath9k/recv.c +++ b/drivers/net/wireless/ath9k/recv.c @@ -81,29 +81,6 @@ static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, u32 len) return skb; } -static void ath_rx_requeue(struct ath_softc *sc, struct ath_buf *bf) -{ - struct sk_buff *skb; - - ASSERT(bf != NULL); - - if (bf->bf_mpdu == NULL) { - skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); - if (skb != NULL) { - bf->bf_mpdu = skb; - bf->bf_buf_addr = pci_map_single(sc->pdev, skb->data, - skb_end_pointer(skb) - skb->head, - PCI_DMA_FROMDEVICE); - bf->bf_dmacontext = bf->bf_buf_addr; - - } - } - - list_move_tail(&bf->list, &sc->sc_rxbuf); - ath_rx_buf_link(sc, bf); -} - - static int ath_rate2idx(struct ath_softc *sc, int rate) { int i = 0, cur_band, n_rates; @@ -445,7 +422,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) struct ath_buf *bf; struct ath_desc *ds; - struct sk_buff *skb = NULL; + struct sk_buff *skb = NULL, *requeue_skb; struct ieee80211_rx_status rx_status; struct ath_hal *ah = sc->sc_ah; struct ieee80211_hdr *hdr; @@ -522,17 +499,28 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) * chain it back at the queue without processing it. */ if (flush) - goto rx_next; + goto requeue; if (!ds->ds_rxstat.rs_datalen) - goto rx_next; + goto requeue; /* The status portion of the descriptor could get corrupted. */ if (sc->sc_rxbufsize < ds->ds_rxstat.rs_datalen) - goto rx_next; + goto requeue; if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc)) - goto rx_next; + goto requeue; + + /* Ensure we always have an skb to requeue once we are done + * processing the current buffer's skb */ + requeue_skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); + + /* If there is no memory we ignore the current RX'd frame, + * tell hardware it can give us a new frame using the old + * skb and put it at the tail of the sc->sc_rxbuf list for + * processing. */ + if (!requeue_skb) + goto requeue; /* Sync and unmap the frame */ pci_dma_sync_single_for_cpu(sc->pdev, bf->bf_buf_addr, @@ -569,7 +557,13 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) /* Send the frame to mac80211 */ __ieee80211_rx(sc->hw, skb, &rx_status); - bf->bf_mpdu = NULL; + + /* We will now give hardware our shiny new allocated skb */ + bf->bf_mpdu = requeue_skb; + bf->bf_buf_addr = pci_map_single(sc->pdev, requeue_skb->data, + sc->sc_rxbufsize, + PCI_DMA_FROMDEVICE); + bf->bf_dmacontext = bf->bf_buf_addr; /* * change the default rx antenna if rx diversity chooses the @@ -581,8 +575,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) } else { sc->sc_rxotherant = 0; } -rx_next: - ath_rx_requeue(sc, bf); +requeue: + list_move_tail(&bf->list, &sc->sc_rxbuf); + ath_rx_buf_link(sc, bf); } while (1); spin_unlock_bh(&sc->sc_rxbuflock); -- cgit v1.1