From: Lucid Duck <lucid_duck@justthetip.ca>
To: pkshih@realtek.com
Cc: linux-wireless@vger.kernel.org, rtl8821cerfe2@gmail.com,
morrownr@gmail.com, Lucid Duck <lucid_duck@justthetip.ca>
Subject: [PATCH v6] wifi: rtw89: usb: fix TX flow control by tracking in-flight URBs
Date: Wed, 1 Apr 2026 22:22:16 -0700 [thread overview]
Message-ID: <20260402052216.207858-1-lucid_duck@justthetip.ca> (raw)
In-Reply-To: <20260330025959.399018-1-lucid_duck@justthetip.ca>
rtw89_usb_ops_check_and_reclaim_tx_resource() returns a hardcoded
placeholder value (42) instead of actual TX resource availability.
This violates mac80211's flow control contract, preventing backpressure
and causing uncontrolled URB accumulation under sustained TX load.
Fix by adding per-channel atomic counters (tx_inflight[]) that track
in-flight URBs. Increment before usb_submit_urb() with rollback on
failure, decrement in the completion callback, and return the
remaining capacity to mac80211. The firmware command channel (CH12)
always returns 1 since it has its own flow control.
The pre-increment pattern prevents a race where USB core completes the
URB on another CPU before the submitting code increments the counter.
128 URBs per channel provides headroom for RTL8832CU at 160 MHz
bandwidth. Tested on RTL8852AU (USB3 80 MHz) where 64 and 128 showed
equivalent throughput, and on RTL8832AU where 128 sustained full
throughput under 8-stream parallel load.
Tested on D-Link DWA-X1850 (RTL8832AU), kernel 6.19.8, Fedora 43:
Unpatched -> Patched (128 URBs)
USB3 5GHz UL: 844 -> 837 Mbps (no regression)
USB3 5GHz retx: 3 -> 0
USB3 2.4GHz UL: 162 -> 164 Mbps (no regression)
4-stream UL: 858 -> 826 Mbps (within variance)
8-stream UL: 872 -> 826 Mbps (within variance)
UDP flood: 0% loss (690K datagrams)
60-second soak: 855 Mbps, 0 retransmits
Reported-by: morrownr <morrownr@gmail.com>
Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
---
v6:
- Rebased onto rtw.git rtw-next to resolve merge conflict in usb.h
(new RXAGG register defines added upstream)
- No code changes from v5
v5:
- Sent from kernel tree (v4 was from standalone rtw89 tree)
v4:
- Increased URB limit from 64 to 128 per Ping-Ke's review (headroom
for RTL8832CU 160 MHz)
- Simplified CH12 handling per Ping-Ke's review
- Removed all comments per Ping-Ke's review
v3:
- Removed comments flagged as "AI-like" by reviewer
- Added 8-stream parallel load test data
v2:
- Pre-increment before usb_submit_urb() to prevent completion race
- Added per-channel tracking (was global)
- Added test data to commit message
drivers/net/wireless/realtek/rtw89/usb.c | 20 ++++++++++++++++++--
drivers/net/wireless/realtek/rtw89/usb.h | 3 +++
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c
index 581b8c05f..767a95f75 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.c
+++ b/drivers/net/wireless/realtek/rtw89/usb.c
@@ -161,16 +161,24 @@ static u32
rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev,
u8 txch)
{
+ struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+ int inflight;
+
if (txch == RTW89_TXCH_CH12)
return 1;
- return 42; /* TODO some kind of calculation? */
+ inflight = atomic_read(&rtwusb->tx_inflight[txch]);
+ if (inflight >= RTW89_USB_MAX_TX_URBS_PER_CH)
+ return 0;
+
+ return RTW89_USB_MAX_TX_URBS_PER_CH - inflight;
}
static void rtw89_usb_write_port_complete(struct urb *urb)
{
struct rtw89_usb_tx_ctrl_block *txcb = urb->context;
struct rtw89_dev *rtwdev = txcb->rtwdev;
+ struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
struct ieee80211_tx_info *info;
struct rtw89_txwd_body *txdesc;
struct sk_buff *skb;
@@ -229,6 +237,8 @@ static void rtw89_usb_write_port_complete(struct urb *urb)
break;
}
+ atomic_dec(&rtwusb->tx_inflight[txcb->txch]);
+
kfree(txcb);
}
@@ -306,9 +316,13 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
skb_queue_tail(&txcb->tx_ack_queue, skb);
+ atomic_inc(&rtwusb->tx_inflight[txch]);
+
ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len,
txcb);
if (ret) {
+ atomic_dec(&rtwusb->tx_inflight[txch]);
+
if (ret != -ENODEV)
rtw89_err(rtwdev, "write port txch %d failed: %d\n",
txch, ret);
@@ -684,8 +698,10 @@ static void rtw89_usb_init_tx(struct rtw89_dev *rtwdev)
struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
int i;
- for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++)
+ for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) {
skb_queue_head_init(&rtwusb->tx_queue[i]);
+ atomic_set(&rtwusb->tx_inflight[i], 0);
+ }
}
static void rtw89_usb_deinit_tx(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h
index 3d17e514e..507f61f58 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.h
+++ b/drivers/net/wireless/realtek/rtw89/usb.h
@@ -31,6 +31,8 @@
#define R_AX_RXAGG_0 0x8900
#define B_AX_RXAGG_0_BUF_SZ_4K GENMASK(7, 0)
+#define RTW89_USB_MAX_TX_URBS_PER_CH 128
+
struct rtw89_usb_info {
u32 usb_host_request_2;
u32 usb_wlan0_1;
@@ -75,6 +77,7 @@ struct rtw89_usb {
struct usb_anchor tx_submitted;
struct sk_buff_head tx_queue[RTW89_TXCH_NUM];
+ atomic_t tx_inflight[RTW89_TXCH_NUM];
};
static inline struct rtw89_usb *rtw89_usb_priv(struct rtw89_dev *rtwdev)
--
2.53.0
next prev parent reply other threads:[~2026-04-02 5:26 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-23 23:33 [PATCH v3] wifi: rtw89: usb: fix TX flow control by tracking in-flight URBs Lucid Duck
2026-03-24 0:46 ` Ping-Ke Shih
2026-03-27 19:40 ` [PATCH v4] " Lucid Duck
[not found] ` <202603281411.62SEBByK43790225@rtits1.realtek.com.tw>
2026-03-30 0:38 ` Ping-Ke Shih
2026-03-30 0:42 ` Ping-Ke Shih
2026-03-30 2:59 ` [PATCH v5] " Lucid Duck
2026-03-30 5:35 ` Ping-Ke Shih
2026-04-02 3:09 ` Ping-Ke Shih
2026-04-02 5:22 ` Lucid Duck [this message]
2026-04-02 5:38 ` [PATCH v6] " Ping-Ke Shih
2026-04-02 6:09 ` Ping-Ke Shih
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260402052216.207858-1-lucid_duck@justthetip.ca \
--to=lucid_duck@justthetip.ca \
--cc=linux-wireless@vger.kernel.org \
--cc=morrownr@gmail.com \
--cc=pkshih@realtek.com \
--cc=rtl8821cerfe2@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox