Skip to content

Commit 56fe63b

Browse files
vladimirolteangregkh
authored andcommitted
net: phylink: add lock for serializing concurrent pl->phydev writes with resolver
[ Upstream commit 0ba5b2f ] Currently phylink_resolve() protects itself against concurrent phylink_bringup_phy() or phylink_disconnect_phy() calls which modify pl->phydev by relying on pl->state_mutex. The problem is that in phylink_resolve(), pl->state_mutex is in a lock inversion state with pl->phydev->lock. So pl->phydev->lock needs to be acquired prior to pl->state_mutex. But that requires dereferencing pl->phydev in the first place, and without pl->state_mutex, that is racy. Hence the reason for the extra lock. Currently it is redundant, but it will serve a functional purpose once mutex_lock(&phy->lock) will be moved outside of the mutex_lock(&pl->state_mutex) section. Another alternative considered would have been to let phylink_resolve() acquire the rtnl_mutex, which is also held when phylink_bringup_phy() and phylink_disconnect_phy() are called. But since phylink_disconnect_phy() runs under rtnl_lock(), it would deadlock with phylink_resolve() when calling flush_work(&pl->resolve). Additionally, it would have been undesirable because it would have unnecessarily blocked many other call paths as well in the entire kernel, so the smaller-scoped lock was preferred. Link: https://lore.kernel.org/netdev/aLb6puGVzR29GpPx@shell.armlinux.org.uk/ Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> Link: https://patch.msgid.link/20250904125238.193990-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Stable-dep-of: e2a10da ("net: phy: transfer phy_config_inband() locking responsibility to phylink") Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent eb148d8 commit 56fe63b

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

drivers/net/phy/phylink.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ struct phylink {
6767
struct timer_list link_poll;
6868

6969
struct mutex state_mutex;
70+
/* Serialize updates to pl->phydev with phylink_resolve() */
71+
struct mutex phydev_mutex;
7072
struct phylink_link_state phy_state;
7173
unsigned int phy_ib_mode;
7274
struct work_struct resolve;
@@ -1568,8 +1570,11 @@ static void phylink_resolve(struct work_struct *w)
15681570
struct phylink_link_state link_state;
15691571
bool mac_config = false;
15701572
bool retrigger = false;
1573+
struct phy_device *phy;
15711574
bool cur_link_state;
15721575

1576+
mutex_lock(&pl->phydev_mutex);
1577+
phy = pl->phydev;
15731578
mutex_lock(&pl->state_mutex);
15741579
cur_link_state = phylink_link_is_up(pl);
15751580

@@ -1603,11 +1608,11 @@ static void phylink_resolve(struct work_struct *w)
16031608
/* If we have a phy, the "up" state is the union of both the
16041609
* PHY and the MAC
16051610
*/
1606-
if (pl->phydev)
1611+
if (phy)
16071612
link_state.link &= pl->phy_state.link;
16081613

16091614
/* Only update if the PHY link is up */
1610-
if (pl->phydev && pl->phy_state.link) {
1615+
if (phy && pl->phy_state.link) {
16111616
/* If the interface has changed, force a link down
16121617
* event if the link isn't already down, and re-resolve.
16131618
*/
@@ -1671,6 +1676,7 @@ static void phylink_resolve(struct work_struct *w)
16711676
queue_work(system_power_efficient_wq, &pl->resolve);
16721677
}
16731678
mutex_unlock(&pl->state_mutex);
1679+
mutex_unlock(&pl->phydev_mutex);
16741680
}
16751681

16761682
static void phylink_run_resolve(struct phylink *pl)
@@ -1806,6 +1812,7 @@ struct phylink *phylink_create(struct phylink_config *config,
18061812
if (!pl)
18071813
return ERR_PTR(-ENOMEM);
18081814

1815+
mutex_init(&pl->phydev_mutex);
18091816
mutex_init(&pl->state_mutex);
18101817
INIT_WORK(&pl->resolve, phylink_resolve);
18111818

@@ -2066,6 +2073,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
20662073
dev_name(&phy->mdio.dev), phy->drv->name, irq_str);
20672074
kfree(irq_str);
20682075

2076+
mutex_lock(&pl->phydev_mutex);
20692077
mutex_lock(&phy->lock);
20702078
mutex_lock(&pl->state_mutex);
20712079
pl->phydev = phy;
@@ -2111,6 +2119,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
21112119

21122120
mutex_unlock(&pl->state_mutex);
21132121
mutex_unlock(&phy->lock);
2122+
mutex_unlock(&pl->phydev_mutex);
21142123

21152124
phylink_dbg(pl,
21162125
"phy: %s setting supported %*pb advertising %*pb\n",
@@ -2289,6 +2298,7 @@ void phylink_disconnect_phy(struct phylink *pl)
22892298

22902299
ASSERT_RTNL();
22912300

2301+
mutex_lock(&pl->phydev_mutex);
22922302
phy = pl->phydev;
22932303
if (phy) {
22942304
mutex_lock(&phy->lock);
@@ -2298,8 +2308,11 @@ void phylink_disconnect_phy(struct phylink *pl)
22982308
pl->mac_tx_clk_stop = false;
22992309
mutex_unlock(&pl->state_mutex);
23002310
mutex_unlock(&phy->lock);
2301-
flush_work(&pl->resolve);
2311+
}
2312+
mutex_unlock(&pl->phydev_mutex);
23022313

2314+
if (phy) {
2315+
flush_work(&pl->resolve);
23032316
phy_disconnect(phy);
23042317
}
23052318
}

0 commit comments

Comments
 (0)