From 1486ffbafdb4dd433203c35c694b7443fa769210 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 5 May 2016 13:41:18 +0200 Subject: [PATCH] ath10k: introduce per-sta DQL and tx scheduling Work in progress. Signed-off-by: Michal Kazior --- drivers/net/wireless/ath/ath10k/core.c | 3 +- drivers/net/wireless/ath/ath10k/core.h | 9 ++- drivers/net/wireless/ath/ath10k/htt_rx.c | 21 ++++++- drivers/net/wireless/ath/ath10k/htt_tx.c | 9 ++- drivers/net/wireless/ath/ath10k/mac.c | 97 +++++++++++++++++++++++--------- drivers/net/wireless/ath/ath10k/txrx.c | 9 ++- drivers/net/wireless/ath/ath10k/txrx.h | 3 +- 7 files changed, 116 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index e94cb87380d2..31d34852586b 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2100,7 +2100,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, spin_lock_init(&ar->data_lock); spin_lock_init(&ar->txqs_lock); - INIT_LIST_HEAD(&ar->txqs); + INIT_LIST_HEAD(&ar->txqs_new); + INIT_LIST_HEAD(&ar->txqs_old); INIT_LIST_HEAD(&ar->peers); init_waitqueue_head(&ar->peer_mapping_wq); init_waitqueue_head(&ar->htt.empty_tx_wq); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 1379054000f9..683d1d050106 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -310,8 +310,13 @@ struct ath10k_peer { struct ath10k_txq { struct list_head list; + struct list_head completed_list; + struct dql dql; + ktime_t tx_start; unsigned long num_fw_queued; unsigned long num_push_allowed; + unsigned int completed; /* protected by: htt->tx_lock */ + s64 deficit; }; struct ath10k_sta { @@ -830,7 +835,9 @@ struct ath10k { /* protects: ar->txqs, artxq->list */ spinlock_t txqs_lock; - struct list_head txqs; + struct ath10k_txq *txq; + struct list_head txqs_new; + struct list_head txqs_old; struct list_head arvifs; struct list_head peers; struct ath10k_peer *peer_map[ATH10K_MAX_NUM_PEER_IDS]; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 7411b69fde42..806bdc6d3fdf 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1635,7 +1635,7 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar, if (!kfifo_put(&htt->txdone_fifo, tx_done)) { ath10k_warn(ar, "txdone fifo overrun, msdu_id %d status %d\n", tx_done.msdu_id, tx_done.status); - ath10k_txrx_tx_unref(htt, &tx_done); + ath10k_txrx_tx_unref(htt, &tx_done, NULL); } } } @@ -2285,7 +2285,7 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } - status = ath10k_txrx_tx_unref(htt, &tx_done); + status = ath10k_txrx_tx_unref(htt, &tx_done, NULL); if (!status) { spin_lock_bh(&htt->tx_lock); ath10k_htt_tx_mgmt_dec_pending(htt); @@ -2410,10 +2410,12 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr) { struct ath10k_htt *htt = (struct ath10k_htt *)ptr; struct ath10k *ar = htt->ar; + struct ath10k_txq *artxq; struct htt_tx_done tx_done = {}; struct sk_buff_head rx_ind_q; struct sk_buff_head tx_ind_q; struct sk_buff *skb; + struct list_head completed_list; unsigned long flags; int num_mpdus; @@ -2428,13 +2430,26 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr) skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q); spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags); + INIT_LIST_HEAD(&completed_list); + /* kfifo_get: called only within txrx_tasklet so it's neatly serialized. * From kfifo_get() documentation: * Note that with only one concurrent reader and one concurrent writer, * you don't need extra locking to use these macro. */ while (kfifo_get(&htt->txdone_fifo, &tx_done)) - ath10k_txrx_tx_unref(htt, &tx_done); + ath10k_txrx_tx_unref(htt, &tx_done, &completed_list); + + while (!list_empty(&completed_list)) { + artxq = list_first_entry(&completed_list, struct ath10k_txq, + completed_list); + list_del_init(&artxq->completed_list); + + spin_lock_bh(&htt->tx_lock); + dql_completed(&artxq->dql, artxq->completed); + artxq->completed = 0; + spin_unlock_bh(&htt->tx_lock); + } while ((skb = __skb_dequeue(&tx_ind_q))) { ath10k_htt_rx_tx_fetch_ind(ar, skb); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 6269c610b0a3..668f03e7d914 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -379,7 +379,7 @@ static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx) tx_done.msdu_id = msdu_id; tx_done.status = HTT_TX_COMPL_STATE_DISCARD; - ath10k_txrx_tx_unref(htt, &tx_done); + ath10k_txrx_tx_unref(htt, &tx_done, NULL); return 0; } @@ -841,12 +841,14 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + struct ath10k_txq *artxq = (void *)skb_cb->txq->drv_priv; struct ath10k_hif_sg_item sg_items[2]; struct ath10k_htt_txbuf *txbuf; struct htt_data_tx_desc_frag *frags; bool is_eth = (txmode == ATH10K_HW_TXRX_ETHERNET); u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu); u8 tid = ath10k_htt_tx_get_tid(msdu, is_eth); + size_t msdu_len; int prefetch_len; int res; u8 flags0 = 0; @@ -1015,12 +1017,17 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, sg_items[1].paddr = skb_cb->paddr; sg_items[1].len = prefetch_len; + msdu_len = msdu->len; + res = ath10k_hif_tx_sg(htt->ar, htt->ar->htc.endpoint[htt->eid].ul_pipe_id, sg_items, ARRAY_SIZE(sg_items)); if (res) goto err_unmap_msdu; + if (skb_cb->txq) + dql_queued(&artxq->dql, msdu_len); + return 0; err_unmap_msdu: diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 1bae547a9616..708c63e3fd66 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3652,6 +3652,8 @@ static void ath10k_mac_txq_init(struct ieee80211_txq *txq) return; INIT_LIST_HEAD(&artxq->list); + INIT_LIST_HEAD(&artxq->completed_list); + dql_init(&artxq->dql, HZ); } static void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq) @@ -3776,40 +3778,81 @@ void ath10k_mac_tx_push_pending(struct ath10k *ar) { struct ieee80211_hw *hw = ar->hw; struct ieee80211_txq *txq; + struct list_head *list; struct ath10k_txq *artxq; - struct ath10k_txq *last; + ktime_t now; + u64 sent; + s64 delta; int ret; - int max; - - if (ar->htt.num_pending_tx >= (ar->htt.max_num_pending_tx / 2)) - return; spin_lock_bh(&ar->txqs_lock); rcu_read_lock(); - last = list_last_entry(&ar->txqs, struct ath10k_txq, list); - while (!list_empty(&ar->txqs)) { - artxq = list_first_entry(&ar->txqs, struct ath10k_txq, list); - txq = container_of((void *)artxq, struct ieee80211_txq, - drv_priv); - - /* Prevent aggressive sta/tid taking over tx queue */ - max = 16; - ret = 0; - while (ath10k_mac_tx_can_push(hw, txq) && max--) { - ret = ath10k_mac_tx_push_txq(hw, txq); - if (ret < 0) + for (;;) { + list = &ar->txqs_new; + if (list_empty(list)) { + list = &ar->txqs_old; + if (list_empty(list)) break; } - list_del_init(&artxq->list); - if (ret != -ENOENT) - list_add_tail(&artxq->list, &ar->txqs); - - ath10k_htt_tx_txq_update(hw, txq); - - if (artxq == last || (ret < 0 && ret != -ENOENT)) + artxq = list_first_entry_or_null(list, struct ath10k_txq, list); + if (!artxq) break; + + if (artxq->deficit <= 0) { + artxq->deficit += 300; + list_move_tail(&artxq->list, &ar->txqs_old); + continue; + } + + if (ar->txq) + artxq = ar->txq; + + txq = container_of((void *)artxq, struct ieee80211_txq, drv_priv); + ret = 0; + sent = 0; + + if (ar->txq != artxq) { + artxq->tx_start = ktime_get_raw(); + + for (;;) { + if (dql_avail(&artxq->dql) < 0) + break; + + if (!ath10k_mac_tx_can_push(hw, txq)) + break; + + ret = ath10k_mac_tx_push_txq(hw, txq); + if (ret < 0) + break; + + sent += ret; + } + + ath10k_htt_tx_txq_update(hw, txq); + + if (ret == -ENOENT && !sent) { + list_del_init(&artxq->list); + continue; + } + + ar->txq = artxq; + } + + if (artxq->num_fw_queued == 0) { + now = ktime_get_raw(); + delta = ktime_us_delta(now, artxq->tx_start); + artxq->tx_start = now; + artxq->deficit -= delta; + ar->txq = NULL; + printk("txq %pM delta %llu usec\n", + txq->sta ? txq->sta->addr : txq->vif->addr, + delta); + } + + list_move_tail(&artxq->list, &ar->txqs_old); + break; } rcu_read_unlock(); @@ -4050,8 +4093,10 @@ static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw, struct ath10k_txq *artxq = (void *)txq->drv_priv; spin_lock_bh(&ar->txqs_lock); - if (list_empty(&artxq->list)) - list_add_tail(&artxq->list, &ar->txqs); + if (list_empty(&artxq->list)) { + list_add_tail(&artxq->list, &ar->txqs_new); + artxq->deficit = 300; + } spin_unlock_bh(&ar->txqs_lock); ath10k_mac_tx_push_pending(ar); diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 1966c787998b..746644b2483f 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -50,7 +50,8 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) } int ath10k_txrx_tx_unref(struct ath10k_htt *htt, - const struct htt_tx_done *tx_done) + const struct htt_tx_done *tx_done, + struct list_head *completed_list) { struct ath10k *ar = htt->ar; struct device *dev = ar->dev; @@ -83,8 +84,12 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt, txq = skb_cb->txq; artxq = (void *)txq->drv_priv; - if (txq) + if (txq) { artxq->num_fw_queued--; + artxq->completed += msdu->len; + if (completed_list && list_empty(&artxq->completed_list)) + list_add_tail(&artxq->completed_list, completed_list); + } ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id); ath10k_htt_tx_dec_pending(htt); diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h index e7ea1ae1c438..0fe5e6fcf7e5 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.h +++ b/drivers/net/wireless/ath/ath10k/txrx.h @@ -20,7 +20,8 @@ #include "htt.h" int ath10k_txrx_tx_unref(struct ath10k_htt *htt, - const struct htt_tx_done *tx_done); + const struct htt_tx_done *tx_done, + struct list_head *completed_list); struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, const u8 *addr); -- 2.1.4