From: Ping-Ke Shih <pkshih@realtek.com>
To: "luka.gejak@linux.dev" <luka.gejak@linux.dev>
Cc: "linux-wireless@vger.kernel.org" <linux-wireless@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"Michael Straube" <straube.linux@gmail.com>,
Peter Robinson <pbrobinson@gmail.com>,
Bitterblue Smith <rtl8821cerfe2@gmail.com>
Subject: RE: [PATCH v2 09/11] wifi: rtw88: sdio: add TX back-pressure and retry on page starvation
Date: Mon, 27 Jul 2026 09:21:10 +0000 [thread overview]
Message-ID: <af51f851a5254faa9caba6b1e04fefe8@realtek.com> (raw)
In-Reply-To: <20260725150427.93887-10-luka.gejak@linux.dev>
luka.gejak@linux.dev <luka.gejak@linux.dev> wrote:
> From: Luka Gejak <luka.gejak@linux.dev>
>
> Two problems show up on RTL8723BS uplink. The per-AC software FIFO is
> unbounded, so mac80211 keeps handing frames down until latency collapses
> under load. And when the chip runs out of free TX pages the queue is
> simply abandoned for that pass, which stalls the AC until something else
> kicks the worker.
>
> Stop the mac80211 queue once a data AC fills past a high watermark and
> wake it from the drain path when it falls back to a low one. Convert the
> TX work item to a delayed work so a temporary page shortage can be
> retried shortly afterwards instead of stalling, and cancel it on
> teardown.
>
> Measured on RTL8723BS hardware, uplink goes from 11.9 Mbit/s with 204
> TCP retransmits to 20.1 Mbit/s with 2.
>
> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
> ---
> drivers/net/wireless/realtek/rtw88/sdio.c | 75 ++++++++++++++++++++---
> drivers/net/wireless/realtek/rtw88/sdio.h | 3 +-
> 2 files changed, 68 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
> index 37d1d58fa55e..572b791249ed 100644
> --- a/drivers/net/wireless/realtek/rtw88/sdio.c
> +++ b/drivers/net/wireless/realtek/rtw88/sdio.c
> @@ -22,6 +22,15 @@
> #define RTW_SDIO_INDIRECT_RW_RETRIES 50
> #define RTW_SDIO_OQT_TIMEOUT_MS 1000
>
> +/* 8723BS SDIO TX FIFO back-pressure watermarks: stop the mac80211 queue once
> + * the per-AC software FIFO fills past the high watermark, and wake it from the
> + * TX drain path once it falls back to the low one. Bounds the queueing latency
> + * that otherwise causes uplink bufferbloat / congestion collapse.
> + */
comment style.
> +#define RTW_SDIO_TX_FIFO_HIWATER 16
> +#define RTW_SDIO_TX_FIFO_LOWATER 8
> +#define RTW_SDIO_TX_RETRY_DELAY msecs_to_jiffies(1)
> +
> static bool rtw_sdio_is_bus_addr(u32 addr)
> {
> return !!(addr & RTW_SDIO_BUS_MSK);
> @@ -1030,7 +1039,7 @@ static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev)
> {
> struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
>
> - queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work);
> + mod_delayed_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work, 0);
queue_delayed_work()?
> }
>
> static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter)
> @@ -1154,6 +1163,18 @@ static int rtw_sdio_tx_write(struct rtw_dev *rtwdev,
>
> skb_queue_tail(&rtwsdio->tx_queue[queue], skb);
>
> + /* Back-pressure on the data ACs (BK/BE/VI/VO): once the FIFO fills past
> + * the high watermark, stop the corresponding mac80211 queue so it stops
> + * handing us frames, bounding the queueing latency. Resumed from the TX
> + * drain path once the FIFO drains below the low watermark.
> + */
> + if (rtw_is_8723bs(rtwdev) && queue < RTW_TX_QUEUE_BCN &&
> + !rtwsdio->queue_stopped[queue] &&
> + skb_queue_len(&rtwsdio->tx_queue[queue]) >= RTW_SDIO_TX_FIFO_HIWATER) {
> + rtwsdio->queue_stopped[queue] = true;
> + ieee80211_stop_queue(rtwdev->hw, skb_get_queue_mapping(skb));
> + }
> +
> return 0;
> }
>
> @@ -1455,32 +1476,48 @@ static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev,
> ieee80211_tx_status_irqsafe(hw, skb);
> }
>
> -static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
> - enum rtw_tx_queue_type queue)
> +static int rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
> + enum rtw_tx_queue_type queue,
> + bool *processed)
> {
> struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> struct sk_buff *skb;
> + u16 q_map;
> int ret;
>
> + *processed = false;
> skb = skb_dequeue(&rtwsdio->tx_queue[queue]);
> if (!skb)
> - return;
> + return 0;
>
> + *processed = true;
> + q_map = skb_get_queue_mapping(skb);
> ret = rtw_sdio_write_port(rtwdev, skb, queue);
> if (ret) {
> skb_queue_head(&rtwsdio->tx_queue[queue], skb);
This case is also `processed = true`?
> - return;
> + return ret;
> }
>
> rtw_sdio_indicate_tx_status(rtwdev, skb);
> +
> + if (rtw_is_8723bs(rtwdev) && queue < RTW_TX_QUEUE_BCN &&
> + rtwsdio->queue_stopped[queue] &&
> + skb_queue_len(&rtwsdio->tx_queue[queue]) <= RTW_SDIO_TX_FIFO_LOWATER) {
> + rtwsdio->queue_stopped[queue] = false;
> + ieee80211_wake_queue(rtwdev->hw, q_map);
> + }
> +
> + return 0;
> }
>
> static void rtw_sdio_tx_handler(struct work_struct *work)
> {
> struct rtw_sdio_work_data *work_data =
> - container_of(work, struct rtw_sdio_work_data, work);
> + container_of(to_delayed_work(work), struct rtw_sdio_work_data,
> + work);
> struct rtw_sdio *rtwsdio;
> struct rtw_dev *rtwdev;
> + bool processed;
> int limit, queue;
>
> rtwdev = work_data->rtwdev;
> @@ -1491,7 +1528,24 @@ static void rtw_sdio_tx_handler(struct work_struct *work)
>
> for (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue >= 0; queue--) {
> for (limit = 0; limit < 1000; limit++) {
> - rtw_sdio_process_tx_queue(rtwdev, queue);
> + int ret;
> +
> + ret = rtw_sdio_process_tx_queue(rtwdev, queue, &processed);
> + if (ret) {
> + if (rtw_is_8723bs(rtwdev) && ret == -EBUSY) {
> + mod_delayed_work(rtwsdio->txwq,
> + &work_data->work,
> + RTW_SDIO_TX_RETRY_DELAY);
> + return;
> + }
> + break;
> + }
> +
> + if (rtw_is_8723bs(rtwdev) &&
> + queue == RTW_TX_QUEUE_MGMT && processed) {
> + mod_delayed_work(rtwsdio->txwq, &work_data->work, 0);
> + return;
> + }
Can you cleanup the handlers of return value and processed?
The logic isn't clear to me.
>
> if (skb_queue_empty(&rtwsdio->tx_queue[queue]))
> break;
> @@ -1518,14 +1572,16 @@ static int rtw_sdio_init_tx(struct rtw_dev *rtwdev)
> return -ENOMEM;
> }
>
> - for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
> + for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) {
> skb_queue_head_init(&rtwsdio->tx_queue[i]);
> + rtwsdio->queue_stopped[i] = false;
> + }
> rtwsdio->tx_handler_data = kmalloc_obj(*rtwsdio->tx_handler_data);
> if (!rtwsdio->tx_handler_data)
> goto err_destroy_wq;
>
> rtwsdio->tx_handler_data->rtwdev = rtwdev;
> - INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
> + INIT_DELAYED_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
>
> return 0;
>
> @@ -1539,6 +1595,7 @@ static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)
> struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> int i;
>
> + cancel_delayed_work_sync(&rtwsdio->tx_handler_data->work);
> destroy_workqueue(rtwsdio->txwq);
> kfree(rtwsdio->tx_handler_data);
>
> diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wireless/realtek/rtw88/sdio.h
> index 541d4302ec6b..f1f59f8301b8 100644
> --- a/drivers/net/wireless/realtek/rtw88/sdio.h
> +++ b/drivers/net/wireless/realtek/rtw88/sdio.h
> @@ -152,7 +152,7 @@ struct rtw_sdio_tx_data {
> };
>
> struct rtw_sdio_work_data {
> - struct work_struct work;
> + struct delayed_work work;
> struct rtw_dev *rtwdev;
> };
>
> @@ -168,6 +168,7 @@ struct rtw_sdio {
> struct workqueue_struct *txwq;
> struct rtw_sdio_work_data *tx_handler_data;
> struct sk_buff_head tx_queue[RTK_MAX_TX_QUEUE_NUM];
> + bool queue_stopped[RTK_MAX_TX_QUEUE_NUM];
>
> atomic_t free_pg_high;
> atomic_t free_pg_normal;
> --
> 2.55.0
next prev parent reply other threads:[~2026-07-27 9:21 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-25 15:04 [PATCH v2 00/11] wifi: rtw88: preparations for RTL8723B/RTL8723BS luka.gejak
2026-07-25 15:04 ` [PATCH v2 01/11] wifi: rtw88: add the RTL8723B chip type and SDIO helper luka.gejak
2026-07-27 7:12 ` Ping-Ke Shih
2026-07-27 13:25 ` Luka Gejak
2026-07-25 15:04 ` [PATCH v2 02/11] wifi: rtw88: rx: mark zero length packets on RTL8723BS luka.gejak
2026-07-27 7:14 ` Ping-Ke Shih
2026-07-25 15:04 ` [PATCH v2 03/11] wifi: rtw88: fw: add the GNT_BT firmware command luka.gejak
2026-07-25 15:04 ` [PATCH v2 04/11] wifi: rtw88: fw: fix the reserved page upload on RTL8723BS luka.gejak
2026-07-27 7:37 ` Ping-Ke Shih
2026-07-25 15:04 ` [PATCH v2 05/11] wifi: rtw88: coex: add the RTL8723BS scan antenna workaround luka.gejak
2026-07-27 7:58 ` Ping-Ke Shih
2026-07-25 15:04 ` [PATCH v2 06/11] wifi: rtw88: coex: reassert the antenna path when associating luka.gejak
2026-07-27 8:21 ` Ping-Ke Shih
2026-07-25 15:04 ` [PATCH v2 07/11] wifi: rtw88: sdio: track free TX pages and OQT credits for RTL8723BS luka.gejak
2026-07-27 8:52 ` Ping-Ke Shih
2026-07-25 15:04 ` [PATCH v2 08/11] wifi: rtw88: sdio: set up RX aggregation and interrupts " luka.gejak
2026-07-27 8:59 ` Ping-Ke Shih
2026-07-25 15:04 ` [PATCH v2 09/11] wifi: rtw88: sdio: add TX back-pressure and retry on page starvation luka.gejak
2026-07-27 9:21 ` Ping-Ke Shih [this message]
2026-07-25 15:04 ` [PATCH v2 10/11] wifi: rtw88: record beacons from the target BSSID before authenticating luka.gejak
2026-07-27 9:27 ` Ping-Ke Shih
2026-07-25 15:04 ` [PATCH v2 11/11] wifi: rtw88: run the RTL8723BS association register sequence luka.gejak
2026-07-27 9:33 ` 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=af51f851a5254faa9caba6b1e04fefe8@realtek.com \
--to=pkshih@realtek.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=luka.gejak@linux.dev \
--cc=pbrobinson@gmail.com \
--cc=rtl8821cerfe2@gmail.com \
--cc=straube.linux@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