mac80211: do not pass PS frames out of mac80211 again
authorJohannes Berg <johannes@sipsolutions.net>
Sun, 7 Jun 2009 19:58:37 +0000 (21:58 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 10 Jun 2009 17:28:37 +0000 (13:28 -0400)
In order to handle powersave frames properly we had needed
to pass these out to the device queues again, and introduce
the skb->requeue bit. This, however, also has unnecessary
overhead by needing to 'clean up' already tried frames, and
this clean-up code is also buggy when software encryption
is used.

Instead of sending the frames via the master netdev queue
again, simply put them into the pending queue. This also
fixes a problem where frames for that particular station
could be reordered when some were still on the software
queues and older ones are re-injected into the software
queue after them.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/linux/skbuff.h
include/net/mac80211.h
net/core/skbuff.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/rx.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wme.c

index f1c93b878b3b95a2c58d21906fa24672f20d8d69..fa51293f270811832b97e8f660f979ae14e0bc44 100644 (file)
@@ -304,9 +304,6 @@ typedef unsigned char *sk_buff_data_t;
  *     @tc_verd: traffic control verdict
  *     @ndisc_nodetype: router type (from link layer)
  *     @do_not_encrypt: set to prevent encryption of this frame
- *     @requeue: set to indicate that the wireless core should attempt
- *             a software retry on this frame if we failed to
- *             receive an ACK for it
  *     @dma_cookie: a cookie to one of several possible DMA operations
  *             done by skb DMA functions
  *     @secmark: security marking
@@ -380,7 +377,6 @@ struct sk_buff {
 #endif
 #if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
        __u8                    do_not_encrypt:1;
-       __u8                    requeue:1;
 #endif
        /* 0/13/14 bit hole */
 
index 17d61d19d9125cbdde3cdb2f8a5840d9bf4f2421..c061044769737f72a54bb1afe8e70c6d77ca38a7 100644 (file)
@@ -239,6 +239,8 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
  *     used to indicate that a pending frame requires TX processing before
  *     it can be sent out.
+ * @IEEE80211_TX_INTFL_RETRIED: completely internal to mac80211,
+ *     used to indicate that a frame was already retried due to PS
  */
 enum mac80211_tx_control_flags {
        IEEE80211_TX_CTL_REQ_TX_STATUS          = BIT(0),
@@ -256,6 +258,7 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_CTL_RATE_CTRL_PROBE        = BIT(12),
        IEEE80211_TX_INTFL_RCALGO               = BIT(13),
        IEEE80211_TX_INTFL_NEED_TXPROCESSING    = BIT(14),
+       IEEE80211_TX_INTFL_RETRIED              = BIT(15),
 };
 
 /**
index 49961ba3c0f64666250cd8f345dcb3914af49a10..b94d777e3eb4d9a5560bf7b5de0ee94801f59e8e 100644 (file)
@@ -552,7 +552,6 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->vlan_tci           = old->vlan_tci;
 #if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
        new->do_not_encrypt     = old->do_not_encrypt;
-       new->requeue            = old->requeue;
 #endif
 
        skb_copy_secmark(new, old);
index c088c46704a316cfcb55a37e9ddf26a414a21f9e..4dbc289641962a1fddad9a520ed9d069a7bc00e6 100644 (file)
@@ -589,6 +589,7 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
        IEEE80211_QUEUE_STOP_REASON_SUSPEND,
        IEEE80211_QUEUE_STOP_REASON_PENDING,
+       IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
 };
 
 struct ieee80211_master_priv {
@@ -1121,6 +1122,10 @@ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
                                    enum queue_stop_reason reason);
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
                                    enum queue_stop_reason reason);
+void ieee80211_add_pending_skb(struct ieee80211_local *local,
+                              struct sk_buff *skb);
+int ieee80211_add_pending_skbs(struct ieee80211_local *local,
+                              struct sk_buff_head *skbs);
 
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg,
index 2683df91807369a566014822e3f99bba7543f8b0..092a017b237e35faa10ef4c72a5f0e21b64c33b6 100644 (file)
@@ -369,60 +369,12 @@ static void ieee80211_tasklet_handler(unsigned long data)
        }
 }
 
-/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to
- * make a prepared TX frame (one that has been given to hw) to look like brand
- * new IEEE 802.11 frame that is ready to go through TX processing again.
- */
-static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
-                                     struct ieee80211_key *key,
-                                     struct sk_buff *skb)
-{
-       unsigned int hdrlen, iv_len, mic_len;
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-       hdrlen = ieee80211_hdrlen(hdr->frame_control);
-
-       if (!key)
-               goto no_key;
-
-       switch (key->conf.alg) {
-       case ALG_WEP:
-               iv_len = WEP_IV_LEN;
-               mic_len = WEP_ICV_LEN;
-               break;
-       case ALG_TKIP:
-               iv_len = TKIP_IV_LEN;
-               mic_len = TKIP_ICV_LEN;
-               break;
-       case ALG_CCMP:
-               iv_len = CCMP_HDR_LEN;
-               mic_len = CCMP_MIC_LEN;
-               break;
-       default:
-               goto no_key;
-       }
-
-       if (skb->len >= hdrlen + mic_len &&
-           !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-               skb_trim(skb, skb->len - mic_len);
-       if (skb->len >= hdrlen + iv_len) {
-               memmove(skb->data + iv_len, skb->data, hdrlen);
-               hdr = (struct ieee80211_hdr *)skb_pull(skb, iv_len);
-       }
-
-no_key:
-       if (ieee80211_is_data_qos(hdr->frame_control)) {
-               hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
-               memmove(skb->data + IEEE80211_QOS_CTL_LEN, skb->data,
-                       hdrlen - IEEE80211_QOS_CTL_LEN);
-               skb_pull(skb, IEEE80211_QOS_CTL_LEN);
-       }
-}
-
 static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
                                            struct sta_info *sta,
                                            struct sk_buff *skb)
 {
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
        sta->tx_filtered_count++;
 
        /*
@@ -464,16 +416,15 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
         */
        if (test_sta_flags(sta, WLAN_STA_PS) &&
            skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
-               ieee80211_remove_tx_extra(local, sta->key, skb);
                skb_queue_tail(&sta->tx_filtered, skb);
                return;
        }
 
-       if (!test_sta_flags(sta, WLAN_STA_PS) && !skb->requeue) {
+       if (!test_sta_flags(sta, WLAN_STA_PS) &&
+           !(info->flags & IEEE80211_TX_INTFL_RETRIED)) {
                /* Software retry the packet once */
-               skb->requeue = 1;
-               ieee80211_remove_tx_extra(local, sta->key, skb);
-               dev_queue_xmit(skb);
+               info->flags |= IEEE80211_TX_INTFL_RETRIED;
+               ieee80211_add_pending_skb(local, skb);
                return;
        }
 
index 754125185109f75d8e654379d6c94901e9bc1e41..de5bba7f910ae876637c3002544a6963eebccdfd 100644 (file)
@@ -797,8 +797,7 @@ static int ap_sta_ps_end(struct sta_info *sta)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       int sent = 0;
+       int sent, buffered;
 
        atomic_dec(&sdata->bss->num_sta_ps);
 
@@ -814,22 +813,16 @@ static int ap_sta_ps_end(struct sta_info *sta)
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 
        /* Send all buffered frames to the station */
-       while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
-               sent++;
-               skb->requeue = 1;
-               dev_queue_xmit(skb);
-       }
-       while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
-               local->total_ps_buffered--;
-               sent++;
+       sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
+       buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf);
+       sent += buffered;
+       local->total_ps_buffered -= buffered;
+
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-               printk(KERN_DEBUG "%s: STA %pM aid %d send PS frame "
-                      "since STA not sleeping anymore\n", sdata->dev->name,
-                      sta->sta.addr, sta->sta.aid);
+       printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
+              "since STA not sleeping anymore\n", sdata->dev->name,
+              sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-               skb->requeue = 1;
-               dev_queue_xmit(skb);
-       }
 
        return sent;
 }
index 1436f747531a378122a6131ddccc95fae2205c1e..bfaa9ce331460484f384a3cc1f99f3728e63c378 100644 (file)
@@ -400,6 +400,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                        sta_info_set_tim_bit(sta);
 
                info->control.jiffies = jiffies;
+               info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
                skb_queue_tail(&sta->ps_tx_buf, tx->skb);
                return TX_QUEUED;
        }
@@ -420,7 +421,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                 * frame filtering and keeps a station  blacklist on its own
                 * (e.g: p54), so that frames can be delivered unimpeded.
                 *
-                * Note: It should be save to disable the filter now.
+                * Note: It should be safe to disable the filter now.
                 * As, it is really unlikely that we still have any pending
                 * frame for this station in the hw's buffers/fifos left,
                 * that is not rejected with a unsuccessful tx_status yet.
index 22f63815fb3623d190e772fe388da047a347422e..66ce96a69f318bab53cf88a8946c49b494f7b9ab 100644 (file)
@@ -341,6 +341,52 @@ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
 }
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
+void ieee80211_add_pending_skb(struct ieee80211_local *local,
+                              struct sk_buff *skb)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       unsigned long flags;
+       int queue = skb_get_queue_mapping(skb);
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_PENDING);
+       skb_queue_tail(&local->pending[queue], skb);
+       __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+int ieee80211_add_pending_skbs(struct ieee80211_local *local,
+                              struct sk_buff_head *skbs)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       struct sk_buff *skb;
+       unsigned long flags;
+       int queue, ret = 0, i;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       for (i = 0; i < hw->queues; i++)
+               __ieee80211_stop_queue(hw, i,
+                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+
+       while ((skb = skb_dequeue(skbs))) {
+               ret++;
+               queue = skb_get_queue_mapping(skb);
+               skb_queue_tail(&local->pending[queue], skb);
+       }
+
+       for (i = 0; i < hw->queues; i++) {
+               if (ret)
+                       __ieee80211_stop_queue(hw, i,
+                               IEEE80211_QUEUE_STOP_REASON_PENDING);
+               __ieee80211_wake_queue(hw, i,
+                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       }
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+       return ret;
+}
+
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
                                    enum queue_stop_reason reason)
 {
index 694343b9102bd19c69a341eb4acd330220c59d71..116a923b14d621e1043b1cb07a5cecaf755039aa 100644 (file)
@@ -101,7 +101,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
         * Now we know the 1d priority, fill in the QoS header if
         * there is one (and we haven't done this before).
         */
-       if (!skb->requeue && ieee80211_is_data_qos(hdr->frame_control)) {
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
                u8 *p = ieee80211_get_qos_ctl(hdr);
                u8 ack_policy = 0;
                tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;