Skip to content

Commit

Permalink
b43: Rewrite DMA Tx status handling sanity checks
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Buesch authored and John W. Linville committed Nov 23, 2009
1 parent 8c35024 commit 07681e2
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 14 deletions.
67 changes: 54 additions & 13 deletions drivers/net/wireless/b43/dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@ static void free_all_descbuffers(struct b43_dmaring *ring)
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;
}
Expand Down Expand Up @@ -926,7 +926,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
enum b43_dmatype type)
{
struct b43_dmaring *ring;
int err;
int i, err;
dma_addr_t dma_test;

ring = kzalloc(sizeof(*ring), GFP_KERNEL);
Expand All @@ -941,6 +941,8 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
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;
Expand Down Expand Up @@ -1251,11 +1253,13 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
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;
}
Expand Down Expand Up @@ -1494,19 +1498,40 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
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) {
struct b43_private_tx_info *priv_info =
b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb));
Expand All @@ -1522,7 +1547,14 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
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);

Expand All @@ -1540,20 +1572,29 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
#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) {
Expand Down
6 changes: 5 additions & 1 deletion drivers/net/wireless/b43/dma.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef B43_DMA_H_
#define B43_DMA_H_

#include <linux/ieee80211.h>
#include <linux/err.h>

#include "b43.h"

Expand Down Expand Up @@ -164,6 +164,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;
Expand Down

0 comments on commit 07681e2

Please sign in to comment.