mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-02-05 02:38:20 +00:00
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 <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
24689c8590
commit
cb71d9bafb
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user