Skip to content

Commit 0ba5b2f

Browse files
vladimirolteankuba-moo
authored andcommitted
net: phylink: add lock for serializing concurrent pl->phydev writes with resolver
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/[email protected]/ Signed-off-by: Vladimir Oltean <[email protected]> Reviewed-by: Russell King (Oracle) <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 03e79de commit 0ba5b2f

File tree

1 file changed

+16
-3
lines changed

1 file changed

+16
-3
lines changed

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;
@@ -1591,8 +1593,11 @@ static void phylink_resolve(struct work_struct *w)
15911593
struct phylink_link_state link_state;
15921594
bool mac_config = false;
15931595
bool retrigger = false;
1596+
struct phy_device *phy;
15941597
bool cur_link_state;
15951598

1599+
mutex_lock(&pl->phydev_mutex);
1600+
phy = pl->phydev;
15961601
mutex_lock(&pl->state_mutex);
15971602
cur_link_state = phylink_link_is_up(pl);
15981603

@@ -1626,11 +1631,11 @@ static void phylink_resolve(struct work_struct *w)
16261631
/* If we have a phy, the "up" state is the union of both the
16271632
* PHY and the MAC
16281633
*/
1629-
if (pl->phydev)
1634+
if (phy)
16301635
link_state.link &= pl->phy_state.link;
16311636

16321637
/* Only update if the PHY link is up */
1633-
if (pl->phydev && pl->phy_state.link) {
1638+
if (phy && pl->phy_state.link) {
16341639
/* If the interface has changed, force a link down
16351640
* event if the link isn't already down, and re-resolve.
16361641
*/
@@ -1694,6 +1699,7 @@ static void phylink_resolve(struct work_struct *w)
16941699
queue_work(system_power_efficient_wq, &pl->resolve);
16951700
}
16961701
mutex_unlock(&pl->state_mutex);
1702+
mutex_unlock(&pl->phydev_mutex);
16971703
}
16981704

16991705
static void phylink_run_resolve(struct phylink *pl)
@@ -1829,6 +1835,7 @@ struct phylink *phylink_create(struct phylink_config *config,
18291835
if (!pl)
18301836
return ERR_PTR(-ENOMEM);
18311837

1838+
mutex_init(&pl->phydev_mutex);
18321839
mutex_init(&pl->state_mutex);
18331840
INIT_WORK(&pl->resolve, phylink_resolve);
18341841

@@ -2089,6 +2096,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
20892096
dev_name(&phy->mdio.dev), phy->drv->name, irq_str);
20902097
kfree(irq_str);
20912098

2099+
mutex_lock(&pl->phydev_mutex);
20922100
mutex_lock(&phy->lock);
20932101
mutex_lock(&pl->state_mutex);
20942102
pl->phydev = phy;
@@ -2134,6 +2142,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
21342142

21352143
mutex_unlock(&pl->state_mutex);
21362144
mutex_unlock(&phy->lock);
2145+
mutex_unlock(&pl->phydev_mutex);
21372146

21382147
phylink_dbg(pl,
21392148
"phy: %s setting supported %*pb advertising %*pb\n",
@@ -2312,6 +2321,7 @@ void phylink_disconnect_phy(struct phylink *pl)
23122321

23132322
ASSERT_RTNL();
23142323

2324+
mutex_lock(&pl->phydev_mutex);
23152325
phy = pl->phydev;
23162326
if (phy) {
23172327
mutex_lock(&phy->lock);
@@ -2321,8 +2331,11 @@ void phylink_disconnect_phy(struct phylink *pl)
23212331
pl->mac_tx_clk_stop = false;
23222332
mutex_unlock(&pl->state_mutex);
23232333
mutex_unlock(&phy->lock);
2324-
flush_work(&pl->resolve);
2334+
}
2335+
mutex_unlock(&pl->phydev_mutex);
23252336

2337+
if (phy) {
2338+
flush_work(&pl->resolve);
23262339
phy_disconnect(phy);
23272340
}
23282341
}

0 commit comments

Comments
 (0)