]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/mac80211/tx.c
mac80211/iwlwifi: move virtual A-MDPU queue bookkeeping to iwlwifi
[linux-2.6-omap-h63xx.git] / net / mac80211 / tx.c
index 0d97cad84b1b777add1dfbf9fca847d4540a633e..3fb04a86444d959d980f5e7d3e0ce1a2c779a63a 100644 (file)
@@ -984,9 +984,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
        struct ieee80211_hdr *hdr;
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
        int hdrlen, tid;
        u8 *qc, *state;
+       bool queued = false;
 
        memset(tx, 0, sizeof(*tx));
        tx->skb = skb;
@@ -1013,20 +1013,53 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
                 */
        }
 
+       /*
+        * If this flag is set to true anywhere, and we get here,
+        * we are doing the needed processing, so remove the flag
+        * now.
+        */
+       info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+
        hdr = (struct ieee80211_hdr *) skb->data;
 
        tx->sta = sta_info_get(local, hdr->addr1);
 
-       if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+       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)
+               if (*state == HT_AGG_STATE_OPERATIONAL) {
                        info->flags |= IEEE80211_TX_CTL_AMPDU;
+               } else if (*state != HT_AGG_STATE_IDLE) {
+                       /* in progress */
+                       queued = true;
+                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+                       __skb_queue_tail(&tid_tx->pending, skb);
+               }
                spin_unlock_irqrestore(&tx->sta->lock, flags);
+
+               if (unlikely(queued))
+                       return TX_QUEUED;
        }
 
        if (is_multicast_ether_addr(hdr->addr1)) {
@@ -1077,18 +1110,26 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local,
        }
        if (unlikely(!dev))
                return -ENODEV;
-       /* initialises tx with control */
+       /*
+        * initialises tx with control
+        *
+        * return value is safe to ignore here because this function
+        * can only be invoked for multicast frames
+        *
+        * XXX: clean up
+        */
        __ieee80211_tx_prepare(tx, skb, dev);
        dev_put(dev);
        return 0;
 }
 
 static int __ieee80211_tx(struct ieee80211_local *local,
-                         struct ieee80211_tx_data *tx)
+                         struct sk_buff **skbp,
+                         struct sta_info *sta)
 {
-       struct sk_buff *skb = tx->skb, *next;
+       struct sk_buff *skb = *skbp, *next;
        struct ieee80211_tx_info *info;
-       int ret;
+       int ret, len;
        bool fragm = false;
 
        local->mdev->trans_start = jiffies;
@@ -1104,30 +1145,16 @@ static int __ieee80211_tx(struct ieee80211_local *local,
                        info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
                                         IEEE80211_TX_CTL_FIRST_FRAGMENT);
 
-               /*
-                * Internally, we need to have the queue mapping point to
-                * the real AC queue, not the virtual A-MPDU queue. This
-                * now finally sets the queue to what the driver wants.
-                * We will later move this down into the only driver that
-                * needs it, iwlwifi.
-                */
-               if (tx->sta && local->hw.ampdu_queues &&
-                   info->flags & IEEE80211_TX_CTL_AMPDU) {
-                       unsigned long flags;
-                       u8 *qc = ieee80211_get_qos_ctl((void *) skb->data);
-                       int tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-
-                       spin_lock_irqsave(&tx->sta->lock, flags);
-                       skb_set_queue_mapping(skb, local->hw.queues +
-                                                  tx->sta->tid_to_tx_q[tid]);
-                       spin_unlock_irqrestore(&tx->sta->lock, flags);
-               }
-
                next = skb->next;
+               len = skb->len;
                ret = local->ops->tx(local_to_hw(local), skb);
+               if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
+                       dev_kfree_skb(skb);
+                       ret = NETDEV_TX_OK;
+               }
                if (ret != NETDEV_TX_OK)
                        return IEEE80211_TX_AGAIN;
-               tx->skb = skb = next;
+               *skbp = skb = next;
                ieee80211_led_tx(local, 1);
                fragm = true;
        }
@@ -1182,23 +1209,26 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        return 0;
 }
 
-static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
+static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
+                        bool txpending)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct sta_info *sta;
        struct ieee80211_tx_data tx;
        ieee80211_tx_result res_prepare;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       int ret;
+       struct sk_buff *next;
+       unsigned long flags;
+       int ret, retries;
        u16 queue;
 
        queue = skb_get_queue_mapping(skb);
 
-       WARN_ON(test_bit(queue, local->queues_pending));
+       WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue]));
 
        if (unlikely(skb->len < 10)) {
                dev_kfree_skb(skb);
-               return 0;
+               return;
        }
 
        rcu_read_lock();
@@ -1206,10 +1236,13 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
        /* initialises tx */
        res_prepare = __ieee80211_tx_prepare(&tx, skb, dev);
 
-       if (res_prepare == TX_DROP) {
+       if (unlikely(res_prepare == TX_DROP)) {
                dev_kfree_skb(skb);
                rcu_read_unlock();
-               return 0;
+               return;
+       } else if (unlikely(res_prepare == TX_QUEUED)) {
+               rcu_read_unlock();
+               return;
        }
 
        sta = tx.sta;
@@ -1219,57 +1252,71 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
        if (invoke_tx_handlers(&tx))
                goto out;
 
-retry:
-       ret = __ieee80211_tx(local, &tx);
-       if (ret) {
-               struct ieee80211_tx_stored_packet *store;
-
+       retries = 0;
+ retry:
+       ret = __ieee80211_tx(local, &tx.skb, tx.sta);
+       switch (ret) {
+       case IEEE80211_TX_OK:
+               break;
+       case IEEE80211_TX_AGAIN:
                /*
                 * Since there are no fragmented frames on A-MPDU
                 * queues, there's no reason for a driver to reject
                 * a frame there, warn and drop it.
                 */
-               if (ret != IEEE80211_TX_PENDING)
-                       if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
-                               goto drop;
+               if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
+                       goto drop;
+               /* fall through */
+       case IEEE80211_TX_PENDING:
+               skb = tx.skb;
+
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+               if (__netif_subqueue_stopped(local->mdev, queue)) {
+                       do {
+                               next = skb->next;
+                               skb->next = NULL;
+                               if (unlikely(txpending))
+                                       skb_queue_head(&local->pending[queue],
+                                                      skb);
+                               else
+                                       skb_queue_tail(&local->pending[queue],
+                                                      skb);
+                       } while ((skb = next));
+
+                       /*
+                        * Make sure nobody will enable the queue on us
+                        * (without going through the tasklet) nor disable the
+                        * netdev queue underneath the pending handling code.
+                        */
+                       __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
+                                 &local->queue_stop_reasons[queue]);
 
-               store = &local->pending_packet[queue];
+                       spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+                                              flags);
+               } else {
+                       spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+                                              flags);
 
-               set_bit(queue, local->queues_pending);
-               smp_mb();
-               /*
-                * When the driver gets out of buffers during sending of
-                * fragments and calls ieee80211_stop_queue, the netif
-                * subqueue is stopped. There is, however, a small window
-                * in which the PENDING bit is not yet set. If a buffer
-                * gets available in that window (i.e. driver calls
-                * ieee80211_wake_queue), we would end up with ieee80211_tx
-                * called with the PENDING bit still set. Prevent this by
-                * continuing transmitting here when that situation is
-                * possible to have happened.
-                */
-               if (!__netif_subqueue_stopped(local->mdev, queue)) {
-                       clear_bit(queue, local->queues_pending);
+                       retries++;
+                       if (WARN(retries > 10, "tx refused but queue active"))
+                               goto drop;
                        goto retry;
                }
-               store->skb = tx.skb;
        }
  out:
        rcu_read_unlock();
-       return 0;
+       return;
 
  drop:
        rcu_read_unlock();
 
        skb = tx.skb;
        while (skb) {
-               struct sk_buff *next;
-
                next = skb->next;
                dev_kfree_skb(skb);
                skb = next;
        }
-       return 0;
 }
 
 /* device xmit handlers */
@@ -1328,7 +1375,6 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                FOUND_SDATA,
                UNKNOWN_ADDRESS,
        } monitor_iface = NOT_MONITOR;
-       int ret;
 
        if (skb->iif)
                odev = dev_get_by_index(&init_net, skb->iif);
@@ -1342,7 +1388,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                       "originating device\n", dev->name);
 #endif
                dev_kfree_skb(skb);
-               return 0;
+               return NETDEV_TX_OK;
        }
 
        if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
@@ -1371,7 +1417,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                else
                        if (mesh_nexthop_lookup(skb, osdata)) {
                                dev_put(odev);
-                               return 0;
+                               return NETDEV_TX_OK;
                        }
                if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0)
                        IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
@@ -1433,7 +1479,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) {
                dev_kfree_skb(skb);
                dev_put(odev);
-               return 0;
+               return NETDEV_TX_OK;
        }
 
        if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1442,10 +1488,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                      u.ap);
        if (likely(monitor_iface != UNKNOWN_ADDRESS))
                info->control.vif = &osdata->vif;
-       ret = ieee80211_tx(odev, skb);
+
+       ieee80211_tx(odev, skb, false);
        dev_put(odev);
 
-       return ret;
+       return NETDEV_TX_OK;
 }
 
 int ieee80211_monitor_start_xmit(struct sk_buff *skb,
@@ -1803,23 +1850,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
  */
 void ieee80211_clear_tx_pending(struct ieee80211_local *local)
 {
-       struct sk_buff *skb;
        int i;
 
-       for (i = 0; i < local->hw.queues; i++) {
-               if (!test_bit(i, local->queues_pending))
-                       continue;
+       for (i = 0; i < local->hw.queues; i++)
+               skb_queue_purge(&local->pending[i]);
+}
 
-               skb = local->pending_packet[i].skb;
-               while (skb) {
-                       struct sk_buff *next;
+static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
+                                    struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sub_if_data *sdata;
+       struct sta_info *sta;
+       struct ieee80211_hdr *hdr;
+       struct net_device *dev;
+       int ret;
+       bool result = true;
 
-                       next = skb->next;
-                       dev_kfree_skb(skb);
-                       skb = next;
-               }
-               clear_bit(i, local->queues_pending);
+       /* does interface still exist? */
+       dev = dev_get_by_index(&init_net, skb->iif);
+       if (!dev) {
+               dev_kfree_skb(skb);
+               return true;
+       }
+
+       /* validate info->control.vif against skb->iif */
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+               sdata = container_of(sdata->bss,
+                                    struct ieee80211_sub_if_data,
+                                    u.ap);
+
+       if (unlikely(info->control.vif && info->control.vif != &sdata->vif)) {
+               dev_kfree_skb(skb);
+               result = true;
+               goto out;
+       }
+
+       if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
+               ieee80211_tx(dev, skb, true);
+       } else {
+               hdr = (struct ieee80211_hdr *)skb->data;
+               sta = sta_info_get(local, hdr->addr1);
+
+               ret = __ieee80211_tx(local, &skb, sta);
+               if (ret != IEEE80211_TX_OK)
+                       result = false;
        }
+
+ out:
+       dev_put(dev);
+
+       return result;
 }
 
 /*
@@ -1830,40 +1912,51 @@ void ieee80211_tx_pending(unsigned long data)
 {
        struct ieee80211_local *local = (struct ieee80211_local *)data;
        struct net_device *dev = local->mdev;
-       struct ieee80211_tx_stored_packet *store;
-       struct ieee80211_hdr *hdr;
-       struct ieee80211_tx_data tx;
-       int i, ret;
+       unsigned long flags;
+       int i;
+       bool next;
 
        rcu_read_lock();
        netif_tx_lock_bh(dev);
-       for (i = 0; i < local->hw.queues; i++) {
-               /* Check that this queue is ok */
-               if (__netif_subqueue_stopped(local->mdev, i) &&
-                   !test_bit(i, local->queues_pending_run))
-                       continue;
 
-               if (!test_bit(i, local->queues_pending)) {
-                       clear_bit(i, local->queues_pending_run);
-                       ieee80211_wake_queue(&local->hw, i);
+       for (i = 0; i < local->hw.queues; i++) {
+               /*
+                * If queue is stopped by something other than due to pending
+                * frames, or we have no pending frames, proceed to next queue.
+                */
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               next = false;
+               if (local->queue_stop_reasons[i] !=
+                       BIT(IEEE80211_QUEUE_STOP_REASON_PENDING) ||
+                   skb_queue_empty(&local->pending[i]))
+                       next = true;
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+               if (next)
                        continue;
-               }
 
-               clear_bit(i, local->queues_pending_run);
+               /*
+                * start the queue now to allow processing our packets,
+                * we're under the tx lock here anyway so nothing will
+                * happen as a result of this
+                */
                netif_start_subqueue(local->mdev, i);
 
-               store = &local->pending_packet[i];
-               tx.flags = 0;
-               tx.skb = store->skb;
-               hdr = (struct ieee80211_hdr *)tx.skb->data;
-               tx.sta = sta_info_get(local, hdr->addr1);
-               ret = __ieee80211_tx(local, &tx);
-               store->skb = tx.skb;
-               if (!ret) {
-                       clear_bit(i, local->queues_pending);
-                       ieee80211_wake_queue(&local->hw, i);
+               while (!skb_queue_empty(&local->pending[i])) {
+                       struct sk_buff *skb = skb_dequeue(&local->pending[i]);
+
+                       if (!ieee80211_tx_pending_skb(local, skb)) {
+                               skb_queue_head(&local->pending[i], skb);
+                               break;
+                       }
                }
+
+               /* Start regular packet processing again. */
+               if (skb_queue_empty(&local->pending[i]))
+                       ieee80211_wake_queue_by_reason(&local->hw, i,
+                                       IEEE80211_QUEUE_STOP_REASON_PENDING);
        }
+
        netif_tx_unlock_bh(dev);
        rcu_read_unlock();
 }