diff options
author | Michael Buesch <mb@bu3sch.de> | 2009-11-19 16:24:29 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-11-23 17:05:28 -0500 |
commit | 07681e211d736ba2394ab7f29f77e93adecd22c5 (patch) | |
tree | d97c679446e738275894636f02d50190bf986599 /drivers/net | |
parent | 8c35024aa65c079f800df7778869a8dbda074182 (diff) |
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>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/b43/dma.c | 67 | ||||
-rw-r--r-- | drivers/net/wireless/b43/dma.h | 6 |
2 files changed, 59 insertions, 14 deletions
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 18b97c02b8af..027be275e035 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c | |||
@@ -874,7 +874,7 @@ static void free_all_descbuffers(struct b43_dmaring *ring) | |||
874 | for (i = 0; i < ring->nr_slots; i++) { | 874 | for (i = 0; i < ring->nr_slots; i++) { |
875 | desc = ring->ops->idx2desc(ring, i, &meta); | 875 | desc = ring->ops->idx2desc(ring, i, &meta); |
876 | 876 | ||
877 | if (!meta->skb) { | 877 | if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) { |
878 | B43_WARN_ON(!ring->tx); | 878 | B43_WARN_ON(!ring->tx); |
879 | continue; | 879 | continue; |
880 | } | 880 | } |
@@ -926,7 +926,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, | |||
926 | enum b43_dmatype type) | 926 | enum b43_dmatype type) |
927 | { | 927 | { |
928 | struct b43_dmaring *ring; | 928 | struct b43_dmaring *ring; |
929 | int err; | 929 | int i, err; |
930 | dma_addr_t dma_test; | 930 | dma_addr_t dma_test; |
931 | 931 | ||
932 | ring = kzalloc(sizeof(*ring), GFP_KERNEL); | 932 | ring = kzalloc(sizeof(*ring), GFP_KERNEL); |
@@ -941,6 +941,8 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, | |||
941 | GFP_KERNEL); | 941 | GFP_KERNEL); |
942 | if (!ring->meta) | 942 | if (!ring->meta) |
943 | goto err_kfree_ring; | 943 | goto err_kfree_ring; |
944 | for (i = 0; i < ring->nr_slots; i++) | ||
945 | ring->meta->skb = B43_DMA_PTR_POISON; | ||
944 | 946 | ||
945 | ring->type = type; | 947 | ring->type = type; |
946 | ring->dev = dev; | 948 | ring->dev = dev; |
@@ -1251,11 +1253,13 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) | |||
1251 | case 0x5000: | 1253 | case 0x5000: |
1252 | ring = dma->tx_ring_mcast; | 1254 | ring = dma->tx_ring_mcast; |
1253 | break; | 1255 | break; |
1254 | default: | ||
1255 | B43_WARN_ON(1); | ||
1256 | } | 1256 | } |
1257 | *slot = (cookie & 0x0FFF); | 1257 | *slot = (cookie & 0x0FFF); |
1258 | B43_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); | 1258 | if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) { |
1259 | b43dbg(dev->wl, "TX-status contains " | ||
1260 | "invalid cookie: 0x%04X\n", cookie); | ||
1261 | return NULL; | ||
1262 | } | ||
1259 | 1263 | ||
1260 | return ring; | 1264 | return ring; |
1261 | } | 1265 | } |
@@ -1494,19 +1498,40 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, | |||
1494 | struct b43_dmaring *ring; | 1498 | struct b43_dmaring *ring; |
1495 | struct b43_dmadesc_generic *desc; | 1499 | struct b43_dmadesc_generic *desc; |
1496 | struct b43_dmadesc_meta *meta; | 1500 | struct b43_dmadesc_meta *meta; |
1497 | int slot; | 1501 | int slot, firstused; |
1498 | bool frame_succeed; | 1502 | bool frame_succeed; |
1499 | 1503 | ||
1500 | ring = parse_cookie(dev, status->cookie, &slot); | 1504 | ring = parse_cookie(dev, status->cookie, &slot); |
1501 | if (unlikely(!ring)) | 1505 | if (unlikely(!ring)) |
1502 | return; | 1506 | return; |
1503 | |||
1504 | B43_WARN_ON(!ring->tx); | 1507 | B43_WARN_ON(!ring->tx); |
1508 | |||
1509 | /* Sanity check: TX packets are processed in-order on one ring. | ||
1510 | * Check if the slot deduced from the cookie really is the first | ||
1511 | * used slot. */ | ||
1512 | firstused = ring->current_slot - ring->used_slots + 1; | ||
1513 | if (firstused < 0) | ||
1514 | firstused = ring->nr_slots + firstused; | ||
1515 | if (unlikely(slot != firstused)) { | ||
1516 | /* This possibly is a firmware bug and will result in | ||
1517 | * malfunction, memory leaks and/or stall of DMA functionality. */ | ||
1518 | b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. " | ||
1519 | "Expected %d, but got %d\n", | ||
1520 | ring->index, firstused, slot); | ||
1521 | return; | ||
1522 | } | ||
1523 | |||
1505 | ops = ring->ops; | 1524 | ops = ring->ops; |
1506 | while (1) { | 1525 | while (1) { |
1507 | B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); | 1526 | B43_WARN_ON(slot < 0 || slot >= ring->nr_slots); |
1508 | desc = ops->idx2desc(ring, slot, &meta); | 1527 | desc = ops->idx2desc(ring, slot, &meta); |
1509 | 1528 | ||
1529 | if (b43_dma_ptr_is_poisoned(meta->skb)) { | ||
1530 | b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) " | ||
1531 | "on ring %d\n", | ||
1532 | slot, firstused, ring->index); | ||
1533 | break; | ||
1534 | } | ||
1510 | if (meta->skb) { | 1535 | if (meta->skb) { |
1511 | struct b43_private_tx_info *priv_info = | 1536 | struct b43_private_tx_info *priv_info = |
1512 | b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); | 1537 | b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); |
@@ -1522,7 +1547,14 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, | |||
1522 | if (meta->is_last_fragment) { | 1547 | if (meta->is_last_fragment) { |
1523 | struct ieee80211_tx_info *info; | 1548 | struct ieee80211_tx_info *info; |
1524 | 1549 | ||
1525 | BUG_ON(!meta->skb); | 1550 | if (unlikely(!meta->skb)) { |
1551 | /* This is a scatter-gather fragment of a frame, so | ||
1552 | * the skb pointer must not be NULL. */ | ||
1553 | b43dbg(dev->wl, "TX status unexpected NULL skb " | ||
1554 | "at slot %d (first=%d) on ring %d\n", | ||
1555 | slot, firstused, ring->index); | ||
1556 | break; | ||
1557 | } | ||
1526 | 1558 | ||
1527 | info = IEEE80211_SKB_CB(meta->skb); | 1559 | info = IEEE80211_SKB_CB(meta->skb); |
1528 | 1560 | ||
@@ -1540,20 +1572,29 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, | |||
1540 | #endif /* DEBUG */ | 1572 | #endif /* DEBUG */ |
1541 | ieee80211_tx_status(dev->wl->hw, meta->skb); | 1573 | ieee80211_tx_status(dev->wl->hw, meta->skb); |
1542 | 1574 | ||
1543 | /* skb is freed by ieee80211_tx_status() */ | 1575 | /* skb will be freed by ieee80211_tx_status(). |
1544 | meta->skb = NULL; | 1576 | * Poison our pointer. */ |
1577 | meta->skb = B43_DMA_PTR_POISON; | ||
1545 | } else { | 1578 | } else { |
1546 | /* No need to call free_descriptor_buffer here, as | 1579 | /* No need to call free_descriptor_buffer here, as |
1547 | * this is only the txhdr, which is not allocated. | 1580 | * this is only the txhdr, which is not allocated. |
1548 | */ | 1581 | */ |
1549 | B43_WARN_ON(meta->skb); | 1582 | if (unlikely(meta->skb)) { |
1583 | b43dbg(dev->wl, "TX status unexpected non-NULL skb " | ||
1584 | "at slot %d (first=%d) on ring %d\n", | ||
1585 | slot, firstused, ring->index); | ||
1586 | break; | ||
1587 | } | ||
1550 | } | 1588 | } |
1551 | 1589 | ||
1552 | /* Everything unmapped and free'd. So it's not used anymore. */ | 1590 | /* Everything unmapped and free'd. So it's not used anymore. */ |
1553 | ring->used_slots--; | 1591 | ring->used_slots--; |
1554 | 1592 | ||
1555 | if (meta->is_last_fragment) | 1593 | if (meta->is_last_fragment) { |
1594 | /* This is the last scatter-gather | ||
1595 | * fragment of the frame. We are done. */ | ||
1556 | break; | 1596 | break; |
1597 | } | ||
1557 | slot = next_slot(ring, slot); | 1598 | slot = next_slot(ring, slot); |
1558 | } | 1599 | } |
1559 | if (ring->stopped) { | 1600 | if (ring->stopped) { |
diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index 356a0ff8f044..e607b392314c 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef B43_DMA_H_ | 1 | #ifndef B43_DMA_H_ |
2 | #define B43_DMA_H_ | 2 | #define B43_DMA_H_ |
3 | 3 | ||
4 | #include <linux/ieee80211.h> | 4 | #include <linux/err.h> |
5 | 5 | ||
6 | #include "b43.h" | 6 | #include "b43.h" |
7 | 7 | ||
@@ -164,6 +164,10 @@ struct b43_dmadesc_generic { | |||
164 | #define B43_RXRING_SLOTS 64 | 164 | #define B43_RXRING_SLOTS 64 |
165 | #define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN | 165 | #define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN |
166 | 166 | ||
167 | /* Pointer poison */ | ||
168 | #define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) | ||
169 | #define b43_dma_ptr_is_poisoned(ptr) (unlikely((ptr) == B43_DMA_PTR_POISON)) | ||
170 | |||
167 | 171 | ||
168 | struct sk_buff; | 172 | struct sk_buff; |
169 | struct b43_private; | 173 | struct b43_private; |