From: "Ivan T. Ivanov" iivanov@suse.de
stable inclusion from linux-4.19.205 commit edf5cef7be1aeae349a16b09857674f71759ce59
--------------------------------
[ Upstream commit 6b67d4d63edece1033972214704c04f36c5be89a ]
Currently phy_device state could be left in inconsistent state shown by following alert message[1]. This is because phy_read_status could be called concurrently from lan78xx_delayedwork, phy_state_machine and __ethtool_get_link. Fix this by making sure that phy_device state is updated atomically.
[1] lan78xx 1-1.1.1:1.0 eth0: No phy led trigger registered for speed(-1)
Signed-off-by: Ivan T. Ivanov iivanov@suse.de Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/net/usb/lan78xx.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 5bd07cdb3e6e2..ac5f72077b267 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1172,7 +1172,7 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) { struct phy_device *phydev = dev->net->phydev; struct ethtool_link_ksettings ecmd; - int ladv, radv, ret; + int ladv, radv, ret, link; u32 buf;
/* clear LAN78xx interrupt status */ @@ -1180,9 +1180,12 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) if (unlikely(ret < 0)) return -EIO;
+ mutex_lock(&phydev->lock); phy_read_status(phydev); + link = phydev->link; + mutex_unlock(&phydev->lock);
- if (!phydev->link && dev->link_on) { + if (!link && dev->link_on) { dev->link_on = false;
/* reset MAC */ @@ -1195,7 +1198,7 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) return -EIO;
del_timer(&dev->stat_monitor); - } else if (phydev->link && !dev->link_on) { + } else if (link && !dev->link_on) { dev->link_on = true;
phy_ethtool_ksettings_get(phydev, &ecmd); @@ -1485,9 +1488,14 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
static u32 lan78xx_get_link(struct net_device *net) { + u32 link; + + mutex_lock(&net->phydev->lock); phy_read_status(net->phydev); + link = net->phydev->link; + mutex_unlock(&net->phydev->lock);
- return net->phydev->link; + return link; }
static void lan78xx_get_drvinfo(struct net_device *net,