Commit b251412d authored by Iestyn C. Elfick's avatar Iestyn C. Elfick Committed by John W. Linville

b43: A fix for DMA transmission sequence errors

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: default avatarChris Vine <chris@cvine.freeserve.co.uk>
Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Cc: Stable <stable@vger.kernel.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent e67dd874
...@@ -1487,8 +1487,12 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1487,8 +1487,12 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
const struct b43_dma_ops *ops; const struct b43_dma_ops *ops;
struct b43_dmaring *ring; struct b43_dmaring *ring;
struct b43_dmadesc_meta *meta; struct b43_dmadesc_meta *meta;
static const struct b43_txstatus fake; /* filled with 0 */
const struct b43_txstatus *txstat;
int slot, firstused; int slot, firstused;
bool frame_succeed; bool frame_succeed;
int skip;
static u8 err_out1, err_out2;
ring = parse_cookie(dev, status->cookie, &slot); ring = parse_cookie(dev, status->cookie, &slot);
if (unlikely(!ring)) if (unlikely(!ring))
...@@ -1501,13 +1505,36 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1501,13 +1505,36 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
firstused = ring->current_slot - ring->used_slots + 1; firstused = ring->current_slot - ring->used_slots + 1;
if (firstused < 0) if (firstused < 0)
firstused = ring->nr_slots + firstused; firstused = ring->nr_slots + firstused;
skip = 0;
if (unlikely(slot != firstused)) { if (unlikely(slot != firstused)) {
/* This possibly is a firmware bug and will result in /* This possibly is a firmware bug and will result in
* malfunction, memory leaks and/or stall of DMA functionality. */ * 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", if (slot == next_slot(ring, next_slot(ring, firstused))) {
ring->index, firstused, slot); /* If a single header/data pair was missed, skip over
return; * 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; ops = ring->ops;
...@@ -1522,11 +1549,13 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1522,11 +1549,13 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
slot, firstused, ring->index); slot, firstused, ring->index);
break; break;
} }
if (meta->skb) { if (meta->skb) {
struct b43_private_tx_info *priv_info = 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); kfree(priv_info->bouncebuffer);
priv_info->bouncebuffer = NULL; priv_info->bouncebuffer = NULL;
} else { } else {
...@@ -1538,8 +1567,9 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1538,8 +1567,9 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
if (unlikely(!meta->skb)) { if (unlikely(!meta->skb)) {
/* This is a scatter-gather fragment of a frame, so /* This is a scatter-gather fragment of a frame,
* the skb pointer must not be NULL. */ * so the skb pointer must not be NULL.
*/
b43dbg(dev->wl, "TX status unexpected NULL skb " b43dbg(dev->wl, "TX status unexpected NULL skb "
"at slot %d (first=%d) on ring %d\n", "at slot %d (first=%d) on ring %d\n",
slot, firstused, ring->index); slot, firstused, ring->index);
...@@ -1550,9 +1580,18 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1550,9 +1580,18 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
/* /*
* Call back to inform the ieee80211 subsystem about * 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 #ifdef CONFIG_B43_DEBUG
if (frame_succeed) if (frame_succeed)
ring->nr_succeed_tx_packets++; ring->nr_succeed_tx_packets++;
...@@ -1580,12 +1619,14 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1580,12 +1619,14 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
/* Everything unmapped and free'd. So it's not used anymore. */ /* Everything unmapped and free'd. So it's not used anymore. */
ring->used_slots--; ring->used_slots--;
if (meta->is_last_fragment) { if (meta->is_last_fragment && !skip) {
/* This is the last scatter-gather /* This is the last scatter-gather
* fragment of the frame. We are done. */ * fragment of the frame. We are done. */
break; break;
} }
slot = next_slot(ring, slot); slot = next_slot(ring, slot);
if (skip > 0)
--skip;
} }
if (ring->stopped) { if (ring->stopped) {
B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME); B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment