wlcore: don't get the hlid from a queued skb
authorArik Nemtsov <arik@wizery.com>
Fri, 27 Jul 2012 07:11:32 +0000 (10:11 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 31 Jul 2012 14:11:02 +0000 (16:11 +0200)
There was a bug hiding here since the hlid was sometimes inferred from
the sta, which might be invalid at this point.

Instead, propagate the hlid from the skb-queue where we got the skb
in the first place.

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Acked-by: Luciano Coelho <coelho@ti.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/ti/wlcore/tx.c

index f0081f746482d8060810d27486c58e50d79df928..6ec29df386e07c28501ba7fe004664de128e1852 100644 (file)
@@ -344,13 +344,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
 /* caller must hold wl->mutex */
 static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                                  struct sk_buff *skb, u32 buf_offset)
+                                  struct sk_buff *skb, u32 buf_offset, u8 hlid)
 {
        struct ieee80211_tx_info *info;
        u32 extra = 0;
        int ret = 0;
        u32 total_len;
-       u8 hlid;
        bool is_dummy;
        bool is_gem = false;
 
@@ -359,9 +358,13 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                return -EINVAL;
        }
 
+       if (hlid == WL12XX_INVALID_LINK_ID) {
+               wl1271_error("invalid hlid. dropping skb 0x%p", skb);
+               return -EINVAL;
+       }
+
        info = IEEE80211_SKB_CB(skb);
 
-       /* TODO: handle dummy packets on multi-vifs */
        is_dummy = wl12xx_is_dummy_packet(wl, skb);
 
        if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
@@ -386,11 +389,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
                is_gem = (cipher == WL1271_CIPHER_SUITE_GEM);
        }
-       hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
-       if (hlid == WL12XX_INVALID_LINK_ID) {
-               wl1271_error("invalid hlid. dropping skb 0x%p", skb);
-               return -EINVAL;
-       }
 
        ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid,
                                 is_gem);
@@ -517,7 +515,8 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
 }
 
 static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
-                                             struct wl12xx_vif *wlvif)
+                                             struct wl12xx_vif *wlvif,
+                                             u8 *hlid)
 {
        struct sk_buff *skb = NULL;
        int i, h, start_hlid;
@@ -544,10 +543,11 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
        if (!skb)
                wlvif->last_tx_hlid = 0;
 
+       *hlid = wlvif->last_tx_hlid;
        return skb;
 }
 
-static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
+static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
 {
        unsigned long flags;
        struct wl12xx_vif *wlvif = wl->last_wlvif;
@@ -556,7 +556,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
        /* continue from last wlvif (round robin) */
        if (wlvif) {
                wl12xx_for_each_wlvif_continue(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif);
+                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
                        if (skb) {
                                wl->last_wlvif = wlvif;
                                break;
@@ -565,13 +565,15 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
        }
 
        /* dequeue from the system HLID before the restarting wlvif list */
-       if (!skb)
+       if (!skb) {
                skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
+               *hlid = wl->system_hlid;
+       }
 
        /* do a new pass over the wlvif list */
        if (!skb) {
                wl12xx_for_each_wlvif(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif);
+                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
                        if (skb) {
                                wl->last_wlvif = wlvif;
                                break;
@@ -591,6 +593,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
                int q;
 
                skb = wl->dummy_packet;
+               *hlid = wl->system_hlid;
                q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                spin_lock_irqsave(&wl->wl_lock, flags);
                WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
@@ -602,7 +605,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
 }
 
 static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                                 struct sk_buff *skb)
+                                 struct sk_buff *skb, u8 hlid)
 {
        unsigned long flags;
        int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
@@ -610,7 +613,6 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        if (wl12xx_is_dummy_packet(wl, skb)) {
                set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
        } else {
-               u8 hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
                skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
 
                /* make sure we dequeue the same packet next time */
@@ -686,26 +688,30 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
        unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
        int ret = 0;
        int bus_ret = 0;
+       u8 hlid;
 
        if (unlikely(wl->state == WL1271_STATE_OFF))
                return 0;
 
-       while ((skb = wl1271_skb_dequeue(wl))) {
+       while ((skb = wl1271_skb_dequeue(wl, &hlid))) {
                struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
                bool has_data = false;
 
                wlvif = NULL;
                if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
                        wlvif = wl12xx_vif_to_data(info->control.vif);
+               else
+                       hlid = wl->system_hlid;
 
                has_data = wlvif && wl1271_tx_is_data_present(skb);
-               ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset);
+               ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset,
+                                             hlid);
                if (ret == -EAGAIN) {
                        /*
                         * Aggregation buffer is full.
                         * Flush buffer and try again.
                         */
-                       wl1271_skb_queue_head(wl, wlvif, skb);
+                       wl1271_skb_queue_head(wl, wlvif, skb, hlid);
 
                        buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset,
                                                            last_len);
@@ -722,7 +728,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
                         * Firmware buffer is full.
                         * Queue back last skb, and stop aggregating.
                         */
-                       wl1271_skb_queue_head(wl, wlvif, skb);
+                       wl1271_skb_queue_head(wl, wlvif, skb, hlid);
                        /* No work left, avoid scheduling redundant tx work */
                        set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
                        goto out_ack;
@@ -732,7 +738,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
                                 * fw still expects dummy packet,
                                 * so re-enqueue it
                                 */
-                               wl1271_skb_queue_head(wl, wlvif, skb);
+                               wl1271_skb_queue_head(wl, wlvif, skb, hlid);
                        else
                                ieee80211_free_txskb(wl->hw, skb);
                        goto out_ack;