diff options
author | Luis R. Rodriguez <lrodriguez@atheros.com> | 2008-11-21 20:41:33 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-11-26 09:47:44 -0500 |
commit | cb71d9bafb37adab50ddce53bb119a84b4966c06 (patch) | |
tree | a6fb54b339748309de9e20455910c402de942b5c /drivers/net/wireless/ath9k/recv.c | |
parent | 24689c8590be6bc0486af665c5ceda6aeff4d53a (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/ath9k/recv.c')
-rw-r--r-- | drivers/net/wireless/ath9k/recv.c | 57 |
1 files changed, 26 insertions, 31 deletions
diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 20f83779278a..6eae2542392a 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) | |||
81 | return skb; | 81 | return skb; |
82 | } | 82 | } |
83 | 83 | ||
84 | static void ath_rx_requeue(struct ath_softc *sc, struct ath_buf *bf) | ||
85 | { | ||
86 | struct sk_buff *skb; | ||
87 | |||
88 | ASSERT(bf != NULL); | ||
89 | |||
90 | if (bf->bf_mpdu == NULL) { | ||
91 | skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); | ||
92 | if (skb != NULL) { | ||
93 | bf->bf_mpdu = skb; | ||
94 | bf->bf_buf_addr = pci_map_single(sc->pdev, skb->data, | ||
95 | skb_end_pointer(skb) - skb->head, | ||
96 | PCI_DMA_FROMDEVICE); | ||
97 | bf->bf_dmacontext = bf->bf_buf_addr; | ||
98 | |||
99 | } | ||
100 | } | ||
101 | |||
102 | list_move_tail(&bf->list, &sc->sc_rxbuf); | ||
103 | ath_rx_buf_link(sc, bf); | ||
104 | } | ||
105 | |||
106 | |||
107 | static int ath_rate2idx(struct ath_softc *sc, int rate) | 84 | static int ath_rate2idx(struct ath_softc *sc, int rate) |
108 | { | 85 | { |
109 | int i = 0, cur_band, n_rates; | 86 | int i = 0, cur_band, n_rates; |
@@ -445,7 +422,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) | |||
445 | 422 | ||
446 | struct ath_buf *bf; | 423 | struct ath_buf *bf; |
447 | struct ath_desc *ds; | 424 | struct ath_desc *ds; |
448 | struct sk_buff *skb = NULL; | 425 | struct sk_buff *skb = NULL, *requeue_skb; |
449 | struct ieee80211_rx_status rx_status; | 426 | struct ieee80211_rx_status rx_status; |
450 | struct ath_hal *ah = sc->sc_ah; | 427 | struct ath_hal *ah = sc->sc_ah; |
451 | struct ieee80211_hdr *hdr; | 428 | struct ieee80211_hdr *hdr; |
@@ -522,17 +499,28 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) | |||
522 | * chain it back at the queue without processing it. | 499 | * chain it back at the queue without processing it. |
523 | */ | 500 | */ |
524 | if (flush) | 501 | if (flush) |
525 | goto rx_next; | 502 | goto requeue; |
526 | 503 | ||
527 | if (!ds->ds_rxstat.rs_datalen) | 504 | if (!ds->ds_rxstat.rs_datalen) |
528 | goto rx_next; | 505 | goto requeue; |
529 | 506 | ||
530 | /* The status portion of the descriptor could get corrupted. */ | 507 | /* The status portion of the descriptor could get corrupted. */ |
531 | if (sc->sc_rxbufsize < ds->ds_rxstat.rs_datalen) | 508 | if (sc->sc_rxbufsize < ds->ds_rxstat.rs_datalen) |
532 | goto rx_next; | 509 | goto requeue; |
533 | 510 | ||
534 | if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc)) | 511 | if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc)) |
535 | goto rx_next; | 512 | goto requeue; |
513 | |||
514 | /* Ensure we always have an skb to requeue once we are done | ||
515 | * processing the current buffer's skb */ | ||
516 | requeue_skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); | ||
517 | |||
518 | /* If there is no memory we ignore the current RX'd frame, | ||
519 | * tell hardware it can give us a new frame using the old | ||
520 | * skb and put it at the tail of the sc->sc_rxbuf list for | ||
521 | * processing. */ | ||
522 | if (!requeue_skb) | ||
523 | goto requeue; | ||
536 | 524 | ||
537 | /* Sync and unmap the frame */ | 525 | /* Sync and unmap the frame */ |
538 | pci_dma_sync_single_for_cpu(sc->pdev, bf->bf_buf_addr, | 526 | 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) | |||
569 | 557 | ||
570 | /* Send the frame to mac80211 */ | 558 | /* Send the frame to mac80211 */ |
571 | __ieee80211_rx(sc->hw, skb, &rx_status); | 559 | __ieee80211_rx(sc->hw, skb, &rx_status); |
572 | bf->bf_mpdu = NULL; | 560 | |
561 | /* We will now give hardware our shiny new allocated skb */ | ||
562 | bf->bf_mpdu = requeue_skb; | ||
563 | bf->bf_buf_addr = pci_map_single(sc->pdev, requeue_skb->data, | ||
564 | sc->sc_rxbufsize, | ||
565 | PCI_DMA_FROMDEVICE); | ||
566 | bf->bf_dmacontext = bf->bf_buf_addr; | ||
573 | 567 | ||
574 | /* | 568 | /* |
575 | * change the default rx antenna if rx diversity chooses the | 569 | * change the default rx antenna if rx diversity chooses the |
@@ -581,8 +575,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) | |||
581 | } else { | 575 | } else { |
582 | sc->sc_rxotherant = 0; | 576 | sc->sc_rxotherant = 0; |
583 | } | 577 | } |
584 | rx_next: | 578 | requeue: |
585 | ath_rx_requeue(sc, bf); | 579 | list_move_tail(&bf->list, &sc->sc_rxbuf); |
580 | ath_rx_buf_link(sc, bf); | ||
586 | } while (1); | 581 | } while (1); |
587 | 582 | ||
588 | spin_unlock_bh(&sc->sc_rxbuflock); | 583 | spin_unlock_bh(&sc->sc_rxbuflock); |