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