diff options
author | Sekhar Nori <nsekhar@ti.com> | 2017-04-03 08:04:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-04-04 13:33:33 -0400 |
commit | 30c57f073449e09ae42f908bfff56c08c8751a6f (patch) | |
tree | 5a1316ef175e9b93befe4eee447c339aa60a8f74 | |
parent | 249ee819e24c180909f43c1173c8ef6724d21faf (diff) |
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>
-rw-r--r-- | drivers/net/ethernet/ti/cpsw.c | 14 |
1 files changed, 8 insertions, 6 deletions
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 58cdc066ef2c..fa674a8bda0c 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c | |||
@@ -1267,6 +1267,7 @@ static void soft_reset_slave(struct cpsw_slave *slave) | |||
1267 | static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) | 1267 | static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) |
1268 | { | 1268 | { |
1269 | u32 slave_port; | 1269 | u32 slave_port; |
1270 | struct phy_device *phy; | ||
1270 | struct cpsw_common *cpsw = priv->cpsw; | 1271 | struct cpsw_common *cpsw = priv->cpsw; |
1271 | 1272 | ||
1272 | soft_reset_slave(slave); | 1273 | soft_reset_slave(slave); |
@@ -1300,27 +1301,28 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) | |||
1300 | 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); | 1301 | 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); |
1301 | 1302 | ||
1302 | if (slave->data->phy_node) { | 1303 | if (slave->data->phy_node) { |
1303 | slave->phy = of_phy_connect(priv->ndev, slave->data->phy_node, | 1304 | phy = of_phy_connect(priv->ndev, slave->data->phy_node, |
1304 | &cpsw_adjust_link, 0, slave->data->phy_if); | 1305 | &cpsw_adjust_link, 0, slave->data->phy_if); |
1305 | if (!slave->phy) { | 1306 | if (!phy) { |
1306 | dev_err(priv->dev, "phy \"%s\" not found on slave %d\n", | 1307 | dev_err(priv->dev, "phy \"%s\" not found on slave %d\n", |
1307 | slave->data->phy_node->full_name, | 1308 | slave->data->phy_node->full_name, |
1308 | slave->slave_num); | 1309 | slave->slave_num); |
1309 | return; | 1310 | return; |
1310 | } | 1311 | } |
1311 | } else { | 1312 | } else { |
1312 | slave->phy = phy_connect(priv->ndev, slave->data->phy_id, | 1313 | phy = phy_connect(priv->ndev, slave->data->phy_id, |
1313 | &cpsw_adjust_link, slave->data->phy_if); | 1314 | &cpsw_adjust_link, slave->data->phy_if); |
1314 | if (IS_ERR(slave->phy)) { | 1315 | if (IS_ERR(phy)) { |
1315 | dev_err(priv->dev, | 1316 | dev_err(priv->dev, |
1316 | "phy \"%s\" not found on slave %d, err %ld\n", | 1317 | "phy \"%s\" not found on slave %d, err %ld\n", |
1317 | slave->data->phy_id, slave->slave_num, | 1318 | slave->data->phy_id, slave->slave_num, |
1318 | PTR_ERR(slave->phy)); | 1319 | PTR_ERR(phy)); |
1319 | slave->phy = NULL; | ||
1320 | return; | 1320 | return; |
1321 | } | 1321 | } |
1322 | } | 1322 | } |
1323 | 1323 | ||
1324 | slave->phy = phy; | ||
1325 | |||
1324 | phy_attached_info(slave->phy); | 1326 | phy_attached_info(slave->phy); |
1325 | 1327 | ||
1326 | phy_start(slave->phy); | 1328 | phy_start(slave->phy); |