From nobody Tue Sep 29 00:30:54 2026 Received: from mta1.migadu.com (out-197.mta1.migadu.com [95.215.58.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DB9D843FD1F for ; Fri, 14 Aug 2026 10:54:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704849; cv=none; b=C3ARjHuzNx2kR5gNtqjbokZ7rKdBJXIwZcvR4kLGeB/h4GbT0iy9ERZ4n/l3KxxhUD/DmDPTC8FobwxXXBiGTqQlquicl4H2z/wtanBS+fFnw8ovJkGhkJZJfKYf45PCga4Iz3lATGW5Ctpl9rLnj1r9mYZ9fYj0dx4Gv3/g32Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704849; c=relaxed/simple; bh=kZDJOvLYT2nsEkel50ckM+8l9eH/aA0F3RgRJztrk3U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DSKau5RmBYkiFvcBrUnITqPX50TohJnTgRQqzcmKKWGJw6SUlQx4oYIoEXqBi1EGQkY9vFmLGoOWgMZdgPBMAzROFNprPH3SwMP+dXFKtN7+ZfI5ZAgLL60BuJTYty4Dyhjd+WLiRkyZbu3m4bxATcJXZ+nXJpo6mZCdpjkVdWk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=i0RRTGJm; arc=none smtp.client-ip=95.215.58.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="i0RRTGJm" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=kZDJOvLYT2nsEkel50ckM+8l9eH/aA0F3RgRJztrk3U=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1786704836; v=1; x=1787309636; b=i0RRTGJmkW5+TT+i8kG7KJVO9CPDNUxj+voy+LH54QtGCJdGDUu1qkOJ/DB7p9AG82uggEhV X0uOeW4/T3xRO9GcabzVQGjKoJ/xveN5PWJyrshT/zHyenEB0CIzOlfFwwpK/PsdWlVUvCO4MTm iqPYXf/nbiiiLYeFxO1sUfqk= X-Envelope-To: linux-kernel@vger.kernel.org Received: from kali (46.239.4.165) by smtp.migadu.com with ESMTPS id 591582e075d66969; Fri, 14 Aug 2026 10:53:56 +0000 X-Migadu-Flow: FLOW_OUT From: luka.gejak@linux.dev To: Ping-Ke Shih , linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Michael Straube , Bitterblue Smith , Peter Robinson , Hans de Goede , Luka Gejak Subject: [PATCH v5 1/6] wifi: rtw88: add the RTL8723B chip type and SDIO helper Date: Fri, 14 Aug 2026 10:53:21 +0000 Message-ID: <20260814105327.6687-2-luka.gejak@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260814105327.6687-1-luka.gejak@linux.dev> References: <20260814105327.6687-1-luka.gejak@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Luka Gejak The RTL8723B is a 802.11n combo chip whose SDIO variant, RTL8723BS, runs a Realtek vendor firmware that differs from the firmware used by the other rtw88 8723 family devices. Supporting it needs a number of small adjustments spread across the shared core, all of which have to be restricted to this one chip and bus combination. Add the chip type and a helper that tests for it, so the changes that follow can be gated without repeating the chip and bus comparison. Signed-off-by: Luka Gejak Acked-by: Ping-Ke Shih --- drivers/net/wireless/realtek/rtw88/main.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wirele= ss/realtek/rtw88/main.h index c6e981ba7986..8f86f7c12de5 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -194,6 +194,7 @@ enum rtw_chip_type { RTW_CHIP_TYPE_8723D, RTW_CHIP_TYPE_8821C, RTW_CHIP_TYPE_8703B, + RTW_CHIP_TYPE_8723B, RTW_CHIP_TYPE_8821A, RTW_CHIP_TYPE_8812A, RTW_CHIP_TYPE_8814A, @@ -2194,6 +2195,12 @@ static inline bool rtw_chip_has_tx_stbc(struct rtw_d= ev *rtwdev) return rtwdev->chip->tx_stbc; } =20 +static inline bool rtw_is_8723bs(struct rtw_dev *rtwdev) +{ + return rtwdev->chip->id =3D=3D RTW_CHIP_TYPE_8723B && + rtwdev->hci.type =3D=3D RTW_HCI_TYPE_SDIO; +} + static inline u8 rtw_acquire_macid(struct rtw_dev *rtwdev) { unsigned long mac_id; --=20 2.53.0 From nobody Tue Sep 29 00:30:54 2026 Received: from mta1.migadu.com (out-200.mta1.migadu.com [95.215.58.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0D2C45D1B9 for ; Fri, 14 Aug 2026 10:54:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.200 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704851; cv=none; b=OeYqVGVrm/tMpFijVQRvms9k74az00aGuxFC8omkhgH1NWhHlbX+gkdhrj0EdEveQHi+r6vBx80YvKqXl48sXFCl5Fyw6lfog8BkoCQK8Hcu7WaDfAKVmB+LYm7Ca+Kf6E/RHhrNyGxTHqmYXOZ3icG9dC+dSwNocTpvAIgF9hg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704851; c=relaxed/simple; bh=otNevKNPjmGQ8hQocf0jIYx9y7s7VhT/vzqqX5vdSV0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eGb4pC9kexH00fPSZNHjaoaLj3w7IZKH4zkaOrO+qAp3XC/oYbirkN+tdcJw/BBzHwWrMh49cEXWTtVkyuvAjdsmWGV2DF6uyJuc2JvLYzxCRPgRbqjYCxHBaDsVIdR7r/iKLO13Vm+JIGtd0dAIybQd1Ju3bXoUIRaEmwDli2M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=FJaN02pG; arc=none smtp.client-ip=95.215.58.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="FJaN02pG" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=otNevKNPjmGQ8hQocf0jIYx9y7s7VhT/vzqqX5vdSV0=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1786704842; v=1; x=1787309642; b=FJaN02pGYR7K+IHFug66ykMQt/GvRs9sXABhuQGaR7XERA/LIlBnfjoqDlHq0xVL9OF2Jx4k QXYG1CgkXaD9bL2ACbP66kYYAhy78UOtgAWUJDcmI5haq86dtE/Lq4gykLFp3iwX49yWdMaGIrk 49k8Q46u1jn5I3N8fakCOALg= X-Envelope-To: linux-kernel@vger.kernel.org Received: from kali (46.239.4.165) by smtp.migadu.com with ESMTPS id 674134dbe00d38be; Fri, 14 Aug 2026 10:54:02 +0000 X-Migadu-Flow: FLOW_OUT From: luka.gejak@linux.dev To: Ping-Ke Shih , linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Michael Straube , Bitterblue Smith , Peter Robinson , Hans de Goede , Luka Gejak Subject: [PATCH v5 2/6] wifi: rtw88: rx: mark zero length packets on RTL8723BS Date: Fri, 14 Aug 2026 10:53:22 +0000 Message-ID: <20260814105327.6687-3-luka.gejak@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260814105327.6687-1-luka.gejak@linux.dev> References: <20260814105327.6687-1-luka.gejak@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Luka Gejak Like the RTL8703B, the RTL8723BS reports receive descriptors with a zero packet length, which the vendor driver drops outright. rtw88 already flags these as having no PSDU for the RTL8703B, so extend the same handling rather than passing an empty frame up. This is gated on the SDIO interface rather than the chip id, because it has only been observed there; the USB variant is not known to do it. Co-developed-by: Michael Straube Signed-off-by: Michael Straube Signed-off-by: Luka Gejak Acked-by: Ping-Ke Shih --- drivers/net/wireless/realtek/rtw88/rx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless= /realtek/rtw88/rx.c index 01fd299abb7f..fde21c840ac0 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.c +++ b/drivers/net/wireless/realtek/rtw88/rx.c @@ -253,10 +253,12 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtw= dev, =20 rtw_rx_addr_match(rtwdev, pkt_stat, hdr); =20 - /* Rtl8723cs driver checks for size < 14 or size > 8192 and - * simply drops the packet. + /* + * Rtl8723cs and rtl8723bs drivers check for size < 14 or size > 8192 + * and simply drop the packet. */ - if (rtwdev->chip->id =3D=3D RTW_CHIP_TYPE_8703B && pkt_stat->pkt_len =3D= =3D 0) { + if (pkt_stat->pkt_len =3D=3D 0 && + (rtwdev->chip->id =3D=3D RTW_CHIP_TYPE_8703B || rtw_is_8723bs(rtwdev)= )) { rx_status->flag |=3D RX_FLAG_NO_PSDU; rtw_dbg(rtwdev, RTW_DBG_RX, "zero length packet"); } --=20 2.53.0 From nobody Tue Sep 29 00:30:54 2026 Received: from mta1.migadu.com (out-205.mta1.migadu.com [95.215.58.205]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9086645DF43 for ; Fri, 14 Aug 2026 10:54:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.205 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704860; cv=none; b=NeWy6ZEJ6XDUqQ3rMz10a0c3IQBktVN8VUJ+AuC1nYFulQuQw8GDk/e9n4Sxhkbq3lyBOmlQLdIfozWyvw2rTcAPoqqRXAEyZtrxBrxlSrpTUtTUmmkrEqsv0Et01DdNw48+3VqnCApXEhX2+VKeV/fL37p1iQwwcq5tJbgPH2U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704860; c=relaxed/simple; bh=cb1ghMSjsMPV5UATzLYB2QEVJLv/pEmZ1lCn92KHN9s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZS9Uz+1cBzxTyy0NOWUAlvqQl0T1IRhfMuaU43vSfF00xzmasLMu7Pecze2i9S/iUm9Fh9o2CzLk12BPpf9wVB4pWUowj/FlbdjESVdZE+PlrtfL4m6TzM+i3VcBLNkhKAX/5xy7T1mQ8IVrLDRdkmvancN5j5mMHflkA6zaUT8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=ktcMgqQ8; arc=none smtp.client-ip=95.215.58.205 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="ktcMgqQ8" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=cb1ghMSjsMPV5UATzLYB2QEVJLv/pEmZ1lCn92KHN9s=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1786704848; v=1; x=1787309648; b=ktcMgqQ84ir2q32C3DDrnPb93I7J/2uQ4tFrApIE0Gz31i3qL77BZ23JNHfNl0kiWRmpA+4h iFM5gFGmR2bxXuLprMJfHHS4vTtahBqukm5ooNaor2/RMN5ecHBAIx8ZEX0VpCN+UNVI2Upm8I1 sPbOlf+hQ6YgPunnv641xnvE= X-Envelope-To: linux-kernel@vger.kernel.org Received: from kali (46.239.4.165) by smtp.migadu.com with ESMTPS id e24ab82240e9b20d; Fri, 14 Aug 2026 10:54:07 +0000 X-Migadu-Flow: FLOW_OUT From: luka.gejak@linux.dev To: Ping-Ke Shih , linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Michael Straube , Bitterblue Smith , Peter Robinson , Hans de Goede , Luka Gejak Subject: [PATCH v5 3/6] wifi: rtw88: tx: extend the TX report purge timeout to RTL8723BS Date: Fri, 14 Aug 2026 10:53:23 +0000 Message-ID: <20260814105327.6687-4-luka.gejak@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260814105327.6687-1-luka.gejak@linux.dev> References: <20260814105327.6687-1-luka.gejak@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Luka Gejak Commit c80788f7c5ae ("wifi: rtw88: increase TX report timeout to fix race condition") raised the purge timeout to 2500 ms for the RTL8723DU, because the firmware can stay off channel during background scans for longer than the 500 ms default, which delays the TX reports and lets the purge timer drop the tracking skbs. The host stack then reads the missing status as loss and collapses TCP throughput. The RTL8723BS runs the same vendor firmware over a slower SDIO host and hits the same race. Testers on ARM SDIO boards see "failed to get tx report from firmware" under load, with the same throughput collapse. Extend the 2500 ms timeout to the RTL8723BS. Reported-by: Peter Robinson Closes: https://lore.kernel.org/all/CALeDE9PgQmpMfDt1DgfLD4tBFGH0MZ7GncV6RU= EOHhHbKF+TdQ@mail.gmail.com/ Signed-off-by: Luka Gejak Acked-by: Ping-Ke Shih --- drivers/net/wireless/realtek/rtw88/tx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless= /realtek/rtw88/tx.c index 9d747a060b98..797c1e0402f2 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -208,8 +208,9 @@ void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, stru= ct sk_buff *skb, u8 sn) __skb_queue_tail(&tx_report->queue, skb); spin_unlock_irqrestore(&tx_report->q_lock, flags); =20 - if (rtwdev->chip->id =3D=3D RTW_CHIP_TYPE_8723D && - rtwdev->hci.type =3D=3D RTW_HCI_TYPE_USB) + if ((rtwdev->chip->id =3D=3D RTW_CHIP_TYPE_8723D && + rtwdev->hci.type =3D=3D RTW_HCI_TYPE_USB) || + rtw_is_8723bs(rtwdev)) timeout =3D msecs_to_jiffies(2500); =20 mod_timer(&tx_report->purge_timer, jiffies + timeout); --=20 2.53.0 From nobody Tue Sep 29 00:30:54 2026 Received: from mta1.migadu.com (out-208.mta1.migadu.com [95.215.58.208]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6FB9145DF76 for ; Fri, 14 Aug 2026 10:54:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.208 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704868; cv=none; b=FLVonD7/z/MvhWidDggHSX+t/yI7QuQZhrvWhkUAKHzdQXD/Gegf5hVdFrv/E0j7e1E3JCtIzfst7yXvqvY/utPiaRuXlwiDoAlcfyx9ypj2csqhuLOlh3wqhHHh0lK9q7LTpiJ3Wfhw/woZyyEIxc+42GCFmcqI1TQEp9rKJY8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704868; c=relaxed/simple; bh=5Trv/6BmEbTrBtdmzU9oSRQoSzziDvXj9zW1CFkSVmk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AwGN5QVIg68JUlgeemyrBmoL2CtN9nmb5d0k3BuTHC7KiREWBwUNH145bvvBE3ZzK+yNC/RUsGr3RvaYDbIYANOVuezTltrERNdGYzez17uRQqY/Jy3OczyK+qvX241pJOVvI0CLuls4/AkWAc/nHSjAaprC2xO253H3jdbf9IA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=N6vXfDEz; arc=none smtp.client-ip=95.215.58.208 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="N6vXfDEz" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=5Trv/6BmEbTrBtdmzU9oSRQoSzziDvXj9zW1CFkSVmk=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1786704854; v=1; x=1787309654; b=N6vXfDEzMgoQldcYRTRx6F0F9iqA3V7ezrAUgLesxzXwQMRYqoC87HKHo5FhAdXIbryGcqdS +KlFy165FQJwn2PqIjLXaJnYXZWc7VeiGleOK6Y8HiOX5XAmnfkBqeHC7E+SjIFuAX49AZsJG9z 3jQ9tMiXZr83fSQqleXmFe1A= X-Envelope-To: linux-kernel@vger.kernel.org Received: from kali (46.239.4.165) by smtp.migadu.com with ESMTPS id a67c29653f87d61c; Fri, 14 Aug 2026 10:54:14 +0000 X-Migadu-Flow: FLOW_OUT From: luka.gejak@linux.dev To: Ping-Ke Shih , linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Michael Straube , Bitterblue Smith , Peter Robinson , Hans de Goede , Luka Gejak Subject: [PATCH v5 4/6] wifi: rtw88: sdio: track free TX pages and OQT credits for RTL8723BS Date: Fri, 14 Aug 2026 10:53:24 +0000 Message-ID: <20260814105327.6687-5-luka.gejak@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260814105327.6687-1-luka.gejak@linux.dev> References: <20260814105327.6687-1-luka.gejak@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Luka Gejak The RTL8723BS reports free TX page counts that the generic 8051 path reads back from the chip on every transfer, which is both slow over SDIO and unreliable on this part: the register frequently reads back zero while pages are in fact available. It also gates transmission on a free count in the SDIO output queue, REG_SDIO_OQT_FREE_PG, which rtw88 does not track at all. The vendor driver calls this the OQT free space and never expands the acronym; the register holds the number of further transfers the SDIO output queue can accept, and the chip discards writes that arrive when it has run out. Mirror the vendor driver and keep the per-queue and public page counts in software, seeded at start and resynchronised from the chip only when the cached counts say there is not enough room. Wait for a free output queue entry before writing, and account for the pages consumed after a successful transfer. Transfers also have to be padded up to the SDIO block size for this chip rather than using the generic alignment, so size the write separately from the frame and trim the skb back afterwards. Measured on RTL8723BS hardware against an iperf3 server one hop behind the AP, with the wlan0 byte counters as ground truth. On the generic path the association completes but no data passes at all: TCP and UDP both measure 0 bit/s in either direction. With this patch TCP is 25.3 Mbit/s up and 37.3 Mbit/s down, and UDP is 25.0 Mbit/s up at 0% loss. Signed-off-by: Luka Gejak Acked-by: Ping-Ke Shih --- drivers/net/wireless/realtek/rtw88/sdio.c | 293 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw88/sdio.h | 10 + 2 files changed, 291 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wirele= ss/realtek/rtw88/sdio.c index 5b40d74b16ee..a8209b9acefd 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -20,6 +20,7 @@ #include "tx.h" =20 #define RTW_SDIO_INDIRECT_RW_RETRIES 50 +#define RTW_SDIO_OQT_TIMEOUT_MS 1000 =20 static bool rtw_sdio_is_bus_addr(u32 addr) { @@ -548,12 +549,157 @@ static int rtw_sdio_read_port(struct rtw_dev *rtwdev= , u8 *buf, size_t count) return ret; } =20 +/* + * The cached free page counters are a fast path hint only. They are writt= en + * from the single threaded TX work and, for H2C and reserved page writes, + * from process context, so they are atomic_t; whenever they claim there is + * not enough room they are resynchronised from the chip before the caller + * gives up, which also absorbs a lost update. + */ +static void rtw_sdio_8723bs_store_free_txpg(struct rtw_dev *rtwdev, + u32 free_txpg) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + + atomic_set(&rtwsdio->free_pg_high, + u32_get_bits(free_txpg, BIT_FREE_TXPG_HIGH)); + atomic_set(&rtwsdio->free_pg_normal, + u32_get_bits(free_txpg, BIT_FREE_TXPG_NORMAL)); + atomic_set(&rtwsdio->free_pg_low, + u32_get_bits(free_txpg, BIT_FREE_TXPG_LOW)); + atomic_set(&rtwsdio->free_pg_pub, + u32_get_bits(free_txpg, BIT_FREE_TXPG_PUB)); +} + +/* + * Refresh the cached counters from the chip. Returns false when the chip + * reports no free pages at all, which means the counts cannot be trusted + * and the caller has to decide what to do instead. + */ +static bool rtw_sdio_8723bs_sync_free_txpg(struct rtw_dev *rtwdev) +{ + u32 free_txpg =3D rtw_read32(rtwdev, REG_SDIO_FREE_TXPG); + + if (!free_txpg) + return false; + + rtw_sdio_8723bs_store_free_txpg(rtwdev, free_txpg); + + return true; +} + +/* + * Size of the public page pool: whatever the transmit page allocation has + * left once the per queue pools are taken out. Fails if the allocation + * cannot cover the reserved queues, since the remainder would underflow a= nd + * there would be no sensible pool to hand out. + */ +static int rtw_sdio_8723bs_pubq_num(struct rtw_dev *rtwdev, u16 *pubq_num) +{ + const struct rtw_page_table *pg_tbl =3D &rtwdev->chip->page_table[0]; + u16 acq_pg_num =3D rtwdev->fifo.acq_pg_num; + u16 reserved_num; + + reserved_num =3D pg_tbl->hq_num + pg_tbl->lq_num + pg_tbl->nq_num + + pg_tbl->exq_num + pg_tbl->gapq_num; + if (acq_pg_num <=3D reserved_num) { + rtw_err(rtwdev, + "no transmit pages left for the public queue: %u of %u reserved\n", + reserved_num, acq_pg_num); + return -EINVAL; + } + + *pubq_num =3D acq_pg_num - reserved_num; + + return 0; +} + +static int rtw_sdio_8723bs_init_free_txpg(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + const struct rtw_page_table *pg_tbl; + u16 pubq_num; + int ret; + + /* Seed from the page table when the chip has nothing to report yet. */ + if (!rtw_sdio_8723bs_sync_free_txpg(rtwdev)) { + ret =3D rtw_sdio_8723bs_pubq_num(rtwdev, &pubq_num); + if (ret) + return ret; + + pg_tbl =3D &rtwdev->chip->page_table[0]; + atomic_set(&rtwsdio->free_pg_high, pg_tbl->hq_num); + atomic_set(&rtwsdio->free_pg_normal, pg_tbl->nq_num); + atomic_set(&rtwsdio->free_pg_low, pg_tbl->lq_num); + atomic_set(&rtwsdio->free_pg_pub, pubq_num); + } + + atomic_set(&rtwsdio->tx_oqt_free, + rtw_read8(rtwdev, REG_SDIO_OQT_FREE_PG)); + + return 0; +} + +/* + * Sum of the queue's dedicated counter and the public pool, clamped at ze= ro: + * a lost update between the check below and rtw_sdio_8723bs_consume_txpg() + * can briefly drive a counter negative, and letting that wrap would hide = the + * shortage instead of triggering a resync from the chip. + */ +static unsigned int rtw_sdio_8723bs_pages_free(struct rtw_dev *rtwdev, + atomic_t *dedicated) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + int free; + + free =3D atomic_read(dedicated) + atomic_read(&rtwsdio->free_pg_pub); + + return free > 0 ? free : 0; +} + +static atomic_t *rtw_sdio_8723bs_free_txpg(struct rtw_dev *rtwdev, u8 queu= e) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + + switch (queue) { + case RTW_TX_QUEUE_VI: + return &rtwsdio->free_pg_normal; + case RTW_TX_QUEUE_BE: + case RTW_TX_QUEUE_BK: + return &rtwsdio->free_pg_low; + case RTW_TX_QUEUE_BCN: + case RTW_TX_QUEUE_H2C: + case RTW_TX_QUEUE_HI0: + case RTW_TX_QUEUE_MGMT: + case RTW_TX_QUEUE_VO: + return &rtwsdio->free_pg_high; + default: + return NULL; + } +} + static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue, size_t count) { unsigned int pages_free, pages_needed; =20 - if (rtw_chip_wcpu_8051(rtwdev)) { + if (rtw_is_8723bs(rtwdev)) { + atomic_t *dedicated; + + dedicated =3D rtw_sdio_8723bs_free_txpg(rtwdev, queue); + if (!dedicated) { + rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue); + return -EINVAL; + } + + pages_free =3D rtw_sdio_8723bs_pages_free(rtwdev, dedicated); + pages_needed =3D DIV_ROUND_UP(count, rtwdev->chip->page_size); + if (pages_needed <=3D pages_free) + return 0; + + rtw_sdio_8723bs_sync_free_txpg(rtwdev); + pages_free =3D rtw_sdio_8723bs_pages_free(rtwdev, dedicated); + } else if (rtw_chip_wcpu_8051(rtwdev)) { u32 free_txpg; =20 free_txpg =3D rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG); @@ -632,44 +778,124 @@ static int rtw_sdio_check_free_txpg(struct rtw_dev *= rtwdev, u8 queue, return 0; } =20 +static int rtw_sdio_8723bs_wait_tx_oqt(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + unsigned long deadline; + u8 free; + + if (atomic_add_unless(&rtwsdio->tx_oqt_free, -1, 0)) + return 0; + + deadline =3D jiffies + msecs_to_jiffies(RTW_SDIO_OQT_TIMEOUT_MS); + do { + free =3D rtw_read8(rtwdev, REG_SDIO_OQT_FREE_PG); + if (free) { + atomic_set(&rtwsdio->tx_oqt_free, free - 1); + return 0; + } + usleep_range(1000, 2000); + } while (time_before(jiffies, deadline)); + + return -EBUSY; +} + +static void rtw_sdio_8723bs_consume_txpg(struct rtw_dev *rtwdev, u8 queue, + unsigned int pages) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + atomic_t *dedicated; + unsigned int taken; + int free; + + dedicated =3D rtw_sdio_8723bs_free_txpg(rtwdev, queue); + if (!dedicated) + return; + + free =3D atomic_read(dedicated); + taken =3D min_t(unsigned int, pages, free > 0 ? free : 0); + atomic_sub(taken, dedicated); + + pages -=3D taken; + if (pages && atomic_sub_return(pages, &rtwsdio->free_pg_pub) < 0) + atomic_set(&rtwsdio->free_pg_pub, 0); +} + static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb, enum rtw_tx_queue_type queue) { struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + bool rtl8723bs =3D rtw_is_8723bs(rtwdev); + unsigned int orig_len =3D skb->len; + unsigned int pages; + size_t write_size; bool bus_claim; size_t txsize; u32 txaddr; int ret; =20 - txaddr =3D rtw_sdio_get_tx_addr(rtwdev, skb->len, queue); - if (!txaddr) - return -EINVAL; + if (rtl8723bs) { + txsize =3D round_up(orig_len, 4); + write_size =3D txsize > RTW_SDIO_BLOCK_SIZE ? + round_up(txsize, RTW_SDIO_BLOCK_SIZE) : txsize; + } else { + txsize =3D sdio_align_size(rtwsdio->sdio_func, orig_len); + write_size =3D txsize; + } =20 - txsize =3D sdio_align_size(rtwsdio->sdio_func, skb->len); + if (write_size > orig_len) { + unsigned int padding =3D write_size - orig_len; + + if (skb_tailroom(skb) < padding) { + ret =3D pskb_expand_head(skb, 0, + padding - skb_tailroom(skb), + GFP_KERNEL); + if (ret) + return ret; + } + skb_put_zero(skb, padding); + } + + txaddr =3D rtw_sdio_get_tx_addr(rtwdev, txsize, queue); + if (!txaddr) { + ret =3D -EINVAL; + goto out_trim; + } =20 ret =3D rtw_sdio_check_free_txpg(rtwdev, queue, txsize); if (ret) - return ret; + goto out_trim; + + if (rtl8723bs) { + ret =3D rtw_sdio_8723bs_wait_tx_oqt(rtwdev); + if (ret) + goto out_trim; + } =20 if (!IS_ALIGNED((unsigned long)skb->data, RTW_SDIO_DATA_PTR_ALIGN)) rtw_warn(rtwdev, "Got unaligned SKB in %s() for queue %u\n", __func__, queue); =20 bus_claim =3D rtw_sdio_bus_claim_needed(rtwsdio); - if (bus_claim) sdio_claim_host(rtwsdio->sdio_func); - - ret =3D sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, txsize); - + ret =3D sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, + write_size); if (bus_claim) sdio_release_host(rtwsdio->sdio_func); =20 - if (ret) + if (ret) { rtw_warn(rtwdev, "Failed to write %zu byte(s) to SDIO port 0x%08x", - txsize, txaddr); + write_size, txaddr); + } else if (rtl8723bs) { + pages =3D DIV_ROUND_UP(txsize, rtwdev->chip->page_size); + rtw_sdio_8723bs_consume_txpg(rtwdev, queue, pages); + } =20 +out_trim: + if (write_size > orig_len) + skb_trim(skb, orig_len); return ret; } =20 @@ -749,8 +975,51 @@ static int rtw_sdio_setup(struct rtw_dev *rtwdev) return 0; } =20 +/* + * Reprogram the queue page allocation if the chip came up with none. This= is + * a repair path, not part of the normal start sequence: a non-zero free p= age + * count means the allocation latched during power on and must be left alo= ne, + * and without a transmit page pool there is nothing to divide up either. + */ +static int rtw_sdio_8723bs_check_rqpn(struct rtw_dev *rtwdev) +{ + const struct rtw_chip_info *chip =3D rtwdev->chip; + struct rtw_fifo_conf *fifo =3D &rtwdev->fifo; + const struct rtw_page_table *pg_tbl; + u32 free_txpg; + u16 pubq_num; + int ret; + + free_txpg =3D rtw_read32(rtwdev, REG_SDIO_FREE_TXPG); + if (free_txpg || !fifo->acq_pg_num) + return 0; + + ret =3D rtw_sdio_8723bs_pubq_num(rtwdev, &pubq_num); + if (ret) + return ret; + + pg_tbl =3D &chip->page_table[0]; + rtw_write32(rtwdev, REG_RQPN_NPQ, + BIT_RQPN_NE(pg_tbl->nq_num, pg_tbl->exq_num)); + rtw_write32(rtwdev, REG_RQPN, + BIT_RQPN_HLP(pg_tbl->hq_num, pg_tbl->lq_num, pubq_num)); + + return 0; +} + static int rtw_sdio_start(struct rtw_dev *rtwdev) { + if (rtw_is_8723bs(rtwdev)) { + int ret =3D rtw_sdio_8723bs_check_rqpn(rtwdev); + + if (ret) + return ret; + + ret =3D rtw_sdio_8723bs_init_free_txpg(rtwdev); + if (ret) + return ret; + } + rtw_sdio_enable_rx_aggregation(rtwdev); rtw_sdio_enable_interrupt(rtwdev); =20 diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wirele= ss/realtek/rtw88/sdio.h index 457e8b02380e..5b000cb8fac2 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.h +++ b/drivers/net/wireless/realtek/rtw88/sdio.h @@ -86,6 +86,10 @@ #define REG_SDIO_OQT_FREE_PG (SDIO_LOCAL_OFFSET + 0x001E) /* Free Tx Buffer Page */ #define REG_SDIO_FREE_TXPG (SDIO_LOCAL_OFFSET + 0x0020) +#define BIT_FREE_TXPG_HIGH GENMASK(7, 0) +#define BIT_FREE_TXPG_NORMAL GENMASK(15, 8) +#define BIT_FREE_TXPG_LOW GENMASK(23, 16) +#define BIT_FREE_TXPG_PUB GENMASK(31, 24) /* HCI Current Power Mode 1 */ #define REG_SDIO_HCPWM1 (SDIO_LOCAL_OFFSET + 0x0024) /* HCI Current Power Mode 2 */ @@ -159,6 +163,12 @@ 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]; + + atomic_t free_pg_high; + atomic_t free_pg_normal; + atomic_t free_pg_low; + atomic_t free_pg_pub; + atomic_t tx_oqt_free; }; =20 extern const struct dev_pm_ops rtw_sdio_pm_ops; --=20 2.53.0 From nobody Tue Sep 29 00:30:54 2026 Received: from mta0.migadu.com (out-138.mta0.migadu.com [91.218.175.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6726A446821 for ; Fri, 14 Aug 2026 10:54:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704866; cv=none; b=iZmFqszAB6zjChSpUq6njc9We2jphhiICBil9qDmpVQxl5YchQV6NM738UYxhEUlhN7NQbULebdYZ72IsF/u0MeIM2TXFAm/WBxfUHKIi+76XLV43UnkpSwtmUHmXJBAbnrFR/7hXI1PMkC07OCkVFCzkGX0Lqvdu1QXxK+LlOw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704866; c=relaxed/simple; bh=KECh3YJHYVDUw7PpXyt/LphZqz7kb5klzSG2nuNLIeI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ns88ZaxDtr2sIFYYrWxF4EnJEuWUY7jl2EgZgUJ+qtEm5c6Wmvzfte8HeTdC7YyQ3DYZp9ddUK4ETGRxrByi7S21frBrOMj+fLYiI/s/oWqaGi6sb5jkqMtCtPFnRkPOXwIV0UlsFxOBA/YDmQPM4GzNfmTmcHakUChQ6+YfWKI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=sordBJRi; arc=none smtp.client-ip=91.218.175.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="sordBJRi" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=KECh3YJHYVDUw7PpXyt/LphZqz7kb5klzSG2nuNLIeI=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1786704856; v=1; x=1787309656; b=sordBJRirrwv9W6Ibgow+w6mOYJRgh9uuFJ+qOb3G7zTdA25Zbg4VJEV0/L3rd+pp8YXDYcM lbtAPimALLI6Bg7v77VqOpmlVopfAz0+ChhIUJSTQponrGkjPYgsyaLLx0BrfWQuCX1/SzB4/6k I8O2wgR5VU8KOQgxZ06/ayQQ= X-Envelope-To: linux-kernel@vger.kernel.org Received: from kali (46.239.4.165) by smtp.migadu.com with ESMTPS id d16dd8ee00b75cca; Fri, 14 Aug 2026 10:54:16 +0000 X-Migadu-Flow: FLOW_OUT From: luka.gejak@linux.dev To: Ping-Ke Shih , linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Michael Straube , Bitterblue Smith , Peter Robinson , Hans de Goede , Luka Gejak Subject: [PATCH v5 5/6] wifi: rtw88: sdio: set up RX aggregation and interrupts for RTL8723BS Date: Fri, 14 Aug 2026 10:53:25 +0000 Message-ID: <20260814105327.6687-6-luka.gejak@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260814105327.6687-1-luka.gejak@linux.dev> References: <20260814105327.6687-1-luka.gejak@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Luka Gejak Enable the existing RX aggregation setup for this chip and select the larger DMA burst count it needs. The RTL8723BS does not raise CPWM1, so leave that source out of its interrupt mask, and set the SDIO TX control bit the vendor driver uses to have transfers always recognised. The chip was also seen to keep raising the interrupt after resume when undefined status bits were written back on acknowledgment, so acknowledge only the bits this driver defines. That observation dates from bring up and I cannot re-measure it: the test machine only offers s2idle and does not reliably return from it, so the resume path is not exercised here. The change is scoped to this chip; the other SDIO parts keep writing the status word back unchanged. Signed-off-by: Luka Gejak Acked-by: Ping-Ke Shih --- drivers/net/wireless/realtek/rtw88/sdio.c | 28 ++++++++++++++++++++++- drivers/net/wireless/realtek/rtw88/sdio.h | 9 ++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wirele= ss/realtek/rtw88/sdio.c index a8209b9acefd..eaec776dd943 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -903,7 +903,11 @@ static void rtw_sdio_init(struct rtw_dev *rtwdev) { struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; =20 - rtwsdio->irq_mask =3D REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1; + if (rtw_is_8723bs(rtwdev)) + rtwsdio->irq_mask =3D REG_SDIO_HIMR_RX_REQUEST; + else + rtwsdio->irq_mask =3D REG_SDIO_HIMR_RX_REQUEST | + REG_SDIO_HIMR_CPWM1; } =20 static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev) @@ -911,6 +915,7 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_d= ev *rtwdev) u8 size, timeout; =20 switch (rtwdev->chip->id) { + case RTW_CHIP_TYPE_8723B: case RTW_CHIP_TYPE_8703B: case RTW_CHIP_TYPE_8821A: case RTW_CHIP_TYPE_8812A: @@ -938,6 +943,8 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_d= ev *rtwdev) FIELD_PREP(BIT_DMA_AGG_TO_V1, timeout)); =20 rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE); + if (rtw_is_8723bs(rtwdev)) + rtw_write8_mask(rtwdev, REG_RXDMA_MODE, BIT_DMA_BURST_CNT, 0x3); } =20 static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev) @@ -1009,6 +1016,8 @@ static int rtw_sdio_8723bs_check_rqpn(struct rtw_dev = *rtwdev) =20 static int rtw_sdio_start(struct rtw_dev *rtwdev) { + u32 clear; + if (rtw_is_8723bs(rtwdev)) { int ret =3D rtw_sdio_8723bs_check_rqpn(rtwdev); =20 @@ -1021,6 +1030,13 @@ static int rtw_sdio_start(struct rtw_dev *rtwdev) } =20 rtw_sdio_enable_rx_aggregation(rtwdev); + + if (rtw_is_8723bs(rtwdev)) { + clear =3D rtw_read32(rtwdev, REG_SDIO_HISR) & RTW_SDIO_HISR_CLEAR_MASK; + if (clear) + rtw_write32(rtwdev, REG_SDIO_HISR, clear); + } + rtw_sdio_enable_interrupt(rtwdev); =20 return 0; @@ -1100,6 +1116,8 @@ static void rtw_sdio_interface_cfg(struct rtw_dev *rt= wdev) =20 val =3D rtw_read32(rtwdev, REG_SDIO_TX_CTRL); val &=3D 0xfff8; + if (rtw_is_8723bs(rtwdev)) + val |=3D BIT_SDIO_TX_CTRL_ALWAYS_RECOGNIZE; rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val); } =20 @@ -1364,6 +1382,14 @@ static void rtw_sdio_handle_interrupt(struct sdio_fu= nc *sdio_func) rtw_sdio_rx_isr(rtwdev); } =20 + /* + * RTL8723BS keeps raising the interrupt after resume if undefined + * status bits are written back, so acknowledge only the bits this + * driver defines. Other chips keep the existing behaviour. + */ + if (rtw_is_8723bs(rtwdev)) + hisr &=3D RTW_SDIO_HISR_CLEAR_MASK; + rtw_write32(rtwdev, REG_SDIO_HISR, hisr); =20 rtwsdio->irq_thread =3D NULL; diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wirele= ss/realtek/rtw88/sdio.h index 5b000cb8fac2..7474a7511811 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.h +++ b/drivers/net/wireless/realtek/rtw88/sdio.h @@ -22,6 +22,7 @@ =20 /* SDIO Tx Control */ #define REG_SDIO_TX_CTRL (SDIO_LOCAL_OFFSET + 0x0000) +#define BIT_SDIO_TX_CTRL_ALWAYS_RECOGNIZE BIT(4) =20 /*SDIO status timeout*/ #define REG_SDIO_TIMEOUT (SDIO_LOCAL_OFFSET + 0x0002) @@ -77,6 +78,14 @@ /* the following two are RTL8188 SDIO Specific */ #define REG_SDIO_HISR_MCU_ERR BIT(28) #define REG_SDIO_HISR_TSF_BIT32_TOGGLE BIT(29) +#define RTW_SDIO_HISR_CLEAR_MASK \ + (REG_SDIO_HISR_TXERR | REG_SDIO_HISR_RXERR | \ + REG_SDIO_HISR_TXFOVW | REG_SDIO_HISR_RXFOVW | \ + REG_SDIO_HISR_TXBCNOK | REG_SDIO_HISR_TXBCNERR | \ + REG_SDIO_HISR_C2HCMD | REG_SDIO_HISR_CPWM1 | \ + REG_SDIO_HISR_CPWM2 | REG_SDIO_HISR_HSISR_IND | \ + REG_SDIO_HISR_GTINT3_IND | REG_SDIO_HISR_GTINT4_IND | \ + REG_SDIO_HISR_PSTIMEOUT | REG_SDIO_HISR_OCPINT) =20 /* HCI Current Power Mode */ #define REG_SDIO_HCPWM (SDIO_LOCAL_OFFSET + 0x0019) --=20 2.53.0 From nobody Tue Sep 29 00:30:54 2026 Received: from mta1.migadu.com (out-213.mta1.migadu.com [95.215.58.213]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 82AE945D1A4 for ; Fri, 14 Aug 2026 10:54:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.213 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704873; cv=none; b=WRz2lPzB0pAMpq9l0OYz4fTJIuwQEQUCDUmo556NGT2xCf1LVX4XW46Q6zVZncI+QgG/IgvoiozgErgs1E9Kqm32Po/vZNkPKrQ3QoygnX6ypx1hpntseL0sX5yYR9sGLm904nVcGNHxxrvg9GE5c03ErCDs5ZYqTDKoi6wKzhk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786704873; c=relaxed/simple; bh=L9S47wt5zxwhgn2O1Ux98Q5TcY716GBZIxS+Kxt/JuU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uVBa2ThIPn5r+MpWPPX2vw//Afv+XStoVrWkNI6HmzVtXTDr3bWz0GP6VXv8bDTcxNETNO1DRKocg1fdGL5ZinFDsVgTbOFnQbU58NQyI7xL+Bq309oZClz40PojsJUwbG9oGmYTkzeCE8lNo6PsD3C13YST3qVf55+kGktEHo0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=KI8s8uYn; arc=none smtp.client-ip=95.215.58.213 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="KI8s8uYn" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=L9S47wt5zxwhgn2O1Ux98Q5TcY716GBZIxS+Kxt/JuU=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1786704861; v=1; x=1787309661; b=KI8s8uYnutAr3j8/Ir4wjWnjG7JPZ1SoigdwxyYailc70Ta0qi4WTkGHP+NsaKBx4CKp311m rD+xi6fcxVUzGexfhfTyqhsNytG/qhdLAyLjYZcjozw+w/5wJTu5yWG2XarPbsT7AYV88jjmjCh xPGs7in2JMQieQ5btWh4k83s= X-Envelope-To: linux-kernel@vger.kernel.org Received: from kali (46.239.4.165) by smtp.migadu.com with ESMTPS id 50f879c32b0c2713; Fri, 14 Aug 2026 10:54:21 +0000 X-Migadu-Flow: FLOW_OUT From: luka.gejak@linux.dev To: Ping-Ke Shih , linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Michael Straube , Bitterblue Smith , Peter Robinson , Hans de Goede , Luka Gejak Subject: [PATCH v5 6/6] wifi: rtw88: sdio: add TX back-pressure and retry on page starvation Date: Fri, 14 Aug 2026 10:53:26 +0000 Message-ID: <20260814105327.6687-7-luka.gejak@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260814105327.6687-1-luka.gejak@linux.dev> References: <20260814105327.6687-1-luka.gejak@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Luka Gejak 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 a transfer cannot be completed 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 and re-arm it when a transfer fails for a reason that can clear on its own, so it is retried rather than the AC abandoned, and cancel the work on teardown. Retrying matters once the queue can be stopped. A stopped queue is handed no further frames, so nothing else would kick the worker, and the AC would stay stopped for good with the link still up and receive unaffected. The two retried cases, a transmit page or output queue shortage and a failed skb expansion, are also the two that fail silently; the rest are logged where they happen, so they are visible rather than an unexplained hang, and they keep the existing behaviour rather than being retried indefinitely. 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 --- drivers/net/wireless/realtek/rtw88/sdio.c | 139 ++++++++++++++++++++-- drivers/net/wireless/realtek/rtw88/sdio.h | 3 +- 2 files changed, 131 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wirele= ss/realtek/rtw88/sdio.c index eaec776dd943..1de104870572 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -22,6 +22,16 @@ #define RTW_SDIO_INDIRECT_RW_RETRIES 50 #define RTW_SDIO_OQT_TIMEOUT_MS 1000 =20 +/* + * 8723BS SDIO TX FIFO back-pressure watermarks: stop the mac80211 queue o= nce + * the per-AC software FIFO fills past the high watermark, and wake it fro= m the + * TX drain path once it falls back to the low one. Bounds the queueing la= tency + * that otherwise causes uplink bufferbloat / congestion collapse. + */ +#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); @@ -1100,7 +1110,11 @@ static void rtw_sdio_tx_kick_off(struct rtw_dev *rtw= dev) { struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; =20 - queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work); + /* + * A retry may already be pending with a delay; re-arm it so a newly + * queued frame is not held back by it. + */ + mod_delayed_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work, 0); } =20 static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter) @@ -1209,6 +1223,50 @@ static int rtw_sdio_write_data_h2c(struct rtw_dev *r= twdev, u8 *buf, u32 size) return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_H2C); } =20 +/* + * Back-pressure on the data ACs (BK/BE/VI/VO): once the software FIFO fil= ls + * past the high watermark, stop the corresponding mac80211 queue so it st= ops + * handing frames down, which bounds the queueing latency. The queue is wo= ken + * again from the TX drain path once the FIFO falls back to the low waterm= ark. + */ +static void rtw_sdio_8723bs_stop_tx_queue(struct rtw_dev *rtwdev, + enum rtw_tx_queue_type queue, + u16 q_map) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + + if (!rtw_is_8723bs(rtwdev) || queue >=3D RTW_TX_QUEUE_BCN) + return; + + if (rtwsdio->tx_queue_stopped[queue]) + return; + + if (skb_queue_len(&rtwsdio->tx_queue[queue]) < RTW_SDIO_TX_FIFO_HIWATER) + return; + + rtwsdio->tx_queue_stopped[queue] =3D true; + ieee80211_stop_queue(rtwdev->hw, q_map); +} + +static void rtw_sdio_8723bs_wake_tx_queue(struct rtw_dev *rtwdev, + enum rtw_tx_queue_type queue, + u16 q_map) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + + if (!rtw_is_8723bs(rtwdev) || queue >=3D RTW_TX_QUEUE_BCN) + return; + + if (!rtwsdio->tx_queue_stopped[queue]) + return; + + if (skb_queue_len(&rtwsdio->tx_queue[queue]) > RTW_SDIO_TX_FIFO_LOWATER) + return; + + rtwsdio->tx_queue_stopped[queue] =3D false; + ieee80211_wake_queue(rtwdev->hw, q_map); +} + static int rtw_sdio_tx_write(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb) @@ -1224,6 +1282,8 @@ static int rtw_sdio_tx_write(struct rtw_dev *rtwdev, =20 skb_queue_tail(&rtwsdio->tx_queue[queue], skb); =20 + rtw_sdio_8723bs_stop_tx_queue(rtwdev, queue, skb_get_queue_mapping(skb)); + return 0; } =20 @@ -1526,43 +1586,99 @@ static void rtw_sdio_indicate_tx_status(struct rtw_= dev *rtwdev, ieee80211_tx_status_irqsafe(hw, skb); } =20 -static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev, - enum rtw_tx_queue_type queue) +/* + * Send one frame from @queue. Returns 0 when a frame was written, 1 when = the + * queue was empty and a negative errno when the write failed, in which ca= se + * the frame is put back at the head of the queue. + */ +static int rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev, + enum rtw_tx_queue_type queue) { struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; struct sk_buff *skb; + u16 q_map; int ret; =20 skb =3D skb_dequeue(&rtwsdio->tx_queue[queue]); if (!skb) - return; + return 1; =20 + q_map =3D skb_get_queue_mapping(skb); ret =3D rtw_sdio_write_port(rtwdev, skb, queue); if (ret) { skb_queue_head(&rtwsdio->tx_queue[queue], skb); - return; + return ret; } =20 rtw_sdio_indicate_tx_status(rtwdev, skb); + + rtw_sdio_8723bs_wake_tx_queue(rtwdev, queue, q_map); + + return 0; +} + +static void rtw_sdio_reschedule_tx_work(struct rtw_dev *rtwdev, + struct rtw_sdio_work_data *work_data, + unsigned long delay) +{ + struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + + queue_delayed_work(rtwsdio->txwq, &work_data->work, delay); } =20 static void rtw_sdio_tx_handler(struct work_struct *work) { struct rtw_sdio_work_data *work_data =3D - 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; - int limit, queue; + int limit, queue, ret; + bool rtl8723bs; =20 rtwdev =3D work_data->rtwdev; rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; + rtl8723bs =3D rtw_is_8723bs(rtwdev); =20 if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) rtw_sdio_deep_ps_leave(rtwdev); =20 for (queue =3D RTK_MAX_TX_QUEUE_NUM - 1; queue >=3D 0; queue--) { for (limit =3D 0; limit < 1000; limit++) { - rtw_sdio_process_tx_queue(rtwdev, queue); + ret =3D rtw_sdio_process_tx_queue(rtwdev, queue); + if (ret > 0) + break; + + if (ret < 0) { + /* + * A page or output queue shortage and a failed + * skb expansion are both transient, and the + * frame is still queued, so come back for it. + * That matters once the queue can be stopped: + * a stopped queue is handed no further frames, + * so nothing else would kick this work item and + * the queue would stay stopped for good. The + * remaining errors cannot succeed on a retry + * and each log where they happen. + */ + if (rtl8723bs && + (ret =3D=3D -EBUSY || ret =3D=3D -ENOMEM)) { + rtw_sdio_reschedule_tx_work(rtwdev, work_data, + RTW_SDIO_TX_RETRY_DELAY); + return; + } + break; + } + + /* + * Restart from the highest priority queue after every + * management frame so the join sequence is not held up + * behind a data backlog. + */ + if (rtl8723bs && queue =3D=3D RTW_TX_QUEUE_MGMT) { + rtw_sdio_reschedule_tx_work(rtwdev, work_data, 0); + return; + } =20 if (skb_queue_empty(&rtwsdio->tx_queue[queue])) break; @@ -1589,14 +1705,16 @@ static int rtw_sdio_init_tx(struct rtw_dev *rtwdev) return -ENOMEM; } =20 - for (i =3D 0; i < RTK_MAX_TX_QUEUE_NUM; i++) + for (i =3D 0; i < RTK_MAX_TX_QUEUE_NUM; i++) { skb_queue_head_init(&rtwsdio->tx_queue[i]); + rtwsdio->tx_queue_stopped[i] =3D false; + } rtwsdio->tx_handler_data =3D kmalloc_obj(*rtwsdio->tx_handler_data); if (!rtwsdio->tx_handler_data) goto err_destroy_wq; =20 rtwsdio->tx_handler_data->rtwdev =3D rtwdev; - INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler); + INIT_DELAYED_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler); =20 return 0; =20 @@ -1610,6 +1728,7 @@ static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev) struct rtw_sdio *rtwsdio =3D (struct rtw_sdio *)rtwdev->priv; int i; =20 + cancel_delayed_work_sync(&rtwsdio->tx_handler_data->work); destroy_workqueue(rtwsdio->txwq); kfree(rtwsdio->tx_handler_data); =20 diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wirele= ss/realtek/rtw88/sdio.h index 7474a7511811..cec3dd1c4b2e 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.h +++ b/drivers/net/wireless/realtek/rtw88/sdio.h @@ -156,7 +156,7 @@ struct rtw_sdio_tx_data { }; =20 struct rtw_sdio_work_data { - struct work_struct work; + struct delayed_work work; struct rtw_dev *rtwdev; }; =20 @@ -172,6 +172,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 tx_queue_stopped[RTK_MAX_TX_QUEUE_NUM]; =20 atomic_t free_pg_high; atomic_t free_pg_normal; --=20 2.53.0