b43: Rewrite TX bounce buffer handling
authorMichael Buesch <mb@bu3sch.de>
Fri, 6 Nov 2009 17:32:44 +0000 (18:32 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 11 Nov 2009 20:23:40 +0000 (15:23 -0500)
Do not mess with the original skb, but allocate an independent bouncebuffer.
This protects against bad interference with mac80211's assumptions about
the skb (which already caused bugs).

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/b43/dma.c
drivers/net/wireless/b43/xmit.h

index de4e804bedf05541c3b9c943bfec268836b24222..b5cd7f57055b3c575574a6f1ac19f28809dbc1f9 100644 (file)
@@ -1157,18 +1157,17 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
 }
 
 static int dma_tx_fragment(struct b43_dmaring *ring,
-                          struct sk_buff **in_skb)
+                          struct sk_buff *skb)
 {
-       struct sk_buff *skb = *in_skb;
        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,34 +1211,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);
-               memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb));
-               bounce_skb->dev = skb->dev;
-               skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb));
-               info = IEEE80211_SKB_CB(bounce_skb);
-
-               dev_kfree_skb_any(skb);
-               skb = bounce_skb;
-               *in_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;
                }
        }
 
@@ -1256,8 +1249,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);
@@ -1362,11 +1353,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
         * static, so we don't need to store it per frame. */
        ring->queue_prio = skb_get_queue_mapping(skb);
 
-       /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing
-        * into the skb data or cb now. */
-       hdr = NULL;
-       info = NULL;
-       err = dma_tx_fragment(ring, &skb);
+       err = dma_tx_fragment(ring, skb);
        if (unlikely(err == -ENOKEY)) {
                /* Drop this packet, as we don't have the encryption key
                 * anymore and must not transmit it unencrypted. */
@@ -1413,12 +1400,17 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
                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 (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;
index 3530de871873a1349e13b4e6c46942c118b5d421..d23ff9fe0c9e3fe1acab1c0d383f8c14630844cf 100644 (file)
@@ -2,6 +2,8 @@
 #define B43_XMIT_H_
 
 #include "main.h"
+#include <net/mac80211.h>
+
 
 #define _b43_declare_plcp_hdr(size) \
        struct b43_plcp_hdr##size {             \
@@ -332,4 +334,21 @@ static inline u8 b43_kidx_to_raw(struct b43_wldev *dev, u8 firmware_kidx)
        return raw_kidx;
 }
 
+/* struct b43_private_tx_info - TX info private to b43.
+ * The structure is placed in (struct ieee80211_tx_info *)->rate_driver_data
+ *
+ * @bouncebuffer: DMA Bouncebuffer (if used)
+ */
+struct b43_private_tx_info {
+       void *bouncebuffer;
+};
+
+static inline struct b43_private_tx_info *
+b43_get_priv_tx_info(struct ieee80211_tx_info *info)
+{
+       BUILD_BUG_ON(sizeof(struct b43_private_tx_info) >
+                    sizeof(info->rate_driver_data));
+       return (struct b43_private_tx_info *)info->rate_driver_data;
+}
+
 #endif /* B43_XMIT_H_ */