wl12xx: implement Tx watermarks per AC
authorArik Nemtsov <arik@wizery.com>
Fri, 24 Jun 2011 10:03:37 +0000 (13:03 +0300)
committerLuciano Coelho <coelho@ti.com>
Tue, 5 Jul 2011 18:33:44 +0000 (21:33 +0300)
Each AC is stopped when its queue is filled up to the high watermark,
and restarted when its queue it lower than the low watermark. This
ensures congested ACs are not able to starve other ACs.

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/tx.c
drivers/net/wireless/wl12xx/tx.h
drivers/net/wireless/wl12xx/wl12xx.h

index 08eb81e698fa0f5e1f5ce486232cde4e81f77b8f..d7a74597fd590cc8d5402fb0ce0b5c314584759f 100644 (file)
@@ -1497,10 +1497,11 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct wl1271 *wl = hw->priv;
        unsigned long flags;
-       int q;
+       int q, mapping;
        u8 hlid = 0;
 
-       q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
+       mapping = skb_get_queue_mapping(skb);
+       q = wl1271_tx_get_queue(mapping);
 
        if (wl->bss_type == BSS_TYPE_AP_BSS)
                hlid = wl1271_tx_get_hlid(skb);
@@ -1513,10 +1514,10 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         * The workqueue is slow to process the tx_queue and we need stop
         * the queue here, otherwise the queue will get too long.
         */
-       if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
-               wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
-               ieee80211_stop_queues(wl->hw);
-               set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
+       if (skb_queue_len(&wl->tx_queue[q]) >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
+               wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
+               ieee80211_stop_queue(wl->hw, mapping);
+               set_bit(q, &wl->stopped_queues_map);
        }
 
        /* queue the packet */
index e6e5f54b8e490caae4a74b465777adc583cb266f..75984dc81a8d928367f6d3d6545ee1d131367f87 100644 (file)
@@ -444,14 +444,19 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
 void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
 {
        unsigned long flags;
+       int i;
 
-       if (test_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags) &&
-           wl->tx_queue_count <= WL1271_TX_QUEUE_LOW_WATERMARK) {
-               /* firmware buffer has space, restart queues */
-               spin_lock_irqsave(&wl->wl_lock, flags);
-               ieee80211_wake_queues(wl->hw);
-               clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
-               spin_unlock_irqrestore(&wl->wl_lock, flags);
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
+               if (test_bit(i, &wl->stopped_queues_map) &&
+                   skb_queue_len(&wl->tx_queue[i]) <=
+                                               WL1271_TX_QUEUE_LOW_WATERMARK) {
+                       /* firmware buffer has space, restart queues */
+                       spin_lock_irqsave(&wl->wl_lock, flags);
+                       ieee80211_wake_queue(wl->hw,
+                                            wl1271_tx_get_mac80211_queue(i));
+                       clear_bit(i, &wl->stopped_queues_map);
+                       spin_unlock_irqrestore(&wl->wl_lock, flags);
+               }
        }
 }
 
@@ -863,6 +868,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
        }
 
        wl->tx_queue_count = 0;
+       wl->stopped_queues_map = 0;
 
        /*
         * Make sure the driver is at a consistent state, in case this
index 7ed3c01f365149145b5f7a27f7ff43aea765bbb2..6b7bf3150ec4ac307a9860f1f2b00460d25f4920 100644 (file)
@@ -182,6 +182,22 @@ static inline int wl1271_tx_get_queue(int queue)
        }
 }
 
+static inline int wl1271_tx_get_mac80211_queue(int queue)
+{
+       switch (queue) {
+       case CONF_TX_AC_VO:
+               return 0;
+       case CONF_TX_AC_VI:
+               return 1;
+       case CONF_TX_AC_BE:
+               return 2;
+       case CONF_TX_AC_BK:
+               return 3;
+       default:
+               return 2;
+       }
+}
+
 void wl1271_tx_work(struct work_struct *work);
 void wl1271_tx_work_locked(struct wl1271 *wl);
 void wl1271_tx_complete(struct wl1271 *wl);
index 000dfcd22556bfee589ef40cf83db797279b7d32..94bfc0a25d579be29b7226dd220b55e588b1db9f 100644 (file)
@@ -439,6 +439,7 @@ struct wl1271 {
        /* Frames scheduled for transmission, not handled yet */
        struct sk_buff_head tx_queue[NUM_TX_QUEUES];
        int tx_queue_count;
+       long stopped_queues_map;
 
        /* Frames received, not handled yet by mac80211 */
        struct sk_buff_head deferred_rx_queue;