]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/tx.c
mac80211: use RCU for TX aggregation
[~andy/linux] / net / mac80211 / tx.c
index db25fa9ef135c61ecc60f84b8b8c5da5ec09e603..7bf1f9c9ea34214c8cdb6598728acecac63b797d 100644 (file)
@@ -429,6 +429,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
        struct sta_info *sta = tx->sta;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+       struct ieee80211_local *local = tx->local;
        u32 staflags;
 
        if (unlikely(!sta ||
@@ -476,6 +477,12 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                info->control.vif = &tx->sdata->vif;
                info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
                skb_queue_tail(&sta->ps_tx_buf, tx->skb);
+
+               if (!timer_pending(&local->sta_cleanup))
+                       mod_timer(&local->sta_cleanup,
+                                 round_jiffies(jiffies +
+                                               STA_INFO_CLEANUP_INTERVAL));
+
                return TX_QUEUED;
        }
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
@@ -513,6 +520,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
        else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
                tx->key = key;
        else if (ieee80211_is_mgmt(hdr->frame_control) &&
+                is_multicast_ether_addr(hdr->addr1) &&
+                ieee80211_is_robust_mgmt_frame(hdr) &&
                 (key = rcu_dereference(tx->sdata->default_mgmt_key)))
                tx->key = key;
        else if ((key = rcu_dereference(tx->sdata->default_key)))
@@ -584,7 +593,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
        struct ieee80211_hdr *hdr = (void *)tx->skb->data;
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *rate;
-       int i, len;
+       int i;
+       u32 len;
        bool inval = false, rts = false, short_preamble = false;
        struct ieee80211_tx_rate_control txrc;
        u32 sta_flags;
@@ -593,7 +603,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
 
        sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
-       len = min_t(int, tx->skb->len + FCS_LEN,
+       len = min_t(u32, tx->skb->len + FCS_LEN,
                         tx->local->hw.wiphy->frag_threshold);
 
        /* set up the tx rate control struct we give the RC algo */
@@ -1082,6 +1092,54 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
        return true;
 }
 
+static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
+                                 struct sk_buff *skb,
+                                 struct ieee80211_tx_info *info,
+                                 struct tid_ampdu_tx *tid_tx,
+                                 int tid)
+{
+       bool queued = false;
+
+       if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
+               info->flags |= IEEE80211_TX_CTL_AMPDU;
+       } else {
+               spin_lock(&tx->sta->lock);
+               /*
+                * Need to re-check now, because we may get here
+                *
+                *  1) in the window during which the setup is actually
+                *     already done, but not marked yet because not all
+                *     packets are spliced over to the driver pending
+                *     queue yet -- if this happened we acquire the lock
+                *     either before or after the splice happens, but
+                *     need to recheck which of these cases happened.
+                *
+                *  2) during session teardown, if the OPERATIONAL bit
+                *     was cleared due to the teardown but the pointer
+                *     hasn't been assigned NULL yet (or we loaded it
+                *     before it was assigned) -- in this case it may
+                *     now be NULL which means we should just let the
+                *     packet pass through because splicing the frames
+                *     back is already done.
+                */
+               tid_tx = tx->sta->ampdu_mlme.tid_tx[tid];
+
+               if (!tid_tx) {
+                       /* do nothing, let packet pass through */
+               } else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
+                       info->flags |= IEEE80211_TX_CTL_AMPDU;
+               } else {
+                       queued = true;
+                       info->control.vif = &tx->sdata->vif;
+                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+                       __skb_queue_tail(&tid_tx->pending, skb);
+               }
+               spin_unlock(&tx->sta->lock);
+       }
+
+       return queued;
+}
+
 /*
  * initialises @tx
  */
@@ -1094,8 +1152,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_hdr *hdr;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        int hdrlen, tid;
-       u8 *qc, *state;
-       bool queued = false;
+       u8 *qc;
 
        memset(tx, 0, sizeof(*tx));
        tx->skb = skb;
@@ -1142,41 +1199,21 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
 
        if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
            (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) {
-               unsigned long flags;
                struct tid_ampdu_tx *tid_tx;
 
                qc = ieee80211_get_qos_ctl(hdr);
                tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
 
-               spin_lock_irqsave(&tx->sta->lock, flags);
-               /*
-                * XXX: This spinlock could be fairly expensive, but see the
-                *      comment in agg-tx.c:ieee80211_agg_tx_operational().
-                *      One way to solve this would be to do something RCU-like
-                *      for managing the tid_tx struct and using atomic bitops
-                *      for the actual state -- by introducing an actual
-                *      'operational' bit that would be possible. It would
-                *      require changing ieee80211_agg_tx_operational() to
-                *      set that bit, and changing the way tid_tx is managed
-                *      everywhere, including races between that bit and
-                *      tid_tx going away (tid_tx being added can be easily
-                *      committed to memory before the 'operational' bit).
-                */
-               tid_tx = tx->sta->ampdu_mlme.tid_tx[tid];
-               state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
-               if (*state == HT_AGG_STATE_OPERATIONAL) {
-                       info->flags |= IEEE80211_TX_CTL_AMPDU;
-               } else if (*state != HT_AGG_STATE_IDLE) {
-                       /* in progress */
-                       queued = true;
-                       info->control.vif = &sdata->vif;
-                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-                       __skb_queue_tail(&tid_tx->pending, skb);
-               }
-               spin_unlock_irqrestore(&tx->sta->lock, flags);
+               tid_tx = rcu_dereference(tx->sta->ampdu_mlme.tid_tx[tid]);
+               if (tid_tx) {
+                       bool queued;
 
-               if (unlikely(queued))
-                       return TX_QUEUED;
+                       queued = ieee80211_tx_prep_agg(tx, skb, info,
+                                                      tid_tx, tid);
+
+                       if (unlikely(queued))
+                               return TX_QUEUED;
+               }
        }
 
        if (is_multicast_ether_addr(hdr->addr1)) {
@@ -2242,8 +2279,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 
        info->control.vif = vif;
 
-       info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-       info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+       info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
+                       IEEE80211_TX_CTL_ASSIGN_SEQ |
+                       IEEE80211_TX_CTL_FIRST_FRAGMENT;
  out:
        rcu_read_unlock();
        return skb;