]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
rtw88: add driver TX queue support
authorYan-Hsuan Chuang <yhchuang@realtek.com>
Wed, 2 Oct 2019 06:35:20 +0000 (14:35 +0800)
committerSeth Forshee <seth.forshee@canonical.com>
Fri, 20 Mar 2020 21:31:24 +0000 (16:31 -0500)
BugLink: https://bugs.launchpad.net/bugs/1780590
The mac80211 provides software TX queue for driver, as long as
driver has hooked ieee80211_ops::wake_tx_queue. Each time a
packet is queued onto the TX queue, that queue will be woken
up the inform driver to serve the queue.

Now driver only supports PCI interface ICs, there's no specific
traffic control for each queue, just schedule a tasklet, and
dump all of the packets at once to the DMA ring. Instead of TX
the packets whenever TX queue is woke, tasklet handler can have
more packets dumped to the device, takes advantage of burst
write with DMA engine.

And if the driver is going to support USB/SDIO ICs, the tasklet
can be more flexible for aggregating the packets, enhance the
efficiency of bandwidth usage.

Signed-off-by: Yan-Hsuan Chuang <yhchuang@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
(cherry picked from commit 3745d3e550d1e6c4301596ac05a5fe82c11301ce)
Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
drivers/net/wireless/realtek/rtw88/mac80211.c
drivers/net/wireless/realtek/rtw88/main.c
drivers/net/wireless/realtek/rtw88/main.h
drivers/net/wireless/realtek/rtw88/tx.c
drivers/net/wireless/realtek/rtw88/tx.h

index 9e8ba52abbc5fd0afd2b4e2e8fcc795cc04c05b4..3b8f95ecf8c6345649655f86b1b3fba327f2d625 100644 (file)
@@ -17,19 +17,30 @@ static void rtw_ops_tx(struct ieee80211_hw *hw,
                       struct sk_buff *skb)
 {
        struct rtw_dev *rtwdev = hw->priv;
-       struct rtw_tx_pkt_info pkt_info = {0};
 
-       if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags))
-               goto out;
+       if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) {
+               ieee80211_free_txskb(hw, skb);
+               return;
+       }
 
-       rtw_tx_pkt_info_update(rtwdev, &pkt_info, control, skb);
-       if (rtw_hci_tx(rtwdev, &pkt_info, skb))
-               goto out;
+       rtw_tx(rtwdev, control, skb);
+}
 
-       return;
+static void rtw_ops_wake_tx_queue(struct ieee80211_hw *hw,
+                                 struct ieee80211_txq *txq)
+{
+       struct rtw_dev *rtwdev = hw->priv;
+       struct rtw_txq *rtwtxq = (struct rtw_txq *)txq->drv_priv;
 
-out:
-       ieee80211_free_txskb(hw, skb);
+       if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags))
+               return;
+
+       spin_lock_bh(&rtwdev->txq_lock);
+       if (list_empty(&rtwtxq->list))
+               list_add_tail(&rtwtxq->list, &rtwdev->txqs);
+       spin_unlock_bh(&rtwdev->txq_lock);
+
+       tasklet_schedule(&rtwdev->tx_tasklet);
 }
 
 static int rtw_ops_start(struct ieee80211_hw *hw)
@@ -147,6 +158,7 @@ static int rtw_ops_add_interface(struct ieee80211_hw *hw,
        rtwvif->stats.rx_cnt = 0;
        rtwvif->in_lps = false;
        rtwvif->conf = &rtw_vif_port[port];
+       rtw_txq_init(rtwdev, vif->txq);
 
        mutex_lock(&rtwdev->mutex);
 
@@ -196,6 +208,8 @@ static void rtw_ops_remove_interface(struct ieee80211_hw *hw,
 
        rtw_leave_lps_deep(rtwdev);
 
+       rtw_txq_cleanup(rtwdev, vif->txq);
+
        eth_zero_addr(rtwvif->mac_addr);
        config |= PORT_SET_MAC_ADDR;
        rtwvif->net_type = RTW_NET_NO_LINK;
@@ -333,6 +347,7 @@ static int rtw_ops_sta_add(struct ieee80211_hw *hw,
 {
        struct rtw_dev *rtwdev = hw->priv;
        struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+       int i;
        int ret = 0;
 
        mutex_lock(&rtwdev->mutex);
@@ -347,6 +362,8 @@ static int rtw_ops_sta_add(struct ieee80211_hw *hw,
        si->vif = vif;
        si->init_ra_lv = 1;
        ewma_rssi_init(&si->avg_rssi);
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+               rtw_txq_init(rtwdev, sta->txq[i]);
 
        rtw_update_sta_info(rtwdev, si);
        rtw_fw_media_status_report(rtwdev, si->mac_id, true);
@@ -367,12 +384,16 @@ static int rtw_ops_sta_remove(struct ieee80211_hw *hw,
 {
        struct rtw_dev *rtwdev = hw->priv;
        struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+       int i;
 
        mutex_lock(&rtwdev->mutex);
 
        rtw_release_macid(rtwdev, si->mac_id);
        rtw_fw_media_status_report(rtwdev, si->mac_id, false);
 
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+               rtw_txq_cleanup(rtwdev, sta->txq[i]);
+
        rtwdev->sta_cnt--;
 
        rtw_info(rtwdev, "sta %pM with macid %d left\n",
@@ -553,6 +574,7 @@ static int rtw_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 
 const struct ieee80211_ops rtw_ops = {
        .tx                     = rtw_ops_tx,
+       .wake_tx_queue          = rtw_ops_wake_tx_queue,
        .start                  = rtw_ops_start,
        .stop                   = rtw_ops_stop,
        .config                 = rtw_ops_config,
index 989050bbd4f7dc085269aa2a4a089f043d40d5b0..c84b0fbf51e6454f70187328341c150ce809f515 100644 (file)
@@ -12,6 +12,7 @@
 #include "phy.h"
 #include "reg.h"
 #include "efuse.h"
+#include "tx.h"
 #include "debug.h"
 
 unsigned int rtw_fw_lps_deep_mode;
@@ -1158,9 +1159,12 @@ int rtw_core_init(struct rtw_dev *rtwdev)
        int ret;
 
        INIT_LIST_HEAD(&rtwdev->rsvd_page_list);
+       INIT_LIST_HEAD(&rtwdev->txqs);
 
        timer_setup(&rtwdev->tx_report.purge_timer,
                    rtw_tx_report_purge_timer, 0);
+       tasklet_init(&rtwdev->tx_tasklet, rtw_tx_tasklet,
+                    (unsigned long)rtwdev);
 
        INIT_DELAYED_WORK(&rtwdev->watch_dog_work, rtw_watch_dog_work);
        INIT_DELAYED_WORK(&coex->bt_relink_work, rtw_coex_bt_relink_work);
@@ -1174,6 +1178,7 @@ int rtw_core_init(struct rtw_dev *rtwdev)
        spin_lock_init(&rtwdev->dm_lock);
        spin_lock_init(&rtwdev->rf_lock);
        spin_lock_init(&rtwdev->h2c.lock);
+       spin_lock_init(&rtwdev->txq_lock);
        spin_lock_init(&rtwdev->tx_report.q_lock);
 
        mutex_init(&rtwdev->mutex);
@@ -1218,6 +1223,7 @@ void rtw_core_deinit(struct rtw_dev *rtwdev)
        if (fw->firmware)
                release_firmware(fw->firmware);
 
+       tasklet_kill(&rtwdev->tx_tasklet);
        spin_lock_irqsave(&rtwdev->tx_report.q_lock, flags);
        skb_queue_purge(&rtwdev->tx_report.queue);
        spin_unlock_irqrestore(&rtwdev->tx_report.q_lock, flags);
@@ -1243,6 +1249,7 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
 
        hw->extra_tx_headroom = max_tx_headroom;
        hw->queues = IEEE80211_NUM_ACS;
+       hw->txq_data_size = sizeof(struct rtw_txq);
        hw->sta_data_size = sizeof(struct rtw_sta_info);
        hw->vif_data_size = sizeof(struct rtw_vif);
 
index 810bf151ad36c4e92122d67f61726a1915ae50eb..cfe94a685dde5a47e21487abd68186b5c4f22c4c 100644 (file)
@@ -587,6 +587,11 @@ struct rtw_tx_report {
        struct timer_list purge_timer;
 };
 
+struct rtw_txq {
+       struct list_head list;
+       unsigned long last_push;
+};
+
 #define RTW_BC_MC_MACID 1
 DECLARE_EWMA(rssi, 10, 16);
 
@@ -1361,6 +1366,11 @@ struct rtw_dev {
        struct sk_buff_head c2h_queue;
        struct work_struct c2h_work;
 
+       /* used to protect txqs list */
+       spinlock_t txq_lock;
+       struct list_head txqs;
+       struct tasklet_struct tx_tasklet;
+
        struct rtw_tx_report tx_report;
 
        struct {
@@ -1396,6 +1406,13 @@ static inline bool rtw_is_assoc(struct rtw_dev *rtwdev)
        return !!rtwdev->sta_cnt;
 }
 
+static inline struct ieee80211_txq *rtwtxq_to_txq(struct rtw_txq *rtwtxq)
+{
+       void *p = rtwtxq;
+
+       return container_of(p, struct ieee80211_txq, drv_priv);
+}
+
 void rtw_get_channel_params(struct cfg80211_chan_def *chandef,
                            struct rtw_channel_params *ch_param);
 bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target);
index 25fa932d02083f4a99792e1bba24bee149ebd4f0..1e19bdac1d45a7a37b8092578c330ce1ace41a42 100644 (file)
@@ -367,3 +367,98 @@ void rtw_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev,
        pkt_info->qsel = TX_DESC_QSEL_MGMT;
        pkt_info->ls = true;
 }
+
+void rtw_tx(struct rtw_dev *rtwdev,
+           struct ieee80211_tx_control *control,
+           struct sk_buff *skb)
+{
+       struct rtw_tx_pkt_info pkt_info = {0};
+
+       rtw_tx_pkt_info_update(rtwdev, &pkt_info, control, skb);
+       if (rtw_hci_tx(rtwdev, &pkt_info, skb))
+               goto out;
+
+       return;
+
+out:
+       ieee80211_free_txskb(rtwdev->hw, skb);
+}
+
+static bool rtw_txq_dequeue(struct rtw_dev *rtwdev,
+                           struct rtw_txq *rtwtxq)
+{
+       struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq);
+       struct ieee80211_tx_control control;
+       struct sk_buff *skb;
+
+       skb = ieee80211_tx_dequeue(rtwdev->hw, txq);
+       if (!skb)
+               return false;
+
+       control.sta = txq->sta;
+       rtw_tx(rtwdev, &control, skb);
+       rtwtxq->last_push = jiffies;
+
+       return true;
+}
+
+static void rtw_txq_push(struct rtw_dev *rtwdev,
+                        struct rtw_txq *rtwtxq,
+                        unsigned long frames)
+{
+       int i;
+
+       rcu_read_lock();
+
+       for (i = 0; i < frames; i++)
+               if (!rtw_txq_dequeue(rtwdev, rtwtxq))
+                       break;
+
+       rcu_read_unlock();
+}
+
+void rtw_tx_tasklet(unsigned long data)
+{
+       struct rtw_dev *rtwdev = (void *)data;
+       struct rtw_txq *rtwtxq, *tmp;
+
+       spin_lock_bh(&rtwdev->txq_lock);
+
+       list_for_each_entry_safe(rtwtxq, tmp, &rtwdev->txqs, list) {
+               struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq);
+               unsigned long frame_cnt;
+               unsigned long byte_cnt;
+
+               ieee80211_txq_get_depth(txq, &frame_cnt, &byte_cnt);
+               rtw_txq_push(rtwdev, rtwtxq, frame_cnt);
+
+               list_del_init(&rtwtxq->list);
+       }
+
+       spin_unlock_bh(&rtwdev->txq_lock);
+}
+
+void rtw_txq_init(struct rtw_dev *rtwdev, struct ieee80211_txq *txq)
+{
+       struct rtw_txq *rtwtxq;
+
+       if (!txq)
+               return;
+
+       rtwtxq = (struct rtw_txq *)txq->drv_priv;
+       INIT_LIST_HEAD(&rtwtxq->list);
+}
+
+void rtw_txq_cleanup(struct rtw_dev *rtwdev, struct ieee80211_txq *txq)
+{
+       struct rtw_txq *rtwtxq;
+
+       if (!txq)
+               return;
+
+       rtwtxq = (struct rtw_txq *)txq->drv_priv;
+       spin_lock_bh(&rtwdev->txq_lock);
+       if (!list_empty(&rtwtxq->list))
+               list_del_init(&rtwtxq->list);
+       spin_unlock_bh(&rtwdev->txq_lock);
+}
index ab5b71f8ac2275b5be391c1be80ea24d9ddbe4b9..9ca4f74a501b6aadbf30a5b1eaa19ab76db634ed 100644 (file)
@@ -77,6 +77,12 @@ enum rtw_tx_desc_queue_select {
        TX_DESC_QSEL_H2C        = 19,
 };
 
+void rtw_tx(struct rtw_dev *rtwdev,
+           struct ieee80211_tx_control *control,
+           struct sk_buff *skb);
+void rtw_txq_init(struct rtw_dev *rtwdev, struct ieee80211_txq *txq);
+void rtw_txq_cleanup(struct rtw_dev *rtwdev, struct ieee80211_txq *txq);
+void rtw_tx_tasklet(unsigned long data);
 void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev,
                            struct rtw_tx_pkt_info *pkt_info,
                            struct ieee80211_tx_control *control,