[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250920132614.277719-6-pchelkin@ispras.ru>
Date: Sat, 20 Sep 2025 16:26:10 +0300
From: Fedor Pchelkin <pchelkin@...ras.ru>
To: Ping-Ke Shih <pkshih@...ltek.com>,
Bitterblue Smith <rtl8821cerfe2@...il.com>
Cc: Fedor Pchelkin <pchelkin@...ras.ru>,
Zong-Zhe Yang <kevin_yang@...ltek.com>,
Po-Hao Huang <phhuang@...ltek.com>,
linux-wireless@...r.kernel.org,
linux-kernel@...r.kernel.org,
lvc-project@...uxtesting.org
Subject: [PATCH rtw-next 5/6] wifi: rtw89: process TX wait skbs for USB via C2H handler
TX wait skbs need to be completed when they are done. PCIe part does this
inside rtw89_pci_tx_status() during RPP processing. Other HCIs use a
mechanism based on C2H firmware messages.
Store a sequence number in a TX wait object so that it'll be possible to
identify completed items inside C2H handler. No need to add the
corresponding skb to the &txcb->tx_ack_queue on USB part.
Found by Linux Verification Center (linuxtesting.org).
Signed-off-by: Fedor Pchelkin <pchelkin@...ras.ru>
---
drivers/net/wireless/realtek/rtw89/core.c | 6 ++++--
drivers/net/wireless/realtek/rtw89/core.h | 18 +++++++++++++++++-
drivers/net/wireless/realtek/rtw89/mac.c | 11 +++++++++++
drivers/net/wireless/realtek/rtw89/usb.c | 9 ++++++++-
4 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 3e7bd0cedbdf..e76f04736502 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -1161,18 +1161,19 @@ int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *sk
lockdep_assert_wiphy(rtwdev->hw->wiphy);
+ list_add_tail_rcu(&wait->list, &rtwdev->tx_waits);
rtw89_core_tx_kick_off(rtwdev, qsel);
time_left = wait_for_completion_timeout(&wait->completion,
msecs_to_jiffies(timeout));
if (time_left == 0) {
ret = -ETIMEDOUT;
- list_add_tail(&wait->list, &rtwdev->tx_waits);
wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->tx_wait_work,
RTW89_TX_WAIT_WORK_TIMEOUT);
} else {
if (!wait->tx_done)
ret = -EAGAIN;
+ list_del_rcu(&wait->list);
rtw89_tx_wait_release(wait);
}
@@ -1237,11 +1238,12 @@ static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev,
tx_req.skb = skb;
tx_req.vif = vif;
tx_req.sta = sta;
+ tx_req.wait = wait;
tx_req.rtwvif_link = rtwvif_link;
tx_req.rtwsta_link = rtwsta_link;
tx_req.desc_info.sw_mld = sw_mld;
- if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ if (wait || (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
rtw89_hci_tx_rpt_enable(rtwdev, &tx_req);
rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, true, true);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 4e597a5df005..e7948bd0bdf6 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1199,6 +1199,7 @@ struct rtw89_core_tx_request {
struct sk_buff *skb;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
+ struct rtw89_tx_wait_info *wait;
struct rtw89_vif_link *rtwvif_link;
struct rtw89_sta_link *rtwsta_link;
struct rtw89_tx_desc_info desc_info;
@@ -3521,6 +3522,7 @@ struct rtw89_tx_wait_info {
struct completion completion;
struct sk_buff *skb;
bool tx_done;
+ u8 sn;
};
struct rtw89_tx_skb_data {
@@ -6289,7 +6291,7 @@ static inline void rtw89_tx_wait_list_clear(struct rtw89_dev *rtwdev)
list_for_each_entry_safe(wait, tmp, &rtwdev->tx_waits, list) {
if (!completion_done(&wait->completion))
continue;
- list_del(&wait->list);
+ list_del_rcu(&wait->list);
rtw89_tx_wait_release(wait);
}
}
@@ -7392,6 +7394,20 @@ static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,
return dev_alloc_skb(length);
}
+static inline bool rtw89_core_is_tx_wait(struct rtw89_dev *rtwdev,
+ struct rtw89_tx_skb_data *skb_data)
+{
+ struct rtw89_tx_wait_info *wait;
+
+ guard(rcu)();
+
+ wait = rcu_dereference(skb_data->wait);
+ if (!wait)
+ return false;
+
+ return true;
+}
+
static inline bool rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev,
struct rtw89_tx_skb_data *skb_data,
bool tx_done)
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 831e53aedccc..79409eb4d028 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -5477,6 +5477,7 @@ rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
{
u8 sw_define = RTW89_GET_MAC_C2H_TX_RPT_SW_DEFINE(c2h->data);
u8 tx_status = RTW89_GET_MAC_C2H_TX_RPT_TX_STATE(c2h->data);
+ struct rtw89_tx_wait_info *wait;
struct sk_buff *cur, *tmp;
unsigned long flags;
u8 *n;
@@ -5485,6 +5486,16 @@ rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
"C2H TX RPT: sn %d, tx_status %d\n",
sw_define, tx_status);
+ rcu_read_lock();
+ list_for_each_entry_rcu(wait, &rtwdev->tx_waits, list) {
+ if (wait->sn == sw_define) {
+ wait->tx_done = tx_status == RTW89_TX_DONE;
+ complete_all(&wait->completion);
+ break;
+ }
+ }
+ rcu_read_unlock();
+
spin_lock_irqsave(&rtwdev->tx_rpt_queue.lock, flags);
skb_queue_walk_safe(&rtwdev->tx_rpt_queue, cur, tmp) {
n = (u8 *)RTW89_TX_SKB_CB(cur)->hci_priv;
diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c
index 98eff955fc96..342c05227191 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.c
+++ b/drivers/net/wireless/realtek/rtw89/usb.c
@@ -191,8 +191,13 @@ static u8 rtw89_usb_get_bulkout_id(u8 ch_dma)
static void rtw89_usb_ops_tx_rpt_enable(struct rtw89_dev *rtwdev,
struct rtw89_core_tx_request *tx_req)
{
+ struct rtw89_tx_wait_info *wait = tx_req->wait;
+
tx_req->desc_info.report = true;
tx_req->desc_info.sn = atomic_inc_return(&rtwdev->sn) & 0xF;
+
+ if (wait)
+ wait->sn = tx_req->desc_info.sn;
}
static void rtw89_usb_write_port_complete(struct urb *urb)
@@ -313,7 +318,9 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
txcb->txch = txch;
skb_queue_head_init(&txcb->tx_ack_queue);
- skb_queue_tail(&txcb->tx_ack_queue, skb);
+ /* tx_wait skbs are completed in rtw89_mac_c2h_tx_rpt() */
+ if (!rtw89_core_is_tx_wait(rtwdev, RTW89_TX_SKB_CB(skb)))
+ skb_queue_tail(&txcb->tx_ack_queue, skb);
ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len,
txcb);
--
2.51.0
Powered by blists - more mailing lists