From b71be352f76ac3c0559feec2fd960ab9c31404c9 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 12 Mar 2016 00:01:37 +0100 Subject: dsa: slave: Don't reference NULL pointer during phy_disconnect When the phy is disconnected, the parent pointer to the netdev it was attached to is set to NULL. The code then tries to suspend the phy, but dsa_slave_fixed_link_update needs the parent pointer to determine which switch the phy is connected to. So it dereferenced a NULL pointer. Check for this condition. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- net/dsa/slave.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 27bf03d11670..49056d90b179 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -896,11 +896,15 @@ static void dsa_slave_adjust_link(struct net_device *dev) static int dsa_slave_fixed_link_update(struct net_device *dev, struct fixed_phy_status *status) { - struct dsa_slave_priv *p = netdev_priv(dev); - struct dsa_switch *ds = p->parent; + struct dsa_slave_priv *p; + struct dsa_switch *ds; - if (ds->drv->fixed_link_update) - ds->drv->fixed_link_update(ds, p->port, status); + if (dev) { + p = netdev_priv(dev); + ds = p->parent; + if (ds->drv->fixed_link_update) + ds->drv->fixed_link_update(ds, p->port, status); + } return 0; } -- cgit v1.2.3 From 3a44514ff979d7b0192a9f67445509f4ea7dfe2f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 12 Mar 2016 00:01:38 +0100 Subject: dsa: Destroy fixed link phys after the phy has been disconnected The phy is disconnected from the slave in dsa_slave_destroy(). Don't destroy fixed link phys until after this, since there can be fixed linked phys connected to ports. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- net/dsa/dsa.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index d8fb47fcad05..1018e7dcfcc9 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -430,7 +430,18 @@ static void dsa_switch_destroy(struct dsa_switch *ds) hwmon_device_unregister(ds->hwmon_dev); #endif - /* Disable configuration of the CPU and DSA ports */ + /* Destroy network devices for physical switch ports. */ + for (port = 0; port < DSA_MAX_PORTS; port++) { + if (!(ds->phys_port_mask & (1 << port))) + continue; + + if (!ds->ports[port]) + continue; + + dsa_slave_destroy(ds->ports[port]); + } + + /* Remove any fixed link PHYs */ for (port = 0; port < DSA_MAX_PORTS; port++) { if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) continue; @@ -448,17 +459,6 @@ static void dsa_switch_destroy(struct dsa_switch *ds) } } - /* Destroy network devices for physical switch ports. */ - for (port = 0; port < DSA_MAX_PORTS; port++) { - if (!(ds->phys_port_mask & (1 << port))) - continue; - - if (!ds->ports[port]) - continue; - - dsa_slave_destroy(ds->ports[port]); - } - mdiobus_unregister(ds->slave_mii_bus); } -- cgit v1.2.3 From ec777e6b4254648be51146d4e7d735ee3d06bd13 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 12 Mar 2016 00:01:39 +0100 Subject: dsa: dsa: Fix freeing of fixed-phys from user ports. All ports types can have a fixed PHY associated with it. Remove the check which limits removal to only CPU and DSA ports. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- net/dsa/dsa.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 1018e7dcfcc9..f100f340d93f 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -443,9 +443,6 @@ static void dsa_switch_destroy(struct dsa_switch *ds) /* Remove any fixed link PHYs */ for (port = 0; port < DSA_MAX_PORTS; port++) { - if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) - continue; - port_dn = cd->port_dn[port]; if (of_phy_is_fixed_link(port_dn)) { phydev = of_phy_find_device(port_dn); -- cgit v1.2.3 From 5bcbe0f35fb13e31fdd0b2dc9695f19ab0208207 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 12 Mar 2016 00:01:40 +0100 Subject: phy: fixed: Fix removal of phys. The fixed phys delete function simply removed the fixed phy from the internal linked list and freed the memory. It however did not unregister the associated phy device. This meant it was still possible to find the phy device on the mdio bus. Make fixed_phy_del() an internal function and add a fixed_phy_unregister() to unregisters the phy device and then uses fixed_phy_del() to free resources. Modify DSA to use this new API function, so we don't leak phys. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/fixed_phy.c | 11 +++++++++-- include/linux/phy_fixed.h | 5 ++--- net/dsa/dsa.c | 4 +--- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index ab9c473d75ea..fc07a8866020 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -285,7 +285,7 @@ err_regs: } EXPORT_SYMBOL_GPL(fixed_phy_add); -void fixed_phy_del(int phy_addr) +static void fixed_phy_del(int phy_addr) { struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp, *tmp; @@ -300,7 +300,6 @@ void fixed_phy_del(int phy_addr) } } } -EXPORT_SYMBOL_GPL(fixed_phy_del); static int phy_fixed_addr; static DEFINE_SPINLOCK(phy_fixed_addr_lock); @@ -371,6 +370,14 @@ struct phy_device *fixed_phy_register(unsigned int irq, } EXPORT_SYMBOL_GPL(fixed_phy_register); +void fixed_phy_unregister(struct phy_device *phy) +{ + phy_device_remove(phy); + + fixed_phy_del(phy->mdio.addr); +} +EXPORT_SYMBOL_GPL(fixed_phy_unregister); + static int __init fixed_mdio_bus_init(void) { struct fixed_mdio_bus *fmb = &platform_fmb; diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h index 2400d2ea4f34..1d41ec44e39d 100644 --- a/include/linux/phy_fixed.h +++ b/include/linux/phy_fixed.h @@ -19,7 +19,7 @@ extern struct phy_device *fixed_phy_register(unsigned int irq, struct fixed_phy_status *status, int link_gpio, struct device_node *np); -extern void fixed_phy_del(int phy_addr); +extern void fixed_phy_unregister(struct phy_device *phydev); extern int fixed_phy_set_link_update(struct phy_device *phydev, int (*link_update)(struct net_device *, struct fixed_phy_status *)); @@ -40,9 +40,8 @@ static inline struct phy_device *fixed_phy_register(unsigned int irq, { return ERR_PTR(-ENODEV); } -static inline int fixed_phy_del(int phy_addr) +static inline void fixed_phy_unregister(struct phy_device *phydev) { - return -ENODEV; } static inline int fixed_phy_set_link_update(struct phy_device *phydev, int (*link_update)(struct net_device *, diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index f100f340d93f..c28c47463b7e 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -447,11 +447,9 @@ static void dsa_switch_destroy(struct dsa_switch *ds) if (of_phy_is_fixed_link(port_dn)) { phydev = of_phy_find_device(port_dn); if (phydev) { - int addr = phydev->mdio.addr; - phy_device_free(phydev); of_node_put(port_dn); - fixed_phy_del(addr); + fixed_phy_unregister(phydev); } } } -- cgit v1.2.3