net: phy: Ensure state transitions are processed from phy_stop()

[ Upstream commit 4203d84032 ]

In the phy_disconnect() -> phy_stop() path, we will be forcibly setting
the PHY state machine to PHY_HALTED. This invalidates the old_state !=
phydev->state condition in phy_state_machine() such that we will neither
display the state change for debugging, nor will we invoke the
link_change_notify() callback.

Factor the code by introducing phy_process_state_change(), and ensure
that we process the state change from phy_stop() as well.

Fixes: 5c5f626bca ("net: phy: improve handling link_change_notify callback")
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Florian Fainelli 2023-03-16 13:33:24 -07:00 committed by Greg Kroah-Hartman
parent 9c515f3290
commit 5603aeb818

View file

@ -57,6 +57,18 @@ static const char *phy_state_to_str(enum phy_state st)
return NULL;
}
static void phy_process_state_change(struct phy_device *phydev,
enum phy_state old_state)
{
if (old_state != phydev->state) {
phydev_dbg(phydev, "PHY state change %s -> %s\n",
phy_state_to_str(old_state),
phy_state_to_str(phydev->state));
if (phydev->drv && phydev->drv->link_change_notify)
phydev->drv->link_change_notify(phydev);
}
}
static void phy_link_up(struct phy_device *phydev)
{
phydev->phy_link_change(phydev, true);
@ -1094,6 +1106,7 @@ EXPORT_SYMBOL(phy_free_interrupt);
void phy_stop(struct phy_device *phydev)
{
struct net_device *dev = phydev->attached_dev;
enum phy_state old_state;
if (!phy_is_started(phydev) && phydev->state != PHY_DOWN) {
WARN(1, "called from state %s\n",
@ -1102,6 +1115,7 @@ void phy_stop(struct phy_device *phydev)
}
mutex_lock(&phydev->lock);
old_state = phydev->state;
if (phydev->state == PHY_CABLETEST) {
phy_abort_cable_test(phydev);
@ -1112,6 +1126,7 @@ void phy_stop(struct phy_device *phydev)
sfp_upstream_stop(phydev->sfp_bus);
phydev->state = PHY_HALTED;
phy_process_state_change(phydev, old_state);
mutex_unlock(&phydev->lock);
@ -1229,13 +1244,7 @@ void phy_state_machine(struct work_struct *work)
if (err < 0)
phy_error(phydev);
if (old_state != phydev->state) {
phydev_dbg(phydev, "PHY state change %s -> %s\n",
phy_state_to_str(old_state),
phy_state_to_str(phydev->state));
if (phydev->drv && phydev->drv->link_change_notify)
phydev->drv->link_change_notify(phydev);
}
phy_process_state_change(phydev, old_state);
/* Only re-schedule a PHY state machine change if we are polling the
* PHY, if PHY_MAC_INTERRUPT is set, then we will be moving