| From b251412db99ccd4495ce372fec7daee27bf06923 Mon Sep 17 00:00:00 2001 |
| From: "Iestyn C. Elfick" <isedev@gmail.com> |
| Date: Wed, 20 Mar 2013 14:02:31 -0500 |
| Subject: b43: A fix for DMA transmission sequence errors |
| |
| From: "Iestyn C. Elfick" <isedev@gmail.com> |
| |
| commit b251412db99ccd4495ce372fec7daee27bf06923 upstream. |
| |
| Intermittently, b43 will report "Out of order TX status report on DMA ring". |
| When this happens, the driver must be reset before communication can resume. |
| The cause of the problem is believed to be an error in the closed-source |
| firmware; however, all versions of the firmware are affected. |
| |
| This change uses the observation that the expected status is always 2 less |
| than the observed value, and supplies a fake status report to skip one |
| header/data pair. |
| |
| Not all devices suffer from this problem, but it can occur several times |
| per second under heavy load. As each occurence kills the unmodified driver, |
| this patch makes if possible for the affected devices to function. The patch |
| logs only the first instance of the reset operation to prevent spamming |
| the logs. |
| |
| Tested-by: Chris Vine <chris@cvine.freeserve.co.uk> |
| Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> |
| Signed-off-by: John W. Linville <linville@tuxdriver.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/wireless/b43/dma.c | 65 +++++++++++++++++++++++++++++++++-------- |
| 1 file changed, 53 insertions(+), 12 deletions(-) |
| |
| --- a/drivers/net/wireless/b43/dma.c |
| +++ b/drivers/net/wireless/b43/dma.c |
| @@ -1390,8 +1390,12 @@ void b43_dma_handle_txstatus(struct b43_ |
| struct b43_dmaring *ring; |
| struct b43_dmadesc_generic *desc; |
| struct b43_dmadesc_meta *meta; |
| + static const struct b43_txstatus fake; /* filled with 0 */ |
| + const struct b43_txstatus *txstat; |
| int slot, firstused; |
| bool frame_succeed; |
| + int skip; |
| + static u8 err_out1, err_out2; |
| |
| ring = parse_cookie(dev, status->cookie, &slot); |
| if (unlikely(!ring)) |
| @@ -1404,13 +1408,36 @@ void b43_dma_handle_txstatus(struct b43_ |
| firstused = ring->current_slot - ring->used_slots + 1; |
| if (firstused < 0) |
| firstused = ring->nr_slots + firstused; |
| + |
| + skip = 0; |
| if (unlikely(slot != firstused)) { |
| /* This possibly is a firmware bug and will result in |
| - * malfunction, memory leaks and/or stall of DMA functionality. */ |
| - b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. " |
| - "Expected %d, but got %d\n", |
| - ring->index, firstused, slot); |
| - return; |
| + * malfunction, memory leaks and/or stall of DMA functionality. |
| + */ |
| + if (slot == next_slot(ring, next_slot(ring, firstused))) { |
| + /* If a single header/data pair was missed, skip over |
| + * the first two slots in an attempt to recover. |
| + */ |
| + slot = firstused; |
| + skip = 2; |
| + if (!err_out1) { |
| + /* Report the error once. */ |
| + b43dbg(dev->wl, |
| + "Skip on DMA ring %d slot %d.\n", |
| + ring->index, slot); |
| + err_out1 = 1; |
| + } |
| + } else { |
| + /* More than a single header/data pair were missed. |
| + * Report this error once. |
| + */ |
| + if (!err_out2) |
| + b43dbg(dev->wl, |
| + "Out of order TX status report on DMA ring %d. Expected %d, but got %d\n", |
| + ring->index, firstused, slot); |
| + err_out2 = 1; |
| + return; |
| + } |
| } |
| |
| ops = ring->ops; |
| @@ -1424,11 +1451,13 @@ void b43_dma_handle_txstatus(struct b43_ |
| slot, firstused, ring->index); |
| break; |
| } |
| + |
| if (meta->skb) { |
| struct b43_private_tx_info *priv_info = |
| - b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); |
| + b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); |
| |
| - unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); |
| + unmap_descbuffer(ring, meta->dmaaddr, |
| + meta->skb->len, 1); |
| kfree(priv_info->bouncebuffer); |
| priv_info->bouncebuffer = NULL; |
| } else { |
| @@ -1440,8 +1469,9 @@ void b43_dma_handle_txstatus(struct b43_ |
| struct ieee80211_tx_info *info; |
| |
| if (unlikely(!meta->skb)) { |
| - /* This is a scatter-gather fragment of a frame, so |
| - * the skb pointer must not be NULL. */ |
| + /* This is a scatter-gather fragment of a frame, |
| + * so the skb pointer must not be NULL. |
| + */ |
| b43dbg(dev->wl, "TX status unexpected NULL skb " |
| "at slot %d (first=%d) on ring %d\n", |
| slot, firstused, ring->index); |
| @@ -1452,9 +1482,18 @@ void b43_dma_handle_txstatus(struct b43_ |
| |
| /* |
| * Call back to inform the ieee80211 subsystem about |
| - * the status of the transmission. |
| + * the status of the transmission. When skipping over |
| + * a missed TX status report, use a status structure |
| + * filled with zeros to indicate that the frame was not |
| + * sent (frame_count 0) and not acknowledged |
| */ |
| - frame_succeed = b43_fill_txstatus_report(dev, info, status); |
| + if (unlikely(skip)) |
| + txstat = &fake; |
| + else |
| + txstat = status; |
| + |
| + frame_succeed = b43_fill_txstatus_report(dev, info, |
| + txstat); |
| #ifdef CONFIG_B43_DEBUG |
| if (frame_succeed) |
| ring->nr_succeed_tx_packets++; |
| @@ -1482,12 +1521,14 @@ void b43_dma_handle_txstatus(struct b43_ |
| /* Everything unmapped and free'd. So it's not used anymore. */ |
| ring->used_slots--; |
| |
| - if (meta->is_last_fragment) { |
| + if (meta->is_last_fragment && !skip) { |
| /* This is the last scatter-gather |
| * fragment of the frame. We are done. */ |
| break; |
| } |
| slot = next_slot(ring, slot); |
| + if (skip > 0) |
| + --skip; |
| } |
| if (ring->stopped) { |
| B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME); |