From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from mail-wy0-f174.google.com ([74.125.82.174]:54095 "EHLO mail-wy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753039Ab1HNKSI (ORCPT ); Sun, 14 Aug 2011 06:18:08 -0400 Received: by mail-wy0-f174.google.com with SMTP id 24so2967072wyg.19 for ; Sun, 14 Aug 2011 03:18:07 -0700 (PDT) From: Eliad Peller To: Luciano Coelho Cc: Subject: [PATCH v2 34/40] wl12xx: schedule TX packets according to FW packet occupancy Date: Sun, 14 Aug 2011 13:17:33 +0300 Message-Id: <1313317059-16567-35-git-send-email-eliad@wizery.com> (sfid-20110814_121813_635529_74AD79AD) In-Reply-To: <1313317059-16567-1-git-send-email-eliad@wizery.com> References: <1313317059-16567-1-git-send-email-eliad@wizery.com> Sender: linux-wireless-owner@vger.kernel.org List-ID: From: Arik Nemtsov When selecting packets for transmission, prefer the ACs that are least occupied in the FW. When packets for multiple ACs are present in the FW, it decides which to transmit according to WMM QoS parameters. With these changes, lower priority ACs should not be starved when higher priority traffic is present. Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller --- v2: consider the priority of the queues drivers/net/wireless/wl12xx/debugfs.c | 4 ++ drivers/net/wireless/wl12xx/main.c | 7 ++- drivers/net/wireless/wl12xx/tx.c | 71 ++++++++++++++++++++++----------- drivers/net/wireless/wl12xx/wl12xx.h | 2 +- 4 files changed, 57 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 3102652..d59354f 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -337,12 +337,16 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, #define DRIVER_STATE_PRINT_STR(x) DRIVER_STATE_PRINT(x, "%s") #define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx") #define DRIVER_STATE_PRINT_HEX(x) DRIVER_STATE_PRINT(x, "0x%x") DRIVER_STATE_PRINT_INT(tx_blocks_available); DRIVER_STATE_PRINT_INT(tx_allocated_blocks); + DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]); + DRIVER_STATE_PRINT_INT(tx_allocated_pkts[1]); + DRIVER_STATE_PRINT_INT(tx_allocated_pkts[2]); + DRIVER_STATE_PRINT_INT(tx_allocated_pkts[3]); DRIVER_STATE_PRINT_INT(tx_frames_cnt); DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]); DRIVER_STATE_PRINT_INT(tx_queue_count[0]); DRIVER_STATE_PRINT_INT(tx_queue_count[1]); DRIVER_STATE_PRINT_INT(tx_queue_count[2]); DRIVER_STATE_PRINT_INT(tx_queue_count[3]); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 479977d..869b8c3 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -824,13 +824,13 @@ static void wl12xx_fw_status(struct wl1271 *wl, status->fw_rx_counter, status->drv_rx_counter, status->tx_results_counter); for (i = 0; i < NUM_TX_QUEUES; i++) { /* prevent wrap-around in freed-packets counter */ - wl->tx_allocated_pkts -= + wl->tx_allocated_pkts[i] -= (status->tx_released_pkts[i] - wl->tx_pkts_freed[i]) & 0xff; wl->tx_pkts_freed[i] = status->tx_released_pkts[i]; } @@ -2057,15 +2057,16 @@ deinit: * get executed before all these vars have been reset. */ wl->flags = 0; wl->tx_blocks_freed = 0; - wl->tx_allocated_pkts = 0; - for (i = 0; i < NUM_TX_QUEUES; i++) + for (i = 0; i < NUM_TX_QUEUES; i++) { wl->tx_pkts_freed[i] = 0; + wl->tx_allocated_pkts[i] = 0; + } wl1271_debugfs_reset(wl); kfree(wl->fw_status); wl->fw_status = NULL; kfree(wl->tx_res_if); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 7dd6d8b..ccbcd0a 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -202,13 +202,13 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, u32 buf_offset, u8 hlid) { struct wl1271_tx_hw_descr *desc; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; u32 len; u32 total_blocks; - int id, ret = -EBUSY; + int id, ret = -EBUSY, ac; /* we use 1 spare block */ u32 spare_blocks = 1; if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE) return -EAGAIN; @@ -239,13 +239,14 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, desc->id = id; wl->tx_blocks_available -= total_blocks; wl->tx_allocated_blocks += total_blocks; - wl->tx_allocated_pkts++; + ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); + wl->tx_allocated_pkts[ac]++; if (wl->bss_type == BSS_TYPE_AP_BSS) wl->links[hlid].allocated_blks += total_blocks; ret = 0; @@ -482,27 +483,51 @@ void wl1271_handle_tx_low_watermark(struct wl1271 *wl) clear_bit(i, &wl->stopped_queues_map); spin_unlock_irqrestore(&wl->wl_lock, flags); } } } +static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl, + struct sk_buff_head *queues) +{ + int i, q = -1, ac; + u32 min_pkts = 0xffffffff; + + /* + * Find a non-empty ac where: + * 1. There are packets to transmit + * 2. The FW has the least allocated blocks + * + * We prioritize the ACs according to VO>VI>BE>BK + */ + for (i = 0; i < NUM_TX_QUEUES; i++) { + ac = wl1271_tx_get_queue(i); + if (!skb_queue_empty(&queues[ac]) && + (wl->tx_allocated_pkts[ac] < min_pkts)) { + q = ac; + min_pkts = wl->tx_allocated_pkts[q]; + } + } + + if (q == -1) + return NULL; + + return &queues[q]; +} + static struct sk_buff *wl1271_sta_skb_dequeue(struct wl1271 *wl) { struct sk_buff *skb = NULL; unsigned long flags; + struct sk_buff_head *queue; - skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_VO]); - if (skb) + queue = wl1271_select_queue(wl, wl->tx_queue); + if (!queue) goto out; - skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_VI]); - if (skb) - goto out; - skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_BE]); - if (skb) - goto out; - skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_BK]); + + skb = skb_dequeue(queue); out: if (skb) { int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count[q]--; @@ -514,35 +539,35 @@ out: static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl) { struct sk_buff *skb = NULL; unsigned long flags; int i, h, start_hlid; + struct sk_buff_head *queue; /* start from the link after the last one */ start_hlid = (wl->last_tx_hlid + 1) % AP_MAX_LINKS; /* dequeue according to AC, round robin on each link */ for (i = 0; i < AP_MAX_LINKS; i++) { h = (start_hlid + i) % AP_MAX_LINKS; - skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VO]); - if (skb) - goto out; - skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VI]); - if (skb) - goto out; - skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BE]); - if (skb) - goto out; - skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BK]); + /* only consider connected stations */ + if (h >= WL1271_AP_STA_HLID_START && + !test_bit(h - WL1271_AP_STA_HLID_START, wl->ap_hlid_map)) + continue; + + queue = wl1271_select_queue(wl, wl->links[h].tx_queue); + if (!queue) + continue; + + skb = skb_dequeue(queue); if (skb) - goto out; + break; } -out: if (skb) { int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); wl->last_tx_hlid = h; spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count[q]--; spin_unlock_irqrestore(&wl->wl_lock, flags); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 24b4025..6118df5 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -421,13 +421,13 @@ struct wl1271 { u32 tx_blocks_available; u32 tx_allocated_blocks; u32 tx_results_count; /* Accounting for allocated / available Tx packets in HW */ u32 tx_pkts_freed[NUM_TX_QUEUES]; - u32 tx_allocated_pkts; + u32 tx_allocated_pkts[NUM_TX_QUEUES]; /* Transmitted TX packets counter for chipset interface */ u32 tx_packets_count; /* Time-offset between host and chipset clocks */ s64 time_offset; -- 1.7.6.401.g6a319