Revert "b43: Enforce DMA descriptor memory constraints"
[safe/jmp/linux-2.6] / drivers / net / wireless / b43 / dma.c
index 25ced8b..88d1fd0 100644 (file)
@@ -770,7 +770,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;
                }
@@ -822,7 +822,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);
@@ -837,6 +837,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;
@@ -856,7 +858,6 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
                } else
                        B43_WARN_ON(1);
        }
-       spin_lock_init(&ring->lock);
 #ifdef CONFIG_B43_DEBUG
        ring->last_injected_overflow = jiffies;
 #endif
@@ -1148,11 +1149,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;
 }
@@ -1162,13 +1165,13 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
 {
        const struct b43_dma_ops *ops = ring->ops;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(info);
        u8 *header;
        int slot, old_top_slot, old_used_slots;
        int err;
        struct b43_dmadesc_generic *desc;
        struct b43_dmadesc_meta *meta;
        struct b43_dmadesc_meta *meta_hdr;
-       struct sk_buff *bounce_skb;
        u16 cookie;
        size_t hdrsize = b43_txhdr_size(ring->dev);
 
@@ -1212,28 +1215,28 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
 
        meta->skb = skb;
        meta->is_last_fragment = 1;
+       priv_info->bouncebuffer = NULL;
 
        meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
        /* create a bounce buffer in zone_dma on mapping failure. */
        if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
-               bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
-               if (!bounce_skb) {
+               priv_info->bouncebuffer = kmalloc(skb->len, GFP_ATOMIC | GFP_DMA);
+               if (!priv_info->bouncebuffer) {
                        ring->current_slot = old_top_slot;
                        ring->used_slots = old_used_slots;
                        err = -ENOMEM;
                        goto out_unmap_hdr;
                }
+               memcpy(priv_info->bouncebuffer, skb->data, skb->len);
 
-               memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
-               dev_kfree_skb_any(skb);
-               skb = bounce_skb;
-               meta->skb = skb;
-               meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
+               meta->dmaaddr = map_descbuffer(ring, priv_info->bouncebuffer, skb->len, 1);
                if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
+                       kfree(priv_info->bouncebuffer);
+                       priv_info->bouncebuffer = NULL;
                        ring->current_slot = old_top_slot;
                        ring->used_slots = old_used_slots;
                        err = -EIO;
-                       goto out_free_bounce;
+                       goto out_unmap_hdr;
                }
        }
 
@@ -1250,8 +1253,6 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
        ops->poke_tx(ring, next_slot(ring, slot));
        return 0;
 
-out_free_bounce:
-       dev_kfree_skb_any(skb);
 out_unmap_hdr:
        unmap_descbuffer(ring, meta_hdr->dmaaddr,
                         hdrsize, 1);
@@ -1315,7 +1316,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
        struct b43_dmaring *ring;
        struct ieee80211_hdr *hdr;
        int err = 0;
-       unsigned long flags;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
        hdr = (struct ieee80211_hdr *)skb->data;
@@ -1331,8 +1331,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
                        dev, skb_get_queue_mapping(skb));
        }
 
-       spin_lock_irqsave(&ring->lock, flags);
-
        B43_WARN_ON(!ring->tx);
 
        if (unlikely(ring->stopped)) {
@@ -1343,7 +1341,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
                if (b43_debug(dev, B43_DBG_DMAVERBOSE))
                        b43err(dev->wl, "Packet after queue stopped\n");
                err = -ENOSPC;
-               goto out_unlock;
+               goto out;
        }
 
        if (unlikely(WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME))) {
@@ -1351,7 +1349,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
                 * full, but queues not stopped. */
                b43err(dev->wl, "DMA queue overflow\n");
                err = -ENOSPC;
-               goto out_unlock;
+               goto out;
        }
 
        /* Assign the queue number to the ring (if not already done before)
@@ -1365,11 +1363,11 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
                 * anymore and must not transmit it unencrypted. */
                dev_kfree_skb_any(skb);
                err = 0;
-               goto out_unlock;
+               goto out;
        }
        if (unlikely(err)) {
                b43err(dev->wl, "DMA tx mapping failure\n");
-               goto out_unlock;
+               goto out;
        }
        ring->nr_tx_packets++;
        if ((free_slots(ring) < TX_SLOTS_PER_FRAME) ||
@@ -1381,8 +1379,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
                        b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
                }
        }
-out_unlock:
-       spin_unlock_irqrestore(&ring->lock, flags);
+out:
 
        return err;
 }
@@ -1394,32 +1391,63 @@ 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);
 
-       spin_lock_irq(&ring->lock);
+       /* 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;
+       }
 
-       B43_WARN_ON(!ring->tx);
        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 (meta->skb)
-                       unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len,
-                                        1);
-               else
+               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));
+
+                       unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1);
+                       kfree(priv_info->bouncebuffer);
+                       priv_info->bouncebuffer = NULL;
+               } else {
                        unmap_descbuffer(ring, meta->dmaaddr,
                                         b43_txhdr_size(dev), 1);
+               }
 
                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);
 
@@ -1435,22 +1463,31 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
                                ring->nr_failed_tx_packets++;
                        ring->nr_total_packet_tries += status->frame_count;
 #endif /* DEBUG */
-                       ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb);
+                       ieee80211_tx_status(dev->wl->hw, meta->skb);
 
-                       /* skb is freed by ieee80211_tx_status_irqsafe() */
-                       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) {
@@ -1461,8 +1498,6 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
                        b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index);
                }
        }
-
-       spin_unlock_irq(&ring->lock);
 }
 
 void b43_dma_get_tx_stats(struct b43_wldev *dev,
@@ -1470,17 +1505,14 @@ void b43_dma_get_tx_stats(struct b43_wldev *dev,
 {
        const int nr_queues = dev->wl->hw->queues;
        struct b43_dmaring *ring;
-       unsigned long flags;
        int i;
 
        for (i = 0; i < nr_queues; i++) {
                ring = select_ring_by_priority(dev, i);
 
-               spin_lock_irqsave(&ring->lock, flags);
                stats[i].len = ring->used_slots / TX_SLOTS_PER_FRAME;
                stats[i].limit = ring->nr_slots / TX_SLOTS_PER_FRAME;
                stats[i].count = ring->nr_tx_packets;
-               spin_unlock_irqrestore(&ring->lock, flags);
        }
 }
 
@@ -1591,22 +1623,14 @@ void b43_dma_rx(struct b43_dmaring *ring)
 
 static void b43_dma_tx_suspend_ring(struct b43_dmaring *ring)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&ring->lock, flags);
        B43_WARN_ON(!ring->tx);
        ring->ops->tx_suspend(ring);
-       spin_unlock_irqrestore(&ring->lock, flags);
 }
 
 static void b43_dma_tx_resume_ring(struct b43_dmaring *ring)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&ring->lock, flags);
        B43_WARN_ON(!ring->tx);
        ring->ops->tx_resume(ring);
-       spin_unlock_irqrestore(&ring->lock, flags);
 }
 
 void b43_dma_tx_suspend(struct b43_wldev *dev)