Commit 30c57f07 authored by Sekhar Nori's avatar Sekhar Nori Committed by David S. Miller

net: ethernet: ti: cpsw: fix race condition during open()

TI's cpsw driver handles both OF and non-OF case for phy
connect. Unfortunately of_phy_connect() returns NULL on
error while phy_connect() returns ERR_PTR().

To handle this, cpsw_slave_open() overrides the return value
from phy_connect() to make it NULL or error.

This leaves a small window, where cpsw_adjust_link() may be
invoked for a slave while slave->phy pointer is temporarily
set to -ENODEV (or some other error) before it is finally set
to NULL.

_cpsw_adjust_link() only handles the NULL case, and an oops
results when ERR_PTR() is seen by it.

Note that cpsw_adjust_link() checks PHY status for each
slave whenever it is invoked. It can so happen that even
though phy_connect() for a given slave returns error,
_cpsw_adjust_link() is still called for that slave because
the link status of another slave changed.

Fix this by using a temporary pointer to store return value
of {of_}phy_connect() and do a one-time write to slave->phy.
Reviewed-by: default avatarGrygorii Strashko <grygorii.strashko@ti.com>
Reported-by: default avatarYan Liu <yan-liu@ti.com>
Signed-off-by: default avatarSekhar Nori <nsekhar@ti.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 249ee819
...@@ -1267,6 +1267,7 @@ static void soft_reset_slave(struct cpsw_slave *slave) ...@@ -1267,6 +1267,7 @@ static void soft_reset_slave(struct cpsw_slave *slave)
static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
{ {
u32 slave_port; u32 slave_port;
struct phy_device *phy;
struct cpsw_common *cpsw = priv->cpsw; struct cpsw_common *cpsw = priv->cpsw;
soft_reset_slave(slave); soft_reset_slave(slave);
...@@ -1300,27 +1301,28 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) ...@@ -1300,27 +1301,28 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
1 << slave_port, 0, 0, ALE_MCAST_FWD_2); 1 << slave_port, 0, 0, ALE_MCAST_FWD_2);
if (slave->data->phy_node) { if (slave->data->phy_node) {
slave->phy = of_phy_connect(priv->ndev, slave->data->phy_node, phy = of_phy_connect(priv->ndev, slave->data->phy_node,
&cpsw_adjust_link, 0, slave->data->phy_if); &cpsw_adjust_link, 0, slave->data->phy_if);
if (!slave->phy) { if (!phy) {
dev_err(priv->dev, "phy \"%s\" not found on slave %d\n", dev_err(priv->dev, "phy \"%s\" not found on slave %d\n",
slave->data->phy_node->full_name, slave->data->phy_node->full_name,
slave->slave_num); slave->slave_num);
return; return;
} }
} else { } else {
slave->phy = phy_connect(priv->ndev, slave->data->phy_id, phy = phy_connect(priv->ndev, slave->data->phy_id,
&cpsw_adjust_link, slave->data->phy_if); &cpsw_adjust_link, slave->data->phy_if);
if (IS_ERR(slave->phy)) { if (IS_ERR(phy)) {
dev_err(priv->dev, dev_err(priv->dev,
"phy \"%s\" not found on slave %d, err %ld\n", "phy \"%s\" not found on slave %d, err %ld\n",
slave->data->phy_id, slave->slave_num, slave->data->phy_id, slave->slave_num,
PTR_ERR(slave->phy)); PTR_ERR(phy));
slave->phy = NULL;
return; return;
} }
} }
slave->phy = phy;
phy_attached_info(slave->phy); phy_attached_info(slave->phy);
phy_start(slave->phy); phy_start(slave->phy);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment