[RFC/RFT] b43: A fix for DMA transmission sequence errors

Larry Finger Larry.Finger at lwfinger.net
Thu Mar 14 14:46:19 EDT 2013


From: Iestyn C. Elfick <isedev at gmail.com>

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.

Signed-off-by: Larry Finger <Larry.Finger at lwfinger.net>
Cc: Stable <stable at vger.kernel.org>
---

Although this is a hack, it seems to work for the OP. It should not cause any
ill effects on any device that does not have the problem.

It does need a lot more testing, particularly on systems prone to the out-of-order
status report problem.

If anyone can think of a cleaner, less intrusive solution, please let us know.

Thanks,

Larry
---

Index: wireless-testing-rebased/drivers/net/wireless/b43/dma.c
===================================================================
--- wireless-testing-rebased.orig/drivers/net/wireless/b43/dma.c
+++ wireless-testing-rebased/drivers/net/wireless/b43/dma.c
@@ -1487,8 +1487,11 @@ void b43_dma_handle_txstatus(struct b43_
 	const struct b43_dma_ops *ops;
 	struct b43_dmaring *ring;
 	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;
 
 	ring = parse_cookie(dev, status->cookie, &slot);
 	if (unlikely(!ring))
@@ -1501,13 +1504,26 @@ 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. */
+		 * 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;
+		if (slot == firstused + 2) {
+			/* If a single header/data pair was missed, skip over
+			 * the first two slots in an attempt to recover.
+			 */
+			slot = firstused;
+			skip = 2;
+			b43dbg(dev->wl, "Skip on DMA ring %d slot %d.\n",
+			       ring->index, slot);
+		} else {
+			return;
+		}
 	}
 
 	ops = ring->ops;
@@ -1522,11 +1538,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 {
@@ -1538,8 +1556,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);
@@ -1550,9 +1569,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 = status;
+			else
+				txstat = &fake;
+
+			frame_succeed = b43_fill_txstatus_report(dev, info,
+								 txstat);
 #ifdef CONFIG_B43_DEBUG
 			if (frame_succeed)
 				ring->nr_succeed_tx_packets++;
@@ -1580,12 +1608,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);



More information about the b43-dev mailing list