ramips: mt7530 swconfig: fix race condition in register access
authorDENG Qingfang <dqfext@gmail.com>
Sat, 3 Apr 2021 14:59:15 +0000 (22:59 +0800)
committerChuanhong Guo <gch981213@gmail.com>
Wed, 14 Apr 2021 16:00:54 +0000 (00:00 +0800)
The mt7530_{r,w}32 operation over MDIO uses 3 mdiobus operations and
does not hold a lock, which causes a race condition when multiple
threads try to access a register, they may get unexpected results.

To avoid this, handle the MDIO lock manually, and use the unlocked
__mdiobus_{read,write} in the critical section.

This fixes the "Ghost VLAN" artifact[1] in MT7530/7621 when the VLAN
operation and the swconfig LED link status poll race between each other.

[1] https://forum.openwrt.org/t/mysterious-vlan-ids-on-mt7621-device/64495

Signed-off-by: DENG Qingfang <dqfext@gmail.com>
target/linux/ramips/files/drivers/net/ethernet/ralink/mt7530.c

index be1b8a6d8ed0d51a536bd37bdc471db9cdb34260..b4632d39904fcb682eac2e4429ec574171658ac7 100644 (file)
@@ -290,9 +290,11 @@ mt7530_r32(struct mt7530_priv *priv, u32 reg)
        if (priv->bus) {
                u16 high, low;
 
-               mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-               low = mdiobus_read(priv->bus, 0x1f, (reg >> 2) & 0xf);
-               high = mdiobus_read(priv->bus, 0x1f, 0x10);
+               mutex_lock(&priv->bus->mdio_lock);
+               __mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+               low = __mdiobus_read(priv->bus, 0x1f, (reg >> 2) & 0xf);
+               high = __mdiobus_read(priv->bus, 0x1f, 0x10);
+               mutex_unlock(&priv->bus->mdio_lock);
 
                return (high << 16) | (low & 0xffff);
        }
@@ -307,9 +309,11 @@ static void
 mt7530_w32(struct mt7530_priv *priv, u32 reg, u32 val)
 {
        if (priv->bus) {
-               mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-               mdiobus_write(priv->bus, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
-               mdiobus_write(priv->bus, 0x1f, 0x10, val >> 16);
+               mutex_lock(&priv->bus->mdio_lock);
+               __mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+               __mdiobus_write(priv->bus, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
+               __mdiobus_write(priv->bus, 0x1f, 0x10, val >> 16);
+               mutex_unlock(&priv->bus->mdio_lock);
                return;
        }