From 11e122cbe90ea5079622fb57bdf2dffe8cf68e57 Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Fri, 14 Aug 2015 12:23:40 +0800 Subject: net: phy: fix PHY_RUNNING in phy_state_machine Currently, if phy state is PHY_RUNNING, we always register a CHANGE when phy works in polling or interrupt ignored, this will make the adjust_link being called even the phy link did Not changed. checking the phy link to make sure the link did changed before we register a CHANGE, if link did not changed, we do nothing. Signed-off-by: Shaohui Xie Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index b2197b506acb..1e1fbb049ec6 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -811,6 +811,7 @@ void phy_state_machine(struct work_struct *work) bool needs_aneg = false, do_suspend = false; enum phy_state old_state; int err = 0; + int old_link; mutex_lock(&phydev->lock); @@ -896,11 +897,18 @@ void phy_state_machine(struct work_struct *work) phydev->adjust_link(phydev->attached_dev); break; case PHY_RUNNING: - /* Only register a CHANGE if we are - * polling or ignoring interrupts + /* Only register a CHANGE if we are polling or ignoring + * interrupts and link changed since latest checking. */ - if (!phy_interrupt_is_valid(phydev)) - phydev->state = PHY_CHANGELINK; + if (!phy_interrupt_is_valid(phydev)) { + old_link = phydev->link; + err = phy_read_status(phydev); + if (err) + break; + + if (old_link != phydev->link) + phydev->state = PHY_CHANGELINK; + } break; case PHY_CHANGELINK: err = phy_read_status(phydev); -- cgit v1.2.3 From 05a7f582be961824d62a7f4a817f3783148b5f8a Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Aug 2015 09:49:47 +0100 Subject: net: phy: add locking to phy_read_mmd_indirect()/phy_write_mmd_indirect() The phy layer is missing locking for the above two functions - it has been observed that two threads (userspace and the phy worker thread) can race, entering the bus ->write or ->read functions simultaneously. This causes the FEC driver to initialise a completion while another thread is waiting on it or while the interrupt is calling complete() on it, which causes spinlock unlock-without-lock, spinlock lockups, and completion timeouts. Fixes: a59a4d192 ("phy: add the EEE support and the way to access to the MMD registers.") Fixes: 0c1d77dfb ("net: libphy: Add phy specific function to access mmd phy registers") Signed-off-by: Russell King Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 1e1fbb049ec6..34fe339f4e80 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1038,10 +1038,14 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int value = -1; if (phydrv->read_mmd_indirect == NULL) { - mmd_phy_indirect(phydev->bus, prtad, devad, addr); + struct mii_bus *bus = phydev->bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); /* Read the content of the MMD's selected register */ - value = phydev->bus->read(phydev->bus, addr, MII_MMD_DATA); + value = bus->read(bus, addr, MII_MMD_DATA); + mutex_unlock(&bus->mdio_lock); } else { value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); } @@ -1071,10 +1075,14 @@ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, struct phy_driver *phydrv = phydev->drv; if (phydrv->write_mmd_indirect == NULL) { - mmd_phy_indirect(phydev->bus, prtad, devad, addr); + struct mii_bus *bus = phydev->bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); /* Write the data into MMD's selected register */ - phydev->bus->write(phydev->bus, addr, MII_MMD_DATA, data); + bus->write(bus, addr, MII_MMD_DATA, data); + mutex_unlock(&bus->mdio_lock); } else { phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); } -- cgit v1.2.3