From: Lucid Duck <lucid_duck@justthetip.ca>
To: linux-wireless@vger.kernel.org
Cc: Ping-Ke Shih <pkshih@realtek.com>, Lucid Duck <lucid_duck@justthetip.ca>
Subject: [PATCH] wifi: rtw89: usb: fix TX flow control by tracking in-flight URBs
Date: Sun, 25 Jan 2026 14:19:43 -0800 [thread overview]
Message-ID: <20260125221943.36001-1-lucid_duck@justthetip.ca> (raw)
rtw89_usb_ops_check_and_reclaim_tx_resource() currently returns a
hardcoded placeholder value of 42, violating mac80211's TX flow control
contract. This causes uncontrolled URB accumulation under sustained TX
load since mac80211 believes resources are always available.
Fix this by implementing proper TX backpressure:
- Add per-channel atomic counters (tx_inflight[]) to track URBs between
submission and completion
- Increment counter before usb_submit_urb() with rollback on failure
- Decrement counter in completion callback
- Return available slots (max - inflight) to mac80211, or 0 at capacity
- Exclude firmware command channel (CH12) from flow control
Tested on D-Link DWA-X1850 (RTL8832AU) with:
- Sustained high-throughput traffic
- Module load/unload stress tests
- Hot-unplug during active transmission
- 30-minute soak test verifying counters balance at idle
Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
drivers/net/wireless/realtek/rtw89/usb.c | 27 ++++++++++++++++++++++--
drivers/net/wireless/realtek/rtw89/usb.h | 6 ++++++
2 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c
index e77561a4d..6fcf32603 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.c
+++ b/drivers/net/wireless/realtek/rtw89/usb.c
@@ -161,16 +161,25 @@ 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;
+
+ /* Firmware command channel is not flow-controlled */
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 +238,10 @@ static void rtw89_usb_write_port_complete(struct urb *urb)
break;
}
+ /* Decrement in-flight counter (skip firmware command channel) */
+ if (txcb->txch != RTW89_TXCH_CH12)
+ atomic_dec(&rtwusb->tx_inflight[txcb->txch]);
+
kfree(txcb);
}
@@ -306,9 +319,17 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
skb_queue_tail(&txcb->tx_ack_queue, skb);
+ /* Increment BEFORE submit to avoid race with completion */
+ if (txch != RTW89_TXCH_CH12)
+ atomic_inc(&rtwusb->tx_inflight[txch]);
+
ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len,
txcb);
if (ret) {
+ /* Rollback increment on failure */
+ if (txch != RTW89_TXCH_CH12)
+ atomic_dec(&rtwusb->tx_inflight[txch]);
+
if (ret != -ENODEV)
rtw89_err(rtwdev, "write port txch %d failed: %d\n",
txch, ret);
@@ -666,8 +687,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 203ec8e99..f72a8b1b2 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.h
+++ b/drivers/net/wireless/realtek/rtw89/usb.h
@@ -20,6 +20,9 @@
#define RTW89_MAX_ENDPOINT_NUM 9
#define RTW89_MAX_BULKOUT_NUM 7
+/* TX flow control: max in-flight URBs per channel */
+#define RTW89_USB_MAX_TX_URBS_PER_CH 32
+
struct rtw89_usb_info {
u32 usb_host_request_2;
u32 usb_wlan0_1;
@@ -63,6 +66,9 @@ struct rtw89_usb {
struct usb_anchor tx_submitted;
struct sk_buff_head tx_queue[RTW89_TXCH_NUM];
+
+ /* TX flow control: track in-flight URBs per channel */
+ atomic_t tx_inflight[RTW89_TXCH_NUM];
};
static inline struct rtw89_usb *rtw89_usb_priv(struct rtw89_dev *rtwdev)
--
2.52.0
next reply other threads:[~2026-01-25 22:31 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-25 22:19 Lucid Duck [this message]
2026-01-26 3:39 ` [PATCH] wifi: rtw89: usb: fix TX flow control by tracking in-flight URBs Ping-Ke Shih
2026-01-26 10:14 ` Mh_chen
2026-01-27 5:00 ` Lucid Duck
2026-01-26 14:09 ` Bitterblue Smith
2026-01-27 20:01 ` Lucid Duck
[not found] ` <202601291256.60TCusZS3018440@rtits1.realtek.com.tw>
2026-01-29 13:12 ` Mh_chen
2026-03-21 3:37 ` [PATCH v2] " Lucid Duck
2026-03-23 9:31 ` Ping-Ke Shih
2026-03-23 23:33 ` Lucid Duck
2026-03-24 0:38 ` 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=20260125221943.36001-1-lucid_duck@justthetip.ca \
--to=lucid_duck@justthetip.ca \
--cc=linux-wireless@vger.kernel.org \
--cc=pkshih@realtek.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