Jesse Keating 2f82dda
Back-port of the following upstream commit...
Jesse Keating 2f82dda
Jesse Keating 2f82dda
commit 07681e211d736ba2394ab7f29f77e93adecd22c5
Jesse Keating 2f82dda
Author: Michael Buesch <mb@bu3sch.de>
Jesse Keating 2f82dda
Date:   Thu Nov 19 22:24:29 2009 +0100
Jesse Keating 2f82dda
Jesse Keating 2f82dda
    b43: Rewrite DMA Tx status handling sanity checks
Jesse Keating 2f82dda
    
Jesse Keating 2f82dda
    This rewrites the error handling policies in the TX status handler.
Jesse Keating 2f82dda
    It tries to be error-tolerant as in "try hard to not crash the machine".
Jesse Keating 2f82dda
    It won't recover from errors (that are bugs in the firmware or driver),
Jesse Keating 2f82dda
    because that's impossible. However, it will return a more or less useful
Jesse Keating 2f82dda
    error message and bail out. It also tries hard to use rate-limited messages
Jesse Keating 2f82dda
    to not flood the syslog in case of a failure.
Jesse Keating 2f82dda
    
Jesse Keating 2f82dda
    Signed-off-by: Michael Buesch <mb@bu3sch.de>
Jesse Keating 2f82dda
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
Jesse Keating 2f82dda
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c.orig linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c.orig	2009-12-02 22:51:21.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c	2010-03-17 14:02:28.000000000 -0400
Jesse Keating 2f82dda
@@ -770,7 +770,7 @@ static void free_all_descbuffers(struct 
Jesse Keating 2f82dda
 	for (i = 0; i < ring->nr_slots; i++) {
Jesse Keating 2f82dda
 		desc = ring->ops->idx2desc(ring, i, &meta);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-		if (!meta->skb) {
Jesse Keating 2f82dda
+		if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) {
Jesse Keating 2f82dda
 			B43_WARN_ON(!ring->tx);
Jesse Keating 2f82dda
 			continue;
Jesse Keating 2f82dda
 		}
Jesse Keating 2f82dda
@@ -822,7 +822,7 @@ struct b43_dmaring *b43_setup_dmaring(st
Jesse Keating 2f82dda
 				      enum b43_dmatype type)
Jesse Keating 2f82dda
 {
Jesse Keating 2f82dda
 	struct b43_dmaring *ring;
Jesse Keating 2f82dda
-	int err;
Jesse Keating 2f82dda
+	int i, err;
Jesse Keating 2f82dda
 	dma_addr_t dma_test;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	ring = kzalloc(sizeof(*ring), GFP_KERNEL);
Jesse Keating 2f82dda
@@ -837,6 +837,8 @@ struct b43_dmaring *b43_setup_dmaring(st
Jesse Keating 2f82dda
 			     GFP_KERNEL);
Jesse Keating 2f82dda
 	if (!ring->meta)
Jesse Keating 2f82dda
 		goto err_kfree_ring;
Jesse Keating 2f82dda
+	for (i = 0; i < ring->nr_slots; i++)
Jesse Keating 2f82dda
+		ring->meta->skb = B43_DMA_PTR_POISON;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	ring->type = type;
Jesse Keating 2f82dda
 	ring->dev = dev;
Jesse Keating 2f82dda
@@ -1147,11 +1149,13 @@ struct b43_dmaring *parse_cookie(struct 
Jesse Keating 2f82dda
 	case 0x5000:
Jesse Keating 2f82dda
 		ring = dma->tx_ring_mcast;
Jesse Keating 2f82dda
 		break;
Jesse Keating 2f82dda
-	default:
Jesse Keating 2f82dda
-		B43_WARN_ON(1);
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
 	*slot = (cookie & 0x0FFF);
Jesse Keating 2f82dda
-	B43_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots));
Jesse Keating 2f82dda
+	if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) {
Jesse Keating 2f82dda
+		b43dbg(dev->wl, "TX-status contains "
Jesse Keating 2f82dda
+		       "invalid cookie: 0x%04X\n", cookie);
Jesse Keating 2f82dda
+		return NULL;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	return ring;
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
@@ -1400,19 +1404,40 @@ void b43_dma_handle_txstatus(struct b43_
Jesse Keating 2f82dda
 	struct b43_dmaring *ring;
Jesse Keating 2f82dda
 	struct b43_dmadesc_generic *desc;
Jesse Keating 2f82dda
 	struct b43_dmadesc_meta *meta;
Jesse Keating 2f82dda
-	int slot;
Jesse Keating 2f82dda
+	int slot, firstused;
Jesse Keating 2f82dda
 	bool frame_succeed;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	ring = parse_cookie(dev, status->cookie, &slot);
Jesse Keating 2f82dda
 	if (unlikely(!ring))
Jesse Keating 2f82dda
 		return;
Jesse Keating 2f82dda
-
Jesse Keating 2f82dda
 	B43_WARN_ON(!ring->tx);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	/* Sanity check: TX packets are processed in-order on one ring.
Jesse Keating 2f82dda
+	 * Check if the slot deduced from the cookie really is the first
Jesse Keating 2f82dda
+	 * used slot. */
Jesse Keating 2f82dda
+	firstused = ring->current_slot - ring->used_slots + 1;
Jesse Keating 2f82dda
+	if (firstused < 0)
Jesse Keating 2f82dda
+		firstused = ring->nr_slots + firstused;
Jesse Keating 2f82dda
+	if (unlikely(slot != firstused)) {
Jesse Keating 2f82dda
+		/* This possibly is a firmware bug and will result in
Jesse Keating 2f82dda
+		 * malfunction, memory leaks and/or stall of DMA functionality. */
Jesse Keating 2f82dda
+		b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. "
Jesse Keating 2f82dda
+		       "Expected %d, but got %d\n",
Jesse Keating 2f82dda
+		       ring->index, firstused, slot);
Jesse Keating 2f82dda
+		return;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 	ops = ring->ops;
Jesse Keating 2f82dda
 	while (1) {
Jesse Keating 2f82dda
-		B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
Jesse Keating 2f82dda
+		B43_WARN_ON(slot < 0 || slot >= ring->nr_slots);
Jesse Keating 2f82dda
 		desc = ops->idx2desc(ring, slot, &meta);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+		if (b43_dma_ptr_is_poisoned(meta->skb)) {
Jesse Keating 2f82dda
+			b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) "
Jesse Keating 2f82dda
+			       "on ring %d\n",
Jesse Keating 2f82dda
+			       slot, firstused, ring->index);
Jesse Keating 2f82dda
+			break;
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
 		if (meta->skb)
Jesse Keating 2f82dda
 			unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len,
Jesse Keating 2f82dda
 					 1);
Jesse Keating 2f82dda
@@ -1423,7 +1448,14 @@ void b43_dma_handle_txstatus(struct b43_
Jesse Keating 2f82dda
 		if (meta->is_last_fragment) {
Jesse Keating 2f82dda
 			struct ieee80211_tx_info *info;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-			BUG_ON(!meta->skb);
Jesse Keating 2f82dda
+			if (unlikely(!meta->skb)) {
Jesse Keating 2f82dda
+				/* This is a scatter-gather fragment of a frame, so
Jesse Keating 2f82dda
+				 * the skb pointer must not be NULL. */
Jesse Keating 2f82dda
+				b43dbg(dev->wl, "TX status unexpected NULL skb "
Jesse Keating 2f82dda
+				       "at slot %d (first=%d) on ring %d\n",
Jesse Keating 2f82dda
+				       slot, firstused, ring->index);
Jesse Keating 2f82dda
+				break;
Jesse Keating 2f82dda
+			}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 			info = IEEE80211_SKB_CB(meta->skb);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
@@ -1441,20 +1473,29 @@ void b43_dma_handle_txstatus(struct b43_
Jesse Keating 2f82dda
 #endif /* DEBUG */
Jesse Keating 2f82dda
 			ieee80211_tx_status(dev->wl->hw, meta->skb);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-			/* skb is freed by ieee80211_tx_status() */
Jesse Keating 2f82dda
-			meta->skb = NULL;
Jesse Keating 2f82dda
+			/* skb will be freed by ieee80211_tx_status().
Jesse Keating 2f82dda
+			 * Poison our pointer. */
Jesse Keating 2f82dda
+			meta->skb = B43_DMA_PTR_POISON;
Jesse Keating 2f82dda
 		} else {
Jesse Keating 2f82dda
 			/* No need to call free_descriptor_buffer here, as
Jesse Keating 2f82dda
 			 * this is only the txhdr, which is not allocated.
Jesse Keating 2f82dda
 			 */
Jesse Keating 2f82dda
-			B43_WARN_ON(meta->skb);
Jesse Keating 2f82dda
+			if (unlikely(meta->skb)) {
Jesse Keating 2f82dda
+				b43dbg(dev->wl, "TX status unexpected non-NULL skb "
Jesse Keating 2f82dda
+				       "at slot %d (first=%d) on ring %d\n",
Jesse Keating 2f82dda
+				       slot, firstused, ring->index);
Jesse Keating 2f82dda
+				break;
Jesse Keating 2f82dda
+			}
Jesse Keating 2f82dda
 		}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 		/* Everything unmapped and free'd. So it's not used anymore. */
Jesse Keating 2f82dda
 		ring->used_slots--;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-		if (meta->is_last_fragment)
Jesse Keating 2f82dda
+		if (meta->is_last_fragment) {
Jesse Keating 2f82dda
+			/* This is the last scatter-gather
Jesse Keating 2f82dda
+			 * fragment of the frame. We are done. */
Jesse Keating 2f82dda
 			break;
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
 		slot = next_slot(ring, slot);
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
 	if (ring->stopped) {
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h.orig linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h.orig	2009-12-02 22:51:21.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h	2010-03-17 13:57:57.000000000 -0400
Jesse Keating 2f82dda
@@ -1,7 +1,7 @@
Jesse Keating 2f82dda
 #ifndef B43_DMA_H_
Jesse Keating 2f82dda
 #define B43_DMA_H_
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-#include <linux/ieee80211.h>
Jesse Keating 2f82dda
+#include <linux/err.h>
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 #include "b43.h"
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
@@ -165,6 +165,10 @@ struct b43_dmadesc_generic {
Jesse Keating 2f82dda
 #define B43_RXRING_SLOTS		64
Jesse Keating 2f82dda
 #define B43_DMA0_RX_BUFFERSIZE		IEEE80211_MAX_FRAME_LEN
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+/* Pointer poison */
Jesse Keating 2f82dda
+#define B43_DMA_PTR_POISON		((void *)ERR_PTR(-ENOMEM))
Jesse Keating 2f82dda
+#define b43_dma_ptr_is_poisoned(ptr)	(unlikely((ptr) == B43_DMA_PTR_POISON))
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct sk_buff;
Jesse Keating 2f82dda
 struct b43_private;