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: Grygorii Strashko <grygorii.strashko@ti.com> Reported-by: Yan Liu <yan-liu@ti.com> Signed-off-by: Sekhar Nori <nsekhar@ti.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
249ee819e2
commit
30c57f0734
@ -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)
|
||||
{
|
||||
u32 slave_port;
|
||||
struct phy_device *phy;
|
||||
struct cpsw_common *cpsw = priv->cpsw;
|
||||
|
||||
soft_reset_slave(slave);
|
||||
@ -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);
|
||||
|
||||
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);
|
||||
if (!slave->phy) {
|
||||
if (!phy) {
|
||||
dev_err(priv->dev, "phy \"%s\" not found on slave %d\n",
|
||||
slave->data->phy_node->full_name,
|
||||
slave->slave_num);
|
||||
return;
|
||||
}
|
||||
} 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);
|
||||
if (IS_ERR(slave->phy)) {
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(priv->dev,
|
||||
"phy \"%s\" not found on slave %d, err %ld\n",
|
||||
slave->data->phy_id, slave->slave_num,
|
||||
PTR_ERR(slave->phy));
|
||||
slave->phy = NULL;
|
||||
PTR_ERR(phy));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
slave->phy = phy;
|
||||
|
||||
phy_attached_info(slave->phy);
|
||||
|
||||
phy_start(slave->phy);
|
||||
|
Loading…
x
Reference in New Issue
Block a user