[PATCH net-next v3 3/3] net: dsa: qca8k: fail mgmt Ethernet MDIO access on busy wait errors

From: Yongzhao Chen

Date: Sat Oct 03 2026 - 13:29:43 EST


qca8k_phy_eth_command() polls MASTER_CTRL over management Ethernet until
BUSY clears, but only bails out when the poll timed out and the last
poll request also failed. If every request succeeds while BUSY stays
set, the -ETIMEDOUT is dropped. A read then returns MASTER_CTRL data
from a transaction that has not completed, and
qca8k_internal_mdio_read() does not fall back to the MDIO bus.

A failed poll request does not stop the loop either.
qca8k_phy_eth_busy_wait() leaves the value unchanged on failure, so the
BUSY test then uses the previous value, or an uninitialized one if the
first request fails.

Stop polling when a request fails and return the poll error or timeout.
The poll-error path also leaked read_skb, which is only consumed when the
read request is sent; free it on this path.

The Sashiko review of v1 identified the dropped timeout. A userspace
model of the driver's functions reproduces it and the poll-error path
with scripted BUSY responses and failed poll requests; these cases pass
after this change.

The polling loop comes from commit 2cd548566384 ("net: dsa: qca8k: add
support for phy read/write with mgmt Ethernet").

Signed-off-by: Yongzhao Chen <yongzhao.derek@xxxxxxxxx>
Assisted-by: LLM
---
v3: Target net-next and drop the Fixes: tag. The commit that
introduced the problem is named in the text.

v2: new patch, addressing the Sashiko review of v1:
https://lore.kernel.org/netdev/179054715863.3145.10179961093285192493@xxxxxxxxxx/

drivers/net/dsa/qca/qca8k-8xxx.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index 2708430413d..07227ae8bc0 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -728,12 +728,15 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
}

ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1,
- !(val & QCA8K_MDIO_MASTER_BUSY), 0,
+ ret1 < 0 || !(val & QCA8K_MDIO_MASTER_BUSY), 0,
QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
mgmt_eth_data, read_skb, &val);

- if (ret < 0 && ret1 < 0) {
+ if (ret1 < 0)
ret = ret1;
+
+ if (ret < 0) {
+ kfree_skb(read_skb);
goto exit;
}

--
2.43.0