Skip to content

Commit 03fb039

Browse files
committed
net: mtk: eth-mux: fix rcu_lock in mux
rcu lock caused by phy not ready yet when phylink_start is called for it switch to sfp works too but switch back causes deadlock (no rcu-stall)
1 parent dc5f57b commit 03fb039

1 file changed

Lines changed: 62 additions & 6 deletions

File tree

drivers/net/ethernet/mediatek/mtk_eth_soc.c

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4919,6 +4919,37 @@ static const struct net_device_ops mtk_netdev_ops = {
49194919
.ndo_select_queue = mtk_select_queue,
49204920
};
49214921

4922+
static struct phylink *mtk_mux_create_phylink(struct mtk_mux *mux, unsigned int channel)
4923+
{
4924+
struct mtk_mux_data *data = mux->data[channel];
4925+
struct mtk_mac *mac = mux->mac;
4926+
struct mtk_eth *eth = mac->hw;
4927+
phy_interface_t phy_mode;
4928+
struct phylink *pl;
4929+
int err;
4930+
4931+
if (!data || !data->of_node)
4932+
return ERR_PTR(-EINVAL);
4933+
4934+
err = of_get_phy_mode(data->of_node, &phy_mode);
4935+
if (err) {
4936+
dev_err(eth->dev, "invalid phy-mode for mux channel %u\n", channel);
4937+
return ERR_PTR(err);
4938+
}
4939+
4940+
pl = phylink_create(&mac->phylink_config,
4941+
of_fwnode_handle(data->of_node),
4942+
phy_mode, &mtk_phylink_ops);
4943+
if (IS_ERR(pl)) {
4944+
dev_err(eth->dev, "failed to create phylink for channel %u\n", channel);
4945+
return pl;
4946+
}
4947+
4948+
dev_info(eth->dev, "Created phylink for channel %u\n", channel);
4949+
data->phylink = pl;
4950+
return pl;
4951+
}
4952+
49224953
static void mux_poll(struct work_struct *work)
49234954
{
49244955
struct mtk_mux *mux = container_of(work, struct mtk_mux, poll.work);
@@ -4939,25 +4970,50 @@ static void mux_poll(struct work_struct *work)
49394970
goto reschedule;
49404971

49414972
dev_info(eth->dev, "ethernet mux: line:%d new channel:%d,sfp:%d\n",__LINE__, new_channel,sfp_present);
4942-
rtnl_lock();
49434973

4974+
rtnl_lock();
49444975
mtk_stop(dev);
49454976

4946-
mac->of_node = mux->data[new_channel]->of_node;
4947-
mac->phylink = mux->data[new_channel]->phylink;
4977+
/* Destroy old phylink if it exists */
4978+
if (mux->data[mux->channel] && mux->data[mux->channel]->phylink) {
4979+
dev_info(eth->dev, "Destroying phylink for channel %u\n", mux->channel);
4980+
phylink_destroy(mux->data[mux->channel]->phylink);
4981+
phylink_stop(mux->data[mux->channel]->phylink);
4982+
phylink_disconnect_phy(mux->data[mux->channel]->phylink);
4983+
phylink_destroy(mux->data[mux->channel]->phylink);
4984+
mux->data[mux->channel]->phylink = NULL;
4985+
}
4986+
rtnl_unlock();
49484987

49494988
dev_info(eth->dev, "ethernet mux: switch to channel%d\n", new_channel);
49504989

49514990
gpiod_set_value_cansleep(mux->chan_sel_gpio, new_channel);
49524991

49534992
usleep_range(100000,200000);
49544993

4994+
/* Create new phylink if not yet present */
4995+
if (!mux->data[new_channel]->phylink) {
4996+
mux->data[new_channel]->phylink = mtk_mux_create_phylink(mux, new_channel);
4997+
if (IS_ERR(mux->data[new_channel]->phylink)) {
4998+
dev_err(eth->dev, "Failed to create new phylink\n");
4999+
mux->data[new_channel]->phylink=NULL;
5000+
goto out_unlock;
5001+
}
5002+
}
5003+
5004+
mac->of_node = mux->data[new_channel]->of_node;
5005+
mac->phylink = mux->data[new_channel]->phylink;
5006+
5007+
rtnl_lock();
49555008
mtk_open(dev);
49565009

49575010
rtnl_unlock();
49585011

49595012
mux->channel = new_channel;
5013+
goto reschedule;
49605014

5015+
out_unlock:
5016+
rtnl_unlock();
49615017
reschedule:
49625018
mod_delayed_work(system_wq, &mux->poll, msecs_to_jiffies(100));
49635019
}
@@ -4995,18 +5051,18 @@ static int mtk_add_mux_channel(struct mtk_mux *mux, struct device_node *np)
49955051
goto err_free_data;
49965052
}
49975053

4998-
phylink = phylink_create(&mux->mac->phylink_config,
5054+
/*phylink = phylink_create(&mux->mac->phylink_config,
49995055
of_fwnode_handle(np),
50005056
phy_mode, &mtk_phylink_ops);
50015057
if (IS_ERR(phylink)) {
50025058
dev_err(eth->dev, "failed to create phylink structure\n");
50035059
err = PTR_ERR(phylink);
50045060
goto err_free_data;
5005-
}
5061+
}*/
50065062

50075063
dev_info(eth->dev, "ethernet mux: line:%d added new channel:%d\n",__LINE__,id);
50085064
data->of_node = np;
5009-
data->phylink = phylink;
5065+
data->phylink = NULL;//phylink;
50105066
mux->data[id] = data;
50115067

50125068
return 0;

0 commit comments

Comments
 (0)