mac80211: provide race-free 64-bit traffic counters
[deliverable/linux.git] / net / mac80211 / tx.c
index 5b9602b6240520844b45f6c6b2b293ac8e9ecfb0..3fcdf211810154aa99ae423ad4bc7c3841cabe3d 100644 (file)
@@ -991,15 +991,18 @@ static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
 {
        struct sk_buff *skb;
+       int ac = -1;
 
        if (!tx->sta)
                return TX_CONTINUE;
 
-       tx->sta->tx_packets++;
        skb_queue_walk(&tx->skbs, skb) {
+               ac = skb_get_queue_mapping(skb);
                tx->sta->tx_fragments++;
-               tx->sta->tx_bytes += skb->len;
+               tx->sta->tx_bytes[ac] += skb->len;
        }
+       if (ac >= 0)
+               tx->sta->tx_packets[ac]++;
 
        return TX_CONTINUE;
 }
@@ -1231,34 +1234,40 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
                if (local->queue_stop_reasons[q] ||
                    (!txpending && !skb_queue_empty(&local->pending[q]))) {
                        if (unlikely(info->flags &
-                                       IEEE80211_TX_INTFL_OFFCHAN_TX_OK &&
-                                    local->queue_stop_reasons[q] &
-                                       ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))) {
+                                    IEEE80211_TX_INTFL_OFFCHAN_TX_OK)) {
+                               if (local->queue_stop_reasons[q] &
+                                   ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL)) {
+                                       /*
+                                        * Drop off-channel frames if queues
+                                        * are stopped for any reason other
+                                        * than off-channel operation. Never
+                                        * queue them.
+                                        */
+                                       spin_unlock_irqrestore(
+                                               &local->queue_stop_reason_lock,
+                                               flags);
+                                       ieee80211_purge_tx_queue(&local->hw,
+                                                                skbs);
+                                       return true;
+                               }
+                       } else {
+
                                /*
-                                * Drop off-channel frames if queues are stopped
-                                * for any reason other than off-channel
-                                * operation. Never queue them.
+                                * Since queue is stopped, queue up frames for
+                                * later transmission from the tx-pending
+                                * tasklet when the queue is woken again.
                                 */
-                               spin_unlock_irqrestore(
-                                       &local->queue_stop_reason_lock, flags);
-                               ieee80211_purge_tx_queue(&local->hw, skbs);
-                               return true;
+                               if (txpending)
+                                       skb_queue_splice_init(skbs,
+                                                             &local->pending[q]);
+                               else
+                                       skb_queue_splice_tail_init(skbs,
+                                                                  &local->pending[q]);
+
+                               spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+                                                      flags);
+                               return false;
                        }
-
-                       /*
-                        * Since queue is stopped, queue up frames for later
-                        * transmission from the tx-pending tasklet when the
-                        * queue is woken again.
-                        */
-                       if (txpending)
-                               skb_queue_splice_init(skbs, &local->pending[q]);
-                       else
-                               skb_queue_splice_tail_init(skbs,
-                                                          &local->pending[q]);
-
-                       spin_unlock_irqrestore(&local->queue_stop_reason_lock,
-                                              flags);
-                       return false;
                }
                spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
@@ -1844,9 +1853,24 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                }
 
                if (!is_multicast_ether_addr(skb->data)) {
+                       struct sta_info *next_hop;
+                       bool mpp_lookup = true;
+
                        mpath = mesh_path_lookup(sdata, skb->data);
-                       if (!mpath)
+                       if (mpath) {
+                               mpp_lookup = false;
+                               next_hop = rcu_dereference(mpath->next_hop);
+                               if (!next_hop ||
+                                   !(mpath->flags & (MESH_PATH_ACTIVE |
+                                                     MESH_PATH_RESOLVING)))
+                                       mpp_lookup = true;
+                       }
+
+                       if (mpp_lookup)
                                mppath = mpp_path_lookup(sdata, skb->data);
+
+                       if (mppath && mpath)
+                               mesh_path_del(mpath->sdata, mpath->dst);
                }
 
                /*
@@ -2017,24 +2041,14 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                skb = skb_clone(skb, GFP_ATOMIC);
                if (skb) {
                        unsigned long flags;
-                       int id, r;
+                       int id;
 
                        spin_lock_irqsave(&local->ack_status_lock, flags);
-                       r = idr_get_new_above(&local->ack_status_frames,
-                                             orig_skb, 1, &id);
-                       if (r == -EAGAIN) {
-                               idr_pre_get(&local->ack_status_frames,
-                                           GFP_ATOMIC);
-                               r = idr_get_new_above(&local->ack_status_frames,
-                                                     orig_skb, 1, &id);
-                       }
-                       if (WARN_ON(!id) || id > 0xffff) {
-                               idr_remove(&local->ack_status_frames, id);
-                               r = -ERANGE;
-                       }
+                       id = idr_alloc(&local->ack_status_frames, orig_skb,
+                                      1, 0x10000, GFP_ATOMIC);
                        spin_unlock_irqrestore(&local->ack_status_lock, flags);
 
-                       if (!r) {
+                       if (id >= 0) {
                                info_id = id;
                                info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
                        } else if (skb_shared(skb)) {
@@ -2360,9 +2374,9 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
        if (local->tim_in_locked_section) {
                __ieee80211_beacon_add_tim(sdata, ps, skb);
        } else {
-               spin_lock(&local->tim_lock);
+               spin_lock_bh(&local->tim_lock);
                __ieee80211_beacon_add_tim(sdata, ps, skb);
-               spin_unlock(&local->tim_lock);
+               spin_unlock_bh(&local->tim_lock);
        }
 
        return 0;
@@ -2734,7 +2748,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
                                cpu_to_le16(IEEE80211_FCTL_MOREDATA);
                }
 
-               sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
+               if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                       sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
                if (!ieee80211_tx_prepare(sdata, &tx, skb))
                        break;
                dev_kfree_skb_any(skb);
This page took 0.029146 seconds and 5 git commands to generate.