[PATCH rtw-next] wifi: rtl8xxxu: isolate the USB register buffer from CPU state
From: Kim Wooseok via B4 Relay
Date: Fri Oct 09 2026 - 18:39:51 EST
From: Kim Wooseok <5mghybrid@xxxxxxxxx>
An RTL8192EU failed to reconnect on a Raspberry Pi after an AP power
cycle. RF_MODE contained a corrupted value, and restoring its initialized
value restored reception.
The register access helpers pass usb_buf directly to usb_control_msg(),
but the buffer is embedded next to usb_buf_mutex. On the affected Pi,
it shares a cache line with the mutex's waiter state. A contending task
updates that state while the USB transfer uses the buffer. This violates
the cache-line isolation required for non-coherent DMA.
Allocate the buffer once in probe and reuse it, avoiding an allocation
for every register access. Keep the existing register access helpers and
mutex protection. Free the buffer on probe failure and after power_off()
in disconnect, since shutdown still uses the register access helpers.
Fixes: 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Kim Wooseok <5mghybrid@xxxxxxxxx>
---
I tested the separate-buffer approach on an RTL8192EU connected to a
Raspberry Pi 5, using the existing Linux 6.18.34 kernel. While repeatedly
requesting scans, wireless ping traffic kept receiving replies and the
RF_MODE and HSSI register values remained stable.
The submission is based on rtw-next. An ARM64 build of the rtl8xxxu
driver with W=1 completed without warnings, and sparse reported no
diagnostics in core.c.
---
drivers/net/wireless/realtek/rtl8xxxu/core.c | 35 ++++++++++++++++--------
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 2 +-
2 files changed, 24 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 3d0c22db3..604bb1e5b 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -622,9 +622,9 @@ u8 rtl8xxxu_read8(struct rtl8xxxu_priv *priv, u16 addr)
mutex_lock(&priv->usb_buf_mutex);
len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
- addr, 0, &priv->usb_buf.val8, sizeof(u8),
+ addr, 0, &priv->usb_buf->val8, sizeof(u8),
RTW_USB_CONTROL_MSG_TIMEOUT);
- data = priv->usb_buf.val8;
+ data = priv->usb_buf->val8;
mutex_unlock(&priv->usb_buf_mutex);
if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
@@ -645,9 +645,9 @@ u16 rtl8xxxu_read16(struct rtl8xxxu_priv *priv, u16 addr)
mutex_lock(&priv->usb_buf_mutex);
len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
- addr, 0, &priv->usb_buf.val16, sizeof(u16),
+ addr, 0, &priv->usb_buf->val16, sizeof(u16),
RTW_USB_CONTROL_MSG_TIMEOUT);
- data = le16_to_cpu(priv->usb_buf.val16);
+ data = le16_to_cpu(priv->usb_buf->val16);
mutex_unlock(&priv->usb_buf_mutex);
if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
@@ -668,9 +668,9 @@ u32 rtl8xxxu_read32(struct rtl8xxxu_priv *priv, u16 addr)
mutex_lock(&priv->usb_buf_mutex);
len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
- addr, 0, &priv->usb_buf.val32, sizeof(u32),
+ addr, 0, &priv->usb_buf->val32, sizeof(u32),
RTW_USB_CONTROL_MSG_TIMEOUT);
- data = le32_to_cpu(priv->usb_buf.val32);
+ data = le32_to_cpu(priv->usb_buf->val32);
mutex_unlock(&priv->usb_buf_mutex);
if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
@@ -688,10 +688,10 @@ int rtl8xxxu_write8(struct rtl8xxxu_priv *priv, u16 addr, u8 val)
addr |= 0x8000;
mutex_lock(&priv->usb_buf_mutex);
- priv->usb_buf.val8 = val;
+ priv->usb_buf->val8 = val;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
- addr, 0, &priv->usb_buf.val8, sizeof(u8),
+ addr, 0, &priv->usb_buf->val8, sizeof(u8),
RTW_USB_CONTROL_MSG_TIMEOUT);
mutex_unlock(&priv->usb_buf_mutex);
@@ -711,10 +711,10 @@ int rtl8xxxu_write16(struct rtl8xxxu_priv *priv, u16 addr, u16 val)
addr |= 0x8000;
mutex_lock(&priv->usb_buf_mutex);
- priv->usb_buf.val16 = cpu_to_le16(val);
+ priv->usb_buf->val16 = cpu_to_le16(val);
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
- addr, 0, &priv->usb_buf.val16, sizeof(u16),
+ addr, 0, &priv->usb_buf->val16, sizeof(u16),
RTW_USB_CONTROL_MSG_TIMEOUT);
mutex_unlock(&priv->usb_buf_mutex);
@@ -733,10 +733,10 @@ int rtl8xxxu_write32(struct rtl8xxxu_priv *priv, u16 addr, u32 val)
addr |= 0x8000;
mutex_lock(&priv->usb_buf_mutex);
- priv->usb_buf.val32 = cpu_to_le32(val);
+ priv->usb_buf->val32 = cpu_to_le32(val);
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
- addr, 0, &priv->usb_buf.val32, sizeof(u32),
+ addr, 0, &priv->usb_buf->val32, sizeof(u32),
RTW_USB_CONTROL_MSG_TIMEOUT);
mutex_unlock(&priv->usb_buf_mutex);
@@ -7851,6 +7851,13 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
usb_set_intfdata(interface, hw);
+ /* Keep the USB register buffer separate from driver state. */
+ priv->usb_buf = kzalloc_obj(*priv->usb_buf);
+ if (!priv->usb_buf) {
+ ret = -ENOMEM;
+ goto err_set_intfdata;
+ }
+
ret = rtl8xxxu_parse_usb(priv, interface);
if (ret)
goto err_set_intfdata;
@@ -7971,7 +7978,9 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
err_set_intfdata:
usb_set_intfdata(interface, NULL);
+ kfree(priv->usb_buf);
kfree(priv->fw_data);
+
mutex_destroy(&priv->usb_buf_mutex);
mutex_destroy(&priv->syson_indirect_access_mutex);
mutex_destroy(&priv->h2c_mutex);
@@ -7999,7 +8008,9 @@ static void rtl8xxxu_disconnect(struct usb_interface *interface)
dev_info(&priv->udev->dev, "disconnecting\n");
+ kfree(priv->usb_buf);
kfree(priv->fw_data);
+
mutex_destroy(&priv->usb_buf_mutex);
mutex_destroy(&priv->syson_indirect_access_mutex);
mutex_destroy(&priv->h2c_mutex);
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index c6953051d..8cbadcf71 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1891,7 +1891,7 @@ struct rtl8xxxu_priv {
__le32 val32;
__le16 val16;
u8 val8;
- } usb_buf;
+ } *usb_buf;
union {
u8 raw[EFUSE_MAP_LEN];
struct rtl8723au_efuse efuse8723;
--
2.53.0