wl12xx: Clean up the dummy packet mechanism
authorIdo Yariv <ido@wizery.com>
Thu, 31 Mar 2011 08:06:59 +0000 (10:06 +0200)
committerLuciano Coelho <coelho@ti.com>
Tue, 19 Apr 2011 13:49:19 +0000 (16:49 +0300)
The current implementation allocates a skb each time one is requested by
the firmware. Since dummy packets are handled differently than regular
packets, the skb needs to be marked. Currently, this is done by
setting the pkt_type member to 5. This might not be safe, as we cannot
be sure that there won't be any other packets with this pkt_type value.

Since the packet does not change from one request to another, we can
simply allocate a dummy packet template and always send it. All changes
to the skb done during packet preparation must be reverted, so the same
skb can be reused.

The dummy packets are not transmitted, therefore there's no need to set
the BSSID or our own MAC address.

In addition, the header portion of the packet was zeroed by mistake, so
fix that as well.

Signed-off-by: Ido Yariv <ido@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/tx.c
drivers/net/wireless/wl12xx/tx.h
drivers/net/wireless/wl12xx/wl12xx.h

index 1cd396306e7b53cf7f5c809ac313dadd3edf950d..7dce24c0b331b8dae4f934e57babb79f781e38de 100644 (file)
@@ -1212,20 +1212,46 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
-#define TX_DUMMY_PACKET_SIZE 1400
 int wl1271_tx_dummy_packet(struct wl1271 *wl)
 {
-       struct sk_buff *skb = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
+       wl->tx_queue_count++;
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       /* The FW is low on RX memory blocks, so send the dummy packet asap */
+       if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
+               wl1271_tx_work_locked(wl);
+
+       /*
+        * If the FW TX is busy, TX work will be scheduled by the threaded
+        * interrupt handler function
+        */
+       return 0;
+}
+
+/*
+ * The size of the dummy packet should be at least 1400 bytes. However, in
+ * order to minimize the number of bus transactions, aligning it to 512 bytes
+ * boundaries could be beneficial, performance wise
+ */
+#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
+
+struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
+{
+       struct sk_buff *skb;
        struct ieee80211_hdr_3addr *hdr;
-       int ret = 0;
+       unsigned int dummy_packet_size;
+
+       dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
+                           sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
 
-       skb = dev_alloc_skb(
-               sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) +
-               TX_DUMMY_PACKET_SIZE);
+       skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
        if (!skb) {
-               wl1271_warning("failed to allocate buffer for dummy packet");
-               ret = -ENOMEM;
-               goto out;
+               wl1271_warning("Failed to allocate a dummy packet skb");
+               return NULL;
        }
 
        skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
@@ -1233,29 +1259,22 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl)
        hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
        memset(hdr, 0, sizeof(*hdr));
        hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
-                                        IEEE80211_FCTL_TODS  |
-                                        IEEE80211_STYPE_NULLFUNC);
-
-       memcpy(hdr->addr1, wl->bssid, ETH_ALEN);
-       memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN);
-       memcpy(hdr->addr3, wl->bssid, ETH_ALEN);
+                                        IEEE80211_STYPE_NULLFUNC |
+                                        IEEE80211_FCTL_TODS);
 
-       skb_put(skb, TX_DUMMY_PACKET_SIZE);
+       memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
 
-       memset(skb->data, 0, TX_DUMMY_PACKET_SIZE);
-
-       skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ;
        /* Dummy packets require the TID to be management */
        skb->priority = WL1271_TID_MGMT;
-       /* CONF_TX_AC_VO */
-       skb->queue_mapping = 0;
 
-       wl1271_op_tx(wl->hw, skb);
+       /* Initialize all fields that might be used */
+       skb->queue_mapping = 0;
+       memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
 
-out:
-       return ret;
+       return skb;
 }
 
+
 static struct notifier_block wl1271_dev_notifier = {
        .notifier_call = wl1271_dev_notify,
 };
@@ -3653,11 +3672,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
                goto err_hw;
        }
 
+       wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
+       if (!wl->dummy_packet) {
+               ret = -ENOMEM;
+               goto err_aggr;
+       }
+
        /* Register platform device */
        ret = platform_device_register(wl->plat_dev);
        if (ret) {
                wl1271_error("couldn't register platform device");
-               goto err_aggr;
+               goto err_dummy_packet;
        }
        dev_set_drvdata(&wl->plat_dev->dev, wl);
 
@@ -3683,6 +3708,9 @@ err_bt_coex_state:
 err_platform:
        platform_device_unregister(wl->plat_dev);
 
+err_dummy_packet:
+       dev_kfree_skb(wl->dummy_packet);
+
 err_aggr:
        free_pages((unsigned long)wl->aggr_buf, order);
 
@@ -3702,6 +3730,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
 int wl1271_free_hw(struct wl1271 *wl)
 {
        platform_device_unregister(wl->plat_dev);
+       dev_kfree_skb(wl->dummy_packet);
        free_pages((unsigned long)wl->aggr_buf,
                        get_order(WL1271_AGGR_BUFFER_SIZE));
        kfree(wl->plat_dev);
index ba69ba7051fae6919b31a66fa5893c309e09dbe3..67245ad396ea3deb998e5a97556959f9d1327dcf 100644 (file)
@@ -219,6 +219,11 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
        return ret;
 }
 
+static bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
+{
+       return wl->dummy_packet == skb;
+}
+
 static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
                              u32 extra, struct ieee80211_tx_info *control,
                              u8 hlid)
@@ -253,7 +258,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
        ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
        desc->tid = skb->priority;
 
-       if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) {
+       if (wl12xx_is_dummy_packet(wl, skb)) {
                /*
                 * FW expects the dummy packet to have an invalid session id -
                 * any session id that is different than the one set in the join
@@ -396,6 +401,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
        memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len);
        memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len);
 
+       /* Revert side effects in the dummy packet skb, so it can be reused */
+       if (wl12xx_is_dummy_packet(wl, skb))
+               skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
+
        return total_len;
 }
 
@@ -508,10 +517,23 @@ out:
 
 static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
 {
+       unsigned long flags;
+       struct sk_buff *skb = NULL;
+
        if (wl->bss_type == BSS_TYPE_AP_BSS)
-               return wl1271_ap_skb_dequeue(wl);
+               skb = wl1271_ap_skb_dequeue(wl);
+       else
+               skb = wl1271_sta_skb_dequeue(wl);
 
-       return wl1271_sta_skb_dequeue(wl);
+       if (!skb &&
+           test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
+               skb = wl->dummy_packet;
+               spin_lock_irqsave(&wl->wl_lock, flags);
+               wl->tx_queue_count--;
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
+       }
+
+       return skb;
 }
 
 static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
@@ -519,7 +541,9 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
        unsigned long flags;
        int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
 
-       if (wl->bss_type == BSS_TYPE_AP_BSS) {
+       if (wl12xx_is_dummy_packet(wl, skb)) {
+               set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
+       } else if (wl->bss_type == BSS_TYPE_AP_BSS) {
                u8 hlid = wl1271_tx_get_hlid(skb);
                skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
 
@@ -628,8 +652,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
        skb = wl->tx_frames[id];
        info = IEEE80211_SKB_CB(skb);
 
-       if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) {
-               dev_kfree_skb(skb);
+       if (wl12xx_is_dummy_packet(wl, skb)) {
                wl1271_free_tx_id(wl, id);
                return;
        }
@@ -764,9 +787,7 @@ void wl1271_tx_reset(struct wl1271 *wl)
                                wl1271_debug(DEBUG_TX, "freeing skb 0x%p",
                                             skb);
 
-                               if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) {
-                                       dev_kfree_skb(skb);
-                               } else {
+                               if (!wl12xx_is_dummy_packet(wl, skb)) {
                                        info = IEEE80211_SKB_CB(skb);
                                        info->status.rates[0].idx = -1;
                                        info->status.rates[0].count = 0;
@@ -792,9 +813,7 @@ void wl1271_tx_reset(struct wl1271 *wl)
                wl1271_free_tx_id(wl, i);
                wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb);
 
-               if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) {
-                       dev_kfree_skb(skb);
-               } else {
+               if (!wl12xx_is_dummy_packet(wl, skb)) {
                        /*
                         * Remove private headers before passing the skb to
                         * mac80211
index d6b05d980626d8ea40cdffee8c687754771deb36..fc7835c4cf6398bfb55be0ae01a0662aaf07111d 100644 (file)
@@ -42,8 +42,6 @@
 #define TX_HW_ATTR_TX_CMPLT_REQ          BIT(12)
 #define TX_HW_ATTR_TX_DUMMY_REQ          BIT(13)
 
-#define TX_PKT_TYPE_DUMMY_REQ            5
-
 #define TX_HW_ATTR_OFST_SAVE_RETRIES     0
 #define TX_HW_ATTR_OFST_HEADER_PAD       1
 #define TX_HW_ATTR_OFST_SESSION_COUNTER  2
index c7c42b687f5ec37a5bff85f6d474d601ad8e965d..1b430d2aec4ec678e33598c7d5b946363baf3ff9 100644 (file)
@@ -356,6 +356,7 @@ enum wl12xx_flags {
        WL1271_FLAG_FW_TX_BUSY,
        WL1271_FLAG_AP_STARTED,
        WL1271_FLAG_IF_INITIALIZED,
+       WL1271_FLAG_DUMMY_PACKET_PENDING,
 };
 
 struct wl1271_link {
@@ -461,6 +462,9 @@ struct wl1271 {
        /* Intermediate buffer, used for packet aggregation */
        u8 *aggr_buf;
 
+       /* Reusable dummy packet template */
+       struct sk_buff *dummy_packet;
+
        /* Network stack work  */
        struct work_struct netstack_work;