linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7] Phy and mdiobus fixes
@ 2015-09-18  9:46 Russell King - ARM Linux
  2015-09-18  9:47 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
                   ` (15 more replies)
  0 siblings, 16 replies; 22+ messages in thread
From: Russell King - ARM Linux @ 2015-09-18  9:46 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linux-kernel, linuxppc-dev,
	Li Yang, Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni

Hi,

While looking at the phy code, I identified a number of weaknesses
where refcounting on device structures was being leaked, where
modules could be removed while in-use, and where the fixed-phy could
end up having unintended consequences caused by incorrect calls to
fixed_phy_update_state().

This patch series resolves those issues, some of which were discovered
with testing on an Armada 388 board.  Not all patches are fully tested,
particularly the one which touches several network drivers.

When resolving the struct device refcounting problems, several different
solutions were considered before settling on the implementation here -
one of the considerations was to avoid touching many network drivers.
The solution here is:

	phy_attach*() - takes a refcount
	phy_detach*() - drops the phy_attach refcount

Provided drivers always attach and detach their phys, which they should
already be doing, this should change nothing, even if they leak a refcount.

	of_phy_find_device() and of_* functions which use that take
	a refcount.  Arrange for this refcount to be dropped once
	the phy is attached.

This is the reason why the previous change is important - we can't drop
this refcount taken by of_phy_find_device() until something else holds
a reference on the device.  This resolves the leaked refcount caused by
using of_phy_connect() or of_phy_attach().

Even without the above changes, these drivers are leaking by calling
of_phy_find_device().  These drivers are addressed by adding the
appropriate release of that refcount.

The mdiobus code also suffered from the same kind of leak, but thankfully
this only happened in one place - the mdio-mux code.

I also found that the try_module_get() in the phy layer code was utterly
useless: phydev->dev.driver was guaranteed to always be NULL, so
try_module_get() was always being called with a NULL argument.  I proved
this with my SFP code, which declares its own MDIO bus - the module use
count was never incremented irrespective of how I set the MDIO bus up.
This allowed the MDIO bus code to be removed from the kernel while there
were still PHYs attached to it.

One other bug was discovered: while using in-band-status with mvneta, it
was found that if a real phy is attached with in-band-status enabled,
and another ethernet interface is using the fixed-phy infrastructure, the
interface using the fixed-phy infrastructure is configured according to
the other interface using the in-band-status - which is caused by the
fixed-phy code not verifying that the phy_device passed in is actually
a fixed-phy device, rather than a real MDIO phy.

Lastly, having mdio_bus reversing phy_device_register() internals seems
like a layering violation - it's trivial to move that code to the phy
device layer.

 drivers/net/ethernet/apm/xgene/xgene_enet_hw.c    | 23 ++++++---
 drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 19 +++++++
 drivers/net/ethernet/freescale/gianfar.c          |  6 ++-
 drivers/net/ethernet/freescale/ucc_geth.c         |  8 ++-
 drivers/net/ethernet/marvell/mvneta.c             |  2 +
 drivers/net/ethernet/xilinx/xilinx_emaclite.c     |  2 +
 drivers/net/phy/fixed_phy.c                       |  2 +-
 drivers/net/phy/mdio-mux.c                        | 19 ++++---
 drivers/net/phy/mdio_bus.c                        | 24 ++++++---
 drivers/net/phy/phy_device.c                      | 62 ++++++++++++++++++-----
 drivers/of/of_mdio.c                              | 27 ++++++++--
 include/linux/phy.h                               |  6 ++-
 12 files changed, 156 insertions(+), 44 deletions(-)

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
@ 2015-09-18  9:47 ` Russell King
  2015-09-18  9:47 ` [PATCH 2/7] phy: fix mdiobus module safety Russell King
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:47 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

of_mdio_find_bus() leaks a struct device refcount, caused by using
class_find_device() and not realising that the device reference has
its refcount incremented:

 * Note, you will need to drop the reference with put_device() after use.
...
        while ((dev = class_dev_iter_next(&iter))) {
                if (match(dev, data)) {
                        get_device(dev);
                        break;
                }

Update the comment, and arrange for the only user of this function
to drop this refcount when disposing of a reference to it.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/mdio-mux.c | 19 +++++++++++++------
 drivers/net/phy/mdio_bus.c |  4 +++-
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 4d4d25efc1e1..280c7c311f72 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -113,18 +113,18 @@ int mdio_mux_init(struct device *dev,
 	if (!parent_bus_node)
 		return -ENODEV;
 
-	parent_bus = of_mdio_find_bus(parent_bus_node);
-	if (parent_bus == NULL) {
-		ret_val = -EPROBE_DEFER;
-		goto err_parent_bus;
-	}
-
 	pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
 	if (pb == NULL) {
 		ret_val = -ENOMEM;
 		goto err_parent_bus;
 	}
 
+	parent_bus = of_mdio_find_bus(parent_bus_node);
+	if (parent_bus == NULL) {
+		ret_val = -EPROBE_DEFER;
+		goto err_parent_bus;
+	}
+
 	pb->switch_data = data;
 	pb->switch_fn = switch_fn;
 	pb->current_child = -1;
@@ -173,6 +173,10 @@ int mdio_mux_init(struct device *dev,
 		dev_info(dev, "Version " DRV_VERSION "\n");
 		return 0;
 	}
+
+	/* balance the reference of_mdio_find_bus() took */
+	put_device(&pb->mii_bus->dev);
+
 err_parent_bus:
 	of_node_put(parent_bus_node);
 	return ret_val;
@@ -189,6 +193,9 @@ void mdio_mux_uninit(void *mux_handle)
 		mdiobus_free(cb->mii_bus);
 		cb = cb->next;
 	}
+
+	/* balance the reference of_mdio_find_bus() in mdio_mux_init() took */
+	put_device(&pb->mii_bus->dev);
 }
 EXPORT_SYMBOL_GPL(mdio_mux_uninit);
 
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 02a4615b65f8..67553e13bd36 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -167,7 +167,9 @@ static int of_mdio_bus_match(struct device *dev, const void *mdio_bus_np)
  * of_mdio_find_bus - Given an mii_bus node, find the mii_bus.
  * @mdio_bus_np: Pointer to the mii_bus.
  *
- * Returns a pointer to the mii_bus, or NULL if none found.
+ * Returns a reference to the mii_bus, or NULL if none found.  The
+ * embedded struct device will have its reference count incremented,
+ * and this must be put once the bus is finished with.
  *
  * Because the association of a device_node and mii_bus is made via
  * of_mdiobus_register(), the mii_bus cannot be found before it is
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 2/7] phy: fix mdiobus module safety
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
  2015-09-18  9:47 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
@ 2015-09-18  9:47 ` Russell King
  2015-09-18  9:47 ` [PATCH 3/7] phy: add proper phy struct device refcounting Russell King
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:47 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

Re-implement the mdiobus module refcounting to ensure that we actually
ensure that the mdiobus module code does not go away while we might call
into it.

The old scheme using bus->dev.driver was buggy, because bus->dev is a
class device which never has a struct device_driver associated with it,
and hence the associated code trying to obtain a refcount did nothing
useful.

Instead, take the approach that other subsystems do: pass the module
when calling mdiobus_register(), and record that in the mii_bus struct.
When we need to increment the module use count in the phy code, use
this stored pointer.  When the phy is deteched, drop the module
refcount, remembering that the phy device might go away at that point.

This doesn't stop the mii_bus going away while there are in-use phys -
it merely stops the underlying code vanishing.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/mdio_bus.c   |  5 +++--
 drivers/net/phy/phy_device.c | 32 ++++++++++++++++++--------------
 include/linux/phy.h          |  5 ++++-
 3 files changed, 25 insertions(+), 17 deletions(-)

diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 67553e13bd36..992406624b7c 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -244,7 +244,7 @@ static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
  *
  * Returns 0 on success or < 0 on error.
  */
-int mdiobus_register(struct mii_bus *bus)
+int __mdiobus_register(struct mii_bus *bus, struct module *owner)
 {
 	int i, err;
 
@@ -255,6 +255,7 @@ int mdiobus_register(struct mii_bus *bus)
 	BUG_ON(bus->state != MDIOBUS_ALLOCATED &&
 	       bus->state != MDIOBUS_UNREGISTERED);
 
+	bus->owner = owner;
 	bus->dev.parent = bus->parent;
 	bus->dev.class = &mdio_bus_class;
 	bus->dev.groups = NULL;
@@ -296,7 +297,7 @@ int mdiobus_register(struct mii_bus *bus)
 	device_del(&bus->dev);
 	return err;
 }
-EXPORT_SYMBOL(mdiobus_register);
+EXPORT_SYMBOL(__mdiobus_register);
 
 void mdiobus_unregister(struct mii_bus *bus)
 {
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index c0f211127274..03adf328f49b 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -582,10 +582,15 @@ EXPORT_SYMBOL(phy_init_hw);
 int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		      u32 flags, phy_interface_t interface)
 {
+	struct mii_bus *bus = phydev->bus;
 	struct device *d = &phydev->dev;
-	struct module *bus_module;
 	int err;
 
+	if (!try_module_get(bus->owner)) {
+		dev_err(&dev->dev, "failed to get the bus module\n");
+		return -EIO;
+	}
+
 	/* Assume that if there is no driver, that it doesn't
 	 * exist, and we should use the genphy driver.
 	 */
@@ -600,20 +605,13 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 			err = device_bind_driver(d);
 
 		if (err)
-			return err;
+			goto error;
 	}
 
 	if (phydev->attached_dev) {
 		dev_err(&dev->dev, "PHY already attached\n");
-		return -EBUSY;
-	}
-
-	/* Increment the bus module reference count */
-	bus_module = phydev->bus->dev.driver ?
-		     phydev->bus->dev.driver->owner : NULL;
-	if (!try_module_get(bus_module)) {
-		dev_err(&dev->dev, "failed to get the bus module\n");
-		return -EIO;
+		err = -EBUSY;
+		goto error;
 	}
 
 	phydev->attached_dev = dev;
@@ -636,6 +634,10 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		phy_resume(phydev);
 
 	return err;
+
+error:
+	module_put(bus->owner);
+	return err;
 }
 EXPORT_SYMBOL(phy_attach_direct);
 
@@ -680,11 +682,9 @@ EXPORT_SYMBOL(phy_attach);
  */
 void phy_detach(struct phy_device *phydev)
 {
+	struct mii_bus *bus;
 	int i;
 
-	if (phydev->bus->dev.driver)
-		module_put(phydev->bus->dev.driver->owner);
-
 	phydev->attached_dev->phydev = NULL;
 	phydev->attached_dev = NULL;
 	phy_suspend(phydev);
@@ -700,6 +700,10 @@ void phy_detach(struct phy_device *phydev)
 			break;
 		}
 	}
+
+	bus = phydev->bus;
+
+	module_put(bus->owner);
 }
 EXPORT_SYMBOL(phy_detach);
 
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 962387a192f1..11bce44f6d65 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -19,6 +19,7 @@
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
+#include <linux/module.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/mod_devicetable.h>
@@ -153,6 +154,7 @@ struct sk_buff;
  * PHYs should register using this structure
  */
 struct mii_bus {
+	struct module *owner;
 	const char *name;
 	char id[MII_BUS_ID_SIZE];
 	void *priv;
@@ -198,7 +200,8 @@ static inline struct mii_bus *mdiobus_alloc(void)
 	return mdiobus_alloc_size(0);
 }
 
-int mdiobus_register(struct mii_bus *bus);
+int __mdiobus_register(struct mii_bus *bus, struct module *owner);
+#define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)
 void mdiobus_unregister(struct mii_bus *bus);
 void mdiobus_free(struct mii_bus *bus);
 struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 3/7] phy: add proper phy struct device refcounting
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
  2015-09-18  9:47 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
  2015-09-18  9:47 ` [PATCH 2/7] phy: fix mdiobus module safety Russell King
@ 2015-09-18  9:47 ` Russell King
  2015-09-18  9:47 ` [PATCH 4/7] of_mdio: fix MDIO phy " Russell King
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:47 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

Take a refcount on the phy struct device when the phy device is attached
to a network device, and drop it after it's detached.  This ensures that
a refcount is held on the phy device while the device is being used by
a network device, thereby preventing the phy_device from being
unexpectedly kfree()'d by phy_device_release().

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/phy_device.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 03adf328f49b..97a4f52addac 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -578,6 +578,7 @@ EXPORT_SYMBOL(phy_init_hw);
  *     generic driver is used.  The phy_device is given a ptr to
  *     the attaching device, and given a callback for link status
  *     change.  The phy_device is returned to the attaching driver.
+ *     This function takes a reference on the phy device.
  */
 int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		      u32 flags, phy_interface_t interface)
@@ -591,6 +592,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		return -EIO;
 	}
 
+	get_device(d);
+
 	/* Assume that if there is no driver, that it doesn't
 	 * exist, and we should use the genphy driver.
 	 */
@@ -636,6 +639,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 	return err;
 
 error:
+	put_device(d);
 	module_put(bus->owner);
 	return err;
 }
@@ -679,6 +683,9 @@ EXPORT_SYMBOL(phy_attach);
 /**
  * phy_detach - detach a PHY device from its network device
  * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
  */
 void phy_detach(struct phy_device *phydev)
 {
@@ -701,8 +708,13 @@ void phy_detach(struct phy_device *phydev)
 		}
 	}
 
+	/*
+	 * The phydev might go away on the put_device() below, so avoid
+	 * a use-after-free bug by reading the underlying bus first.
+	 */
 	bus = phydev->bus;
 
+	put_device(&phydev->dev);
 	module_put(bus->owner);
 }
 EXPORT_SYMBOL(phy_detach);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 4/7] of_mdio: fix MDIO phy device refcounting
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (2 preceding siblings ...)
  2015-09-18  9:47 ` [PATCH 3/7] phy: add proper phy struct device refcounting Russell King
@ 2015-09-18  9:47 ` Russell King
  2015-09-18  9:47 ` [PATCH 5/7] net: fix phy refcounting in a bunch of drivers Russell King
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:47 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

bus_find_device() is defined as:

 * This is similar to the bus_for_each_dev() function above, but it
 * returns a reference to a device that is 'found' for later use, as
 * determined by the @match callback.

and it does indeed return a reference-counted pointer to the device:

        while ((dev = next_device(&i)))
                if (match(dev, data) && get_device(dev))
                                        ^^^^^^^^^^^^^^^
                        break;
        klist_iter_exit(&i);
        return dev;

What that means is that when we're done with the struct device, we must
drop that reference.  Neither of_phy_connect() nor of_phy_attach() did
this when phy_connect_direct() or phy_attach_direct() failed.

With our previous patch, phy_connect_direct() and phy_attach_direct()
take a new refcount on the phy device when successful, so we can drop
our local reference immediatley after these functions, whether or not
they succeeded.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/of/of_mdio.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 1350fa25cdb0..a87a868fed64 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -197,7 +197,8 @@ static int of_phy_match(struct device *dev, void *phy_np)
  * of_phy_find_device - Give a PHY node, find the phy_device
  * @phy_np: Pointer to the phy's device tree node
  *
- * Returns a pointer to the phy_device.
+ * If successful, returns a pointer to the phy_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure.
  */
 struct phy_device *of_phy_find_device(struct device_node *phy_np)
 {
@@ -217,7 +218,9 @@ EXPORT_SYMBOL(of_phy_find_device);
  * @hndlr: Link state callback for the network device
  * @iface: PHY data interface type
  *
- * Returns a pointer to the phy_device if successful.  NULL otherwise
+ * If successful, returns a pointer to the phy_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure. The
+ * refcount must be dropped by calling phy_disconnect() or phy_detach().
  */
 struct phy_device *of_phy_connect(struct net_device *dev,
 				  struct device_node *phy_np,
@@ -225,13 +228,19 @@ struct phy_device *of_phy_connect(struct net_device *dev,
 				  phy_interface_t iface)
 {
 	struct phy_device *phy = of_phy_find_device(phy_np);
+	int ret;
 
 	if (!phy)
 		return NULL;
 
 	phy->dev_flags = flags;
 
-	return phy_connect_direct(dev, phy, hndlr, iface) ? NULL : phy;
+	ret = phy_connect_direct(dev, phy, hndlr, iface);
+
+	/* refcount is held by phy_connect_direct() on success */
+	put_device(&phy->dev);
+
+	return ret ? NULL : phy;
 }
 EXPORT_SYMBOL(of_phy_connect);
 
@@ -241,17 +250,27 @@ EXPORT_SYMBOL(of_phy_connect);
  * @phy_np: Node pointer for the PHY
  * @flags: flags to pass to the PHY
  * @iface: PHY data interface type
+ *
+ * If successful, returns a pointer to the phy_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure. The
+ * refcount must be dropped by calling phy_disconnect() or phy_detach().
  */
 struct phy_device *of_phy_attach(struct net_device *dev,
 				 struct device_node *phy_np, u32 flags,
 				 phy_interface_t iface)
 {
 	struct phy_device *phy = of_phy_find_device(phy_np);
+	int ret;
 
 	if (!phy)
 		return NULL;
 
-	return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy;
+	ret = phy_attach_direct(dev, phy, flags, iface);
+
+	/* refcount is held by phy_attach_direct() on success */
+	put_device(&phy->dev);
+
+	return ret ? NULL : phy;
 }
 EXPORT_SYMBOL(of_phy_attach);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 5/7] net: fix phy refcounting in a bunch of drivers
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (3 preceding siblings ...)
  2015-09-18  9:47 ` [PATCH 4/7] of_mdio: fix MDIO phy " Russell King
@ 2015-09-18  9:47 ` Russell King
  2015-09-18  9:47 ` [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:47 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

of_phy_find_device() increments the phy struct device refcount,
which we need to properly balance.  Add code to network drivers
using this function to ensure that the struct device refcount is
correctly balanced.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/ethernet/apm/xgene/xgene_enet_hw.c    | 23 +++++++++++++++--------
 drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 19 +++++++++++++++++++
 drivers/net/ethernet/freescale/gianfar.c          |  3 +++
 drivers/net/ethernet/freescale/ucc_geth.c         |  8 +++++++-
 drivers/net/ethernet/marvell/mvneta.c             |  2 ++
 drivers/net/ethernet/xilinx/xilinx_emaclite.c     |  2 ++
 6 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
index cfa37041ab71..d2103a3199eb 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -689,16 +689,23 @@ static int xgene_enet_phy_connect(struct net_device *ndev)
 			netdev_dbg(ndev, "No phy-handle found in DT\n");
 			return -ENODEV;
 		}
-		pdata->phy_dev = of_phy_find_device(phy_np);
-	}
 
-	phy_dev = pdata->phy_dev;
+		pdata->phy_dev = of_phy_connect(ndev, phy_np,
+						&xgene_enet_adjust_link,
+						pdata->phy_mode);
+		if (!pdata->phy_dev) {
+			netdev_err(ndev, "Could not connect to PHY\n");
+			return -ENODEV;
+		}
+	} else {
+		phy_dev = pdata->phy_dev;
 
-	if (!phy_dev ||
-	    phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link,
-			       pdata->phy_mode)) {
-		netdev_err(ndev, "Could not connect to PHY\n");
-		return  -ENODEV;
+		if (!phy_dev ||
+		    phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link,
+				       pdata->phy_mode)) {
+			netdev_err(ndev, "Could not connect to PHY\n");
+			return  -ENODEV;
+		}
 	}
 
 	pdata->phy_speed = SPEED_UNKNOWN;
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index 574c49278900..529d212bd071 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -1000,6 +1000,20 @@ static int bgx_init_phy(struct bgx *bgx)
 	return bgx_init_of_phy(bgx);
 }
 
+/*
+ * This drops the refcount obtained from of_phy_find_device() above.
+ * We do not need to keep the refcount after phy_connect_direct() has
+ * taken its own reference.
+ */
+static void bgx_drop_phy_ref(struct bgx *bgx)
+{
+	unsigned int lmac;
+
+	for (lmac = 0; lmac < bgx->lmac_count; lmac++)
+		if (bgx->lmac[lmac].phydev)
+			put_device(&bgx->lmac[lmac].phydev->dev);
+}
+
 static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	int err;
@@ -1056,9 +1070,14 @@ static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 		}
 	}
 
+	if (np)
+		bgx_drop_phy_ref(bgx);
+
 	return 0;
 
 err_enable:
+	if (np)
+		bgx_drop_phy_ref(bgx);
 	bgx_vnic[bgx->bgx_id] = NULL;
 err_release_regions:
 	pci_release_regions(pdev);
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 4b69d061d90f..65a16086faec 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -1702,6 +1702,7 @@ static void gfar_configure_serdes(struct net_device *dev)
 	tbiphy = of_phy_find_device(priv->tbi_node);
 	if (!tbiphy) {
 		dev_err(&dev->dev, "error: Could not get TBI device\n");
+		put_device(&tbiphy->dev);
 		return;
 	}
 
@@ -1723,6 +1724,8 @@ static void gfar_configure_serdes(struct net_device *dev)
 	phy_write(tbiphy, MII_BMCR,
 		  BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
 		  BMCR_SPEED1000);
+
+	put_device(&tbiphy->dev);
 }
 
 static int __gfar_is_rx_idle(struct gfar_private *priv)
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index 4dd40e057f40..650f7888e32b 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -1384,6 +1384,8 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
 		value = phy_read(tbiphy, ENET_TBI_MII_CR);
 		value &= ~0x1000;	/* Turn off autonegotiation */
 		phy_write(tbiphy, ENET_TBI_MII_CR, value);
+
+		put_device(&tbiphy->dev);
 	}
 
 	init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2);
@@ -1702,8 +1704,10 @@ static void uec_configure_serdes(struct net_device *dev)
 	 * everything for us?  Resetting it takes the link down and requires
 	 * several seconds for it to come back.
 	 */
-	if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS)
+	if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) {
+		put_device(&tbiphy->dev);
 		return;
+	}
 
 	/* Single clk mode, mii mode off(for serdes communication) */
 	phy_write(tbiphy, ENET_TBI_MII_ANA, TBIANA_SETTINGS);
@@ -1711,6 +1715,8 @@ static void uec_configure_serdes(struct net_device *dev)
 	phy_write(tbiphy, ENET_TBI_MII_TBICON, TBICON_CLK_SELECT);
 
 	phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS);
+
+	put_device(&tbiphy->dev);
 }
 
 /* Configure the PHY for dev.
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index fe2299ac4f5c..0dce037a2682 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3173,6 +3173,8 @@ static int mvneta_probe(struct platform_device *pdev)
 		struct phy_device *phy = of_phy_find_device(dn);
 
 		mvneta_fixed_link_update(pp, phy);
+
+		put_device(&phy->dev);
 	}
 
 	return 0;
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index 6008eee01a33..cf468c87ce57 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -828,6 +828,8 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
 		if (!phydev)
 			dev_info(dev,
 				 "MDIO of the phy is not registered yet\n");
+		else
+			put_device(&phydev->dev);
 		return 0;
 	}
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state()
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (4 preceding siblings ...)
  2015-09-18  9:47 ` [PATCH 5/7] net: fix phy refcounting in a bunch of drivers Russell King
@ 2015-09-18  9:47 ` Russell King
  2015-09-18  9:47 ` [PATCH 7/7] phy: add phy_device_remove() Russell King
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:47 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

Validate that the phy_device passed into fixed_phy_update_state() is a
fixed-phy device before walking the list of phys for a fixed phy at the
same address.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/fixed_phy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index fb1299c6326e..e23bf5b90e17 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -220,7 +220,7 @@ int fixed_phy_update_state(struct phy_device *phydev,
 	struct fixed_mdio_bus *fmb = &platform_fmb;
 	struct fixed_phy *fp;
 
-	if (!phydev || !phydev->bus)
+	if (!phydev || phydev->bus != fmb->mii_bus)
 		return -EINVAL;
 
 	list_for_each_entry(fp, &fmb->phys, node) {
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 7/7] phy: add phy_device_remove()
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (5 preceding siblings ...)
  2015-09-18  9:47 ` [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
@ 2015-09-18  9:47 ` Russell King
  2015-09-18  9:54 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:47 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

Add a phy_device_remove() function to complement phy_device_register(),
which undoes the effects of phy_device_register() by removing the phy
device from visibility, but not freeing it.

This allows these details to be moved out of the mdio bus code into
the phy code where this action belongs.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/ethernet/freescale/gianfar.c |  5 +++--
 drivers/net/phy/mdio_bus.c               | 15 ++++++++++-----
 drivers/net/phy/phy_device.c             | 18 ++++++++++++++++++
 include/linux/phy.h                      |  1 +
 4 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 65a16086faec..903211df3288 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -1702,7 +1702,6 @@ static void gfar_configure_serdes(struct net_device *dev)
 	tbiphy = of_phy_find_device(priv->tbi_node);
 	if (!tbiphy) {
 		dev_err(&dev->dev, "error: Could not get TBI device\n");
-		put_device(&tbiphy->dev);
 		return;
 	}
 
@@ -1711,8 +1710,10 @@ static void gfar_configure_serdes(struct net_device *dev)
 	 * everything for us?  Resetting it takes the link down and requires
 	 * several seconds for it to come back.
 	 */
-	if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS)
+	if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) {
+		put_device(&tbiphy->dev);
 		return;
+	}
 
 	/* Single clk mode, mii mode off(for serdes communication) */
 	phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 992406624b7c..c340e412b38f 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -291,8 +291,11 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
 
 error:
 	while (--i >= 0) {
-		if (bus->phy_map[i])
-			device_unregister(&bus->phy_map[i]->dev);
+		struct phy_device *phydev = bus->phy_map[i];
+		if (phydev) {
+			phy_device_remove(phydev);
+			phy_device_free(phydev);
+		}
 	}
 	device_del(&bus->dev);
 	return err;
@@ -307,9 +310,11 @@ void mdiobus_unregister(struct mii_bus *bus)
 	bus->state = MDIOBUS_UNREGISTERED;
 
 	for (i = 0; i < PHY_MAX_ADDR; i++) {
-		if (bus->phy_map[i])
-			device_unregister(&bus->phy_map[i]->dev);
-		bus->phy_map[i] = NULL;
+		struct phy_device *phydev = bus->phy_map[i];
+		if (phydev) {
+			phy_device_remove(phydev);
+			phy_device_free(phydev);
+		}
 	}
 	device_del(&bus->dev);
 }
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 97a4f52addac..f761288abe66 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -384,6 +384,24 @@ int phy_device_register(struct phy_device *phydev)
 EXPORT_SYMBOL(phy_device_register);
 
 /**
+ * phy_device_remove - Remove a previously registered phy device from the MDIO bus
+ * @phydev: phy_device structure to remove
+ *
+ * This doesn't free the phy_device itself, it merely reverses the effects
+ * of phy_device_register(). Use phy_device_free() to free the device
+ * after calling this function.
+ */
+void phy_device_remove(struct phy_device *phydev)
+{
+	struct mii_bus *bus = phydev->bus;
+	int addr = phydev->addr;
+
+	device_del(&phydev->dev);
+	bus->phy_map[addr] = NULL;
+}
+EXPORT_SYMBOL(phy_device_remove);
+
+/**
  * phy_find_first - finds the first PHY device on the bus
  * @bus: the target MII bus
  */
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 11bce44f6d65..4a4e3a092337 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -745,6 +745,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
 				     struct phy_c45_device_ids *c45_ids);
 struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45);
 int phy_device_register(struct phy_device *phy);
+void phy_device_remove(struct phy_device *phydev);
 int phy_init_hw(struct phy_device *phydev);
 int phy_suspend(struct phy_device *phydev);
 int phy_resume(struct phy_device *phydev);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (6 preceding siblings ...)
  2015-09-18  9:47 ` [PATCH 7/7] phy: add phy_device_remove() Russell King
@ 2015-09-18  9:54 ` Russell King
  2015-09-21 19:01   ` David Miller
  2015-09-18  9:55 ` [PATCH 2/7] phy: fix mdiobus module safety Russell King
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 22+ messages in thread
From: Russell King @ 2015-09-18  9:54 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni, linux-kernel

of_mdio_find_bus() leaks a struct device refcount, caused by using
class_find_device() and not realising that the device reference has
its refcount incremented:

 * Note, you will need to drop the reference with put_device() after use.
...
        while ((dev = class_dev_iter_next(&iter))) {
                if (match(dev, data)) {
                        get_device(dev);
                        break;
                }

Update the comment, and arrange for the only user of this function
to drop this refcount when disposing of a reference to it.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/mdio-mux.c | 19 +++++++++++++------
 drivers/net/phy/mdio_bus.c |  4 +++-
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 4d4d25efc1e1..280c7c311f72 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -113,18 +113,18 @@ int mdio_mux_init(struct device *dev,
 	if (!parent_bus_node)
 		return -ENODEV;
 
-	parent_bus = of_mdio_find_bus(parent_bus_node);
-	if (parent_bus == NULL) {
-		ret_val = -EPROBE_DEFER;
-		goto err_parent_bus;
-	}
-
 	pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
 	if (pb == NULL) {
 		ret_val = -ENOMEM;
 		goto err_parent_bus;
 	}
 
+	parent_bus = of_mdio_find_bus(parent_bus_node);
+	if (parent_bus == NULL) {
+		ret_val = -EPROBE_DEFER;
+		goto err_parent_bus;
+	}
+
 	pb->switch_data = data;
 	pb->switch_fn = switch_fn;
 	pb->current_child = -1;
@@ -173,6 +173,10 @@ int mdio_mux_init(struct device *dev,
 		dev_info(dev, "Version " DRV_VERSION "\n");
 		return 0;
 	}
+
+	/* balance the reference of_mdio_find_bus() took */
+	put_device(&pb->mii_bus->dev);
+
 err_parent_bus:
 	of_node_put(parent_bus_node);
 	return ret_val;
@@ -189,6 +193,9 @@ void mdio_mux_uninit(void *mux_handle)
 		mdiobus_free(cb->mii_bus);
 		cb = cb->next;
 	}
+
+	/* balance the reference of_mdio_find_bus() in mdio_mux_init() took */
+	put_device(&pb->mii_bus->dev);
 }
 EXPORT_SYMBOL_GPL(mdio_mux_uninit);
 
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 02a4615b65f8..67553e13bd36 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -167,7 +167,9 @@ static int of_mdio_bus_match(struct device *dev, const void *mdio_bus_np)
  * of_mdio_find_bus - Given an mii_bus node, find the mii_bus.
  * @mdio_bus_np: Pointer to the mii_bus.
  *
- * Returns a pointer to the mii_bus, or NULL if none found.
+ * Returns a reference to the mii_bus, or NULL if none found.  The
+ * embedded struct device will have its reference count incremented,
+ * and this must be put once the bus is finished with.
  *
  * Because the association of a device_node and mii_bus is made via
  * of_mdiobus_register(), the mii_bus cannot be found before it is
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 2/7] phy: fix mdiobus module safety
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (7 preceding siblings ...)
  2015-09-18  9:54 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
@ 2015-09-18  9:55 ` Russell King
  2015-09-18  9:55 ` [PATCH 3/7] phy: add proper phy struct device refcounting Russell King
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:55 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni, linux-kernel

Re-implement the mdiobus module refcounting to ensure that we actually
ensure that the mdiobus module code does not go away while we might call
into it.

The old scheme using bus->dev.driver was buggy, because bus->dev is a
class device which never has a struct device_driver associated with it,
and hence the associated code trying to obtain a refcount did nothing
useful.

Instead, take the approach that other subsystems do: pass the module
when calling mdiobus_register(), and record that in the mii_bus struct.
When we need to increment the module use count in the phy code, use
this stored pointer.  When the phy is deteched, drop the module
refcount, remembering that the phy device might go away at that point.

This doesn't stop the mii_bus going away while there are in-use phys -
it merely stops the underlying code vanishing.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/mdio_bus.c   |  5 +++--
 drivers/net/phy/phy_device.c | 32 ++++++++++++++++++--------------
 include/linux/phy.h          |  5 ++++-
 3 files changed, 25 insertions(+), 17 deletions(-)

diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 67553e13bd36..992406624b7c 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -244,7 +244,7 @@ static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
  *
  * Returns 0 on success or < 0 on error.
  */
-int mdiobus_register(struct mii_bus *bus)
+int __mdiobus_register(struct mii_bus *bus, struct module *owner)
 {
 	int i, err;
 
@@ -255,6 +255,7 @@ int mdiobus_register(struct mii_bus *bus)
 	BUG_ON(bus->state != MDIOBUS_ALLOCATED &&
 	       bus->state != MDIOBUS_UNREGISTERED);
 
+	bus->owner = owner;
 	bus->dev.parent = bus->parent;
 	bus->dev.class = &mdio_bus_class;
 	bus->dev.groups = NULL;
@@ -296,7 +297,7 @@ int mdiobus_register(struct mii_bus *bus)
 	device_del(&bus->dev);
 	return err;
 }
-EXPORT_SYMBOL(mdiobus_register);
+EXPORT_SYMBOL(__mdiobus_register);
 
 void mdiobus_unregister(struct mii_bus *bus)
 {
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index c0f211127274..03adf328f49b 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -582,10 +582,15 @@ EXPORT_SYMBOL(phy_init_hw);
 int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		      u32 flags, phy_interface_t interface)
 {
+	struct mii_bus *bus = phydev->bus;
 	struct device *d = &phydev->dev;
-	struct module *bus_module;
 	int err;
 
+	if (!try_module_get(bus->owner)) {
+		dev_err(&dev->dev, "failed to get the bus module\n");
+		return -EIO;
+	}
+
 	/* Assume that if there is no driver, that it doesn't
 	 * exist, and we should use the genphy driver.
 	 */
@@ -600,20 +605,13 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 			err = device_bind_driver(d);
 
 		if (err)
-			return err;
+			goto error;
 	}
 
 	if (phydev->attached_dev) {
 		dev_err(&dev->dev, "PHY already attached\n");
-		return -EBUSY;
-	}
-
-	/* Increment the bus module reference count */
-	bus_module = phydev->bus->dev.driver ?
-		     phydev->bus->dev.driver->owner : NULL;
-	if (!try_module_get(bus_module)) {
-		dev_err(&dev->dev, "failed to get the bus module\n");
-		return -EIO;
+		err = -EBUSY;
+		goto error;
 	}
 
 	phydev->attached_dev = dev;
@@ -636,6 +634,10 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		phy_resume(phydev);
 
 	return err;
+
+error:
+	module_put(bus->owner);
+	return err;
 }
 EXPORT_SYMBOL(phy_attach_direct);
 
@@ -680,11 +682,9 @@ EXPORT_SYMBOL(phy_attach);
  */
 void phy_detach(struct phy_device *phydev)
 {
+	struct mii_bus *bus;
 	int i;
 
-	if (phydev->bus->dev.driver)
-		module_put(phydev->bus->dev.driver->owner);
-
 	phydev->attached_dev->phydev = NULL;
 	phydev->attached_dev = NULL;
 	phy_suspend(phydev);
@@ -700,6 +700,10 @@ void phy_detach(struct phy_device *phydev)
 			break;
 		}
 	}
+
+	bus = phydev->bus;
+
+	module_put(bus->owner);
 }
 EXPORT_SYMBOL(phy_detach);
 
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 962387a192f1..11bce44f6d65 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -19,6 +19,7 @@
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
+#include <linux/module.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/mod_devicetable.h>
@@ -153,6 +154,7 @@ struct sk_buff;
  * PHYs should register using this structure
  */
 struct mii_bus {
+	struct module *owner;
 	const char *name;
 	char id[MII_BUS_ID_SIZE];
 	void *priv;
@@ -198,7 +200,8 @@ static inline struct mii_bus *mdiobus_alloc(void)
 	return mdiobus_alloc_size(0);
 }
 
-int mdiobus_register(struct mii_bus *bus);
+int __mdiobus_register(struct mii_bus *bus, struct module *owner);
+#define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)
 void mdiobus_unregister(struct mii_bus *bus);
 void mdiobus_free(struct mii_bus *bus);
 struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 3/7] phy: add proper phy struct device refcounting
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (8 preceding siblings ...)
  2015-09-18  9:55 ` [PATCH 2/7] phy: fix mdiobus module safety Russell King
@ 2015-09-18  9:55 ` Russell King
  2015-09-18  9:55 ` [PATCH 4/7] of_mdio: fix MDIO phy " Russell King
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:55 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni, linux-kernel

Take a refcount on the phy struct device when the phy device is attached
to a network device, and drop it after it's detached.  This ensures that
a refcount is held on the phy device while the device is being used by
a network device, thereby preventing the phy_device from being
unexpectedly kfree()'d by phy_device_release().

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/phy_device.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 03adf328f49b..97a4f52addac 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -578,6 +578,7 @@ EXPORT_SYMBOL(phy_init_hw);
  *     generic driver is used.  The phy_device is given a ptr to
  *     the attaching device, and given a callback for link status
  *     change.  The phy_device is returned to the attaching driver.
+ *     This function takes a reference on the phy device.
  */
 int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		      u32 flags, phy_interface_t interface)
@@ -591,6 +592,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		return -EIO;
 	}
 
+	get_device(d);
+
 	/* Assume that if there is no driver, that it doesn't
 	 * exist, and we should use the genphy driver.
 	 */
@@ -636,6 +639,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 	return err;
 
 error:
+	put_device(d);
 	module_put(bus->owner);
 	return err;
 }
@@ -679,6 +683,9 @@ EXPORT_SYMBOL(phy_attach);
 /**
  * phy_detach - detach a PHY device from its network device
  * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
  */
 void phy_detach(struct phy_device *phydev)
 {
@@ -701,8 +708,13 @@ void phy_detach(struct phy_device *phydev)
 		}
 	}
 
+	/*
+	 * The phydev might go away on the put_device() below, so avoid
+	 * a use-after-free bug by reading the underlying bus first.
+	 */
 	bus = phydev->bus;
 
+	put_device(&phydev->dev);
 	module_put(bus->owner);
 }
 EXPORT_SYMBOL(phy_detach);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 4/7] of_mdio: fix MDIO phy device refcounting
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (9 preceding siblings ...)
  2015-09-18  9:55 ` [PATCH 3/7] phy: add proper phy struct device refcounting Russell King
@ 2015-09-18  9:55 ` Russell King
  2015-09-18  9:55 ` [PATCH 5/7] net: fix phy refcounting in a bunch of drivers Russell King
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:55 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni, linux-kernel

bus_find_device() is defined as:

 * This is similar to the bus_for_each_dev() function above, but it
 * returns a reference to a device that is 'found' for later use, as
 * determined by the @match callback.

and it does indeed return a reference-counted pointer to the device:

        while ((dev = next_device(&i)))
                if (match(dev, data) && get_device(dev))
                                        ^^^^^^^^^^^^^^^
                        break;
        klist_iter_exit(&i);
        return dev;

What that means is that when we're done with the struct device, we must
drop that reference.  Neither of_phy_connect() nor of_phy_attach() did
this when phy_connect_direct() or phy_attach_direct() failed.

With our previous patch, phy_connect_direct() and phy_attach_direct()
take a new refcount on the phy device when successful, so we can drop
our local reference immediatley after these functions, whether or not
they succeeded.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/of/of_mdio.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 1350fa25cdb0..a87a868fed64 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -197,7 +197,8 @@ static int of_phy_match(struct device *dev, void *phy_np)
  * of_phy_find_device - Give a PHY node, find the phy_device
  * @phy_np: Pointer to the phy's device tree node
  *
- * Returns a pointer to the phy_device.
+ * If successful, returns a pointer to the phy_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure.
  */
 struct phy_device *of_phy_find_device(struct device_node *phy_np)
 {
@@ -217,7 +218,9 @@ EXPORT_SYMBOL(of_phy_find_device);
  * @hndlr: Link state callback for the network device
  * @iface: PHY data interface type
  *
- * Returns a pointer to the phy_device if successful.  NULL otherwise
+ * If successful, returns a pointer to the phy_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure. The
+ * refcount must be dropped by calling phy_disconnect() or phy_detach().
  */
 struct phy_device *of_phy_connect(struct net_device *dev,
 				  struct device_node *phy_np,
@@ -225,13 +228,19 @@ struct phy_device *of_phy_connect(struct net_device *dev,
 				  phy_interface_t iface)
 {
 	struct phy_device *phy = of_phy_find_device(phy_np);
+	int ret;
 
 	if (!phy)
 		return NULL;
 
 	phy->dev_flags = flags;
 
-	return phy_connect_direct(dev, phy, hndlr, iface) ? NULL : phy;
+	ret = phy_connect_direct(dev, phy, hndlr, iface);
+
+	/* refcount is held by phy_connect_direct() on success */
+	put_device(&phy->dev);
+
+	return ret ? NULL : phy;
 }
 EXPORT_SYMBOL(of_phy_connect);
 
@@ -241,17 +250,27 @@ EXPORT_SYMBOL(of_phy_connect);
  * @phy_np: Node pointer for the PHY
  * @flags: flags to pass to the PHY
  * @iface: PHY data interface type
+ *
+ * If successful, returns a pointer to the phy_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure. The
+ * refcount must be dropped by calling phy_disconnect() or phy_detach().
  */
 struct phy_device *of_phy_attach(struct net_device *dev,
 				 struct device_node *phy_np, u32 flags,
 				 phy_interface_t iface)
 {
 	struct phy_device *phy = of_phy_find_device(phy_np);
+	int ret;
 
 	if (!phy)
 		return NULL;
 
-	return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy;
+	ret = phy_attach_direct(dev, phy, flags, iface);
+
+	/* refcount is held by phy_attach_direct() on success */
+	put_device(&phy->dev);
+
+	return ret ? NULL : phy;
 }
 EXPORT_SYMBOL(of_phy_attach);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 5/7] net: fix phy refcounting in a bunch of drivers
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (10 preceding siblings ...)
  2015-09-18  9:55 ` [PATCH 4/7] of_mdio: fix MDIO phy " Russell King
@ 2015-09-18  9:55 ` Russell King
  2015-09-18  9:55 ` [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:55 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni, linux-kernel

of_phy_find_device() increments the phy struct device refcount,
which we need to properly balance.  Add code to network drivers
using this function to ensure that the struct device refcount is
correctly balanced.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/ethernet/apm/xgene/xgene_enet_hw.c    | 23 +++++++++++++++--------
 drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 19 +++++++++++++++++++
 drivers/net/ethernet/freescale/gianfar.c          |  3 +++
 drivers/net/ethernet/freescale/ucc_geth.c         |  8 +++++++-
 drivers/net/ethernet/marvell/mvneta.c             |  2 ++
 drivers/net/ethernet/xilinx/xilinx_emaclite.c     |  2 ++
 6 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
index cfa37041ab71..d2103a3199eb 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -689,16 +689,23 @@ static int xgene_enet_phy_connect(struct net_device *ndev)
 			netdev_dbg(ndev, "No phy-handle found in DT\n");
 			return -ENODEV;
 		}
-		pdata->phy_dev = of_phy_find_device(phy_np);
-	}
 
-	phy_dev = pdata->phy_dev;
+		pdata->phy_dev = of_phy_connect(ndev, phy_np,
+						&xgene_enet_adjust_link,
+						pdata->phy_mode);
+		if (!pdata->phy_dev) {
+			netdev_err(ndev, "Could not connect to PHY\n");
+			return -ENODEV;
+		}
+	} else {
+		phy_dev = pdata->phy_dev;
 
-	if (!phy_dev ||
-	    phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link,
-			       pdata->phy_mode)) {
-		netdev_err(ndev, "Could not connect to PHY\n");
-		return  -ENODEV;
+		if (!phy_dev ||
+		    phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link,
+				       pdata->phy_mode)) {
+			netdev_err(ndev, "Could not connect to PHY\n");
+			return  -ENODEV;
+		}
 	}
 
 	pdata->phy_speed = SPEED_UNKNOWN;
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index 574c49278900..529d212bd071 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -1000,6 +1000,20 @@ static int bgx_init_phy(struct bgx *bgx)
 	return bgx_init_of_phy(bgx);
 }
 
+/*
+ * This drops the refcount obtained from of_phy_find_device() above.
+ * We do not need to keep the refcount after phy_connect_direct() has
+ * taken its own reference.
+ */
+static void bgx_drop_phy_ref(struct bgx *bgx)
+{
+	unsigned int lmac;
+
+	for (lmac = 0; lmac < bgx->lmac_count; lmac++)
+		if (bgx->lmac[lmac].phydev)
+			put_device(&bgx->lmac[lmac].phydev->dev);
+}
+
 static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	int err;
@@ -1056,9 +1070,14 @@ static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 		}
 	}
 
+	if (np)
+		bgx_drop_phy_ref(bgx);
+
 	return 0;
 
 err_enable:
+	if (np)
+		bgx_drop_phy_ref(bgx);
 	bgx_vnic[bgx->bgx_id] = NULL;
 err_release_regions:
 	pci_release_regions(pdev);
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 4b69d061d90f..65a16086faec 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -1702,6 +1702,7 @@ static void gfar_configure_serdes(struct net_device *dev)
 	tbiphy = of_phy_find_device(priv->tbi_node);
 	if (!tbiphy) {
 		dev_err(&dev->dev, "error: Could not get TBI device\n");
+		put_device(&tbiphy->dev);
 		return;
 	}
 
@@ -1723,6 +1724,8 @@ static void gfar_configure_serdes(struct net_device *dev)
 	phy_write(tbiphy, MII_BMCR,
 		  BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
 		  BMCR_SPEED1000);
+
+	put_device(&tbiphy->dev);
 }
 
 static int __gfar_is_rx_idle(struct gfar_private *priv)
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index 4dd40e057f40..650f7888e32b 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -1384,6 +1384,8 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
 		value = phy_read(tbiphy, ENET_TBI_MII_CR);
 		value &= ~0x1000;	/* Turn off autonegotiation */
 		phy_write(tbiphy, ENET_TBI_MII_CR, value);
+
+		put_device(&tbiphy->dev);
 	}
 
 	init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2);
@@ -1702,8 +1704,10 @@ static void uec_configure_serdes(struct net_device *dev)
 	 * everything for us?  Resetting it takes the link down and requires
 	 * several seconds for it to come back.
 	 */
-	if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS)
+	if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) {
+		put_device(&tbiphy->dev);
 		return;
+	}
 
 	/* Single clk mode, mii mode off(for serdes communication) */
 	phy_write(tbiphy, ENET_TBI_MII_ANA, TBIANA_SETTINGS);
@@ -1711,6 +1715,8 @@ static void uec_configure_serdes(struct net_device *dev)
 	phy_write(tbiphy, ENET_TBI_MII_TBICON, TBICON_CLK_SELECT);
 
 	phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS);
+
+	put_device(&tbiphy->dev);
 }
 
 /* Configure the PHY for dev.
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index fe2299ac4f5c..0dce037a2682 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3173,6 +3173,8 @@ static int mvneta_probe(struct platform_device *pdev)
 		struct phy_device *phy = of_phy_find_device(dn);
 
 		mvneta_fixed_link_update(pp, phy);
+
+		put_device(&phy->dev);
 	}
 
 	return 0;
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index 6008eee01a33..cf468c87ce57 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -828,6 +828,8 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
 		if (!phydev)
 			dev_info(dev,
 				 "MDIO of the phy is not registered yet\n");
+		else
+			put_device(&phydev->dev);
 		return 0;
 	}
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state()
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (11 preceding siblings ...)
  2015-09-18  9:55 ` [PATCH 5/7] net: fix phy refcounting in a bunch of drivers Russell King
@ 2015-09-18  9:55 ` Russell King
  2015-09-18  9:55 ` [PATCH 7/7] phy: add phy_device_remove() Russell King
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:55 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni, linux-kernel

Validate that the phy_device passed into fixed_phy_update_state() is a
fixed-phy device before walking the list of phys for a fixed phy at the
same address.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/phy/fixed_phy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index fb1299c6326e..e23bf5b90e17 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -220,7 +220,7 @@ int fixed_phy_update_state(struct phy_device *phydev,
 	struct fixed_mdio_bus *fmb = &platform_fmb;
 	struct fixed_phy *fp;
 
-	if (!phydev || !phydev->bus)
+	if (!phydev || phydev->bus != fmb->mii_bus)
 		return -EINVAL;
 
 	list_for_each_entry(fp, &fmb->phys, node) {
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 7/7] phy: add phy_device_remove()
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (12 preceding siblings ...)
  2015-09-18  9:55 ` [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
@ 2015-09-18  9:55 ` Russell King
  2015-09-18  9:56 ` [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
  2015-09-19 20:49 ` Florian Fainelli
  15 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-18  9:55 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linuxppc-dev, Li Yang,
	Michal Simek, netdev, Robert Richter, Rob Herring,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni, linux-kernel

Add a phy_device_remove() function to complement phy_device_register(),
which undoes the effects of phy_device_register() by removing the phy
device from visibility, but not freeing it.

This allows these details to be moved out of the mdio bus code into
the phy code where this action belongs.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/ethernet/freescale/gianfar.c |  5 +++--
 drivers/net/phy/mdio_bus.c               | 15 ++++++++++-----
 drivers/net/phy/phy_device.c             | 18 ++++++++++++++++++
 include/linux/phy.h                      |  1 +
 4 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 65a16086faec..903211df3288 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -1702,7 +1702,6 @@ static void gfar_configure_serdes(struct net_device *dev)
 	tbiphy = of_phy_find_device(priv->tbi_node);
 	if (!tbiphy) {
 		dev_err(&dev->dev, "error: Could not get TBI device\n");
-		put_device(&tbiphy->dev);
 		return;
 	}
 
@@ -1711,8 +1710,10 @@ static void gfar_configure_serdes(struct net_device *dev)
 	 * everything for us?  Resetting it takes the link down and requires
 	 * several seconds for it to come back.
 	 */
-	if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS)
+	if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) {
+		put_device(&tbiphy->dev);
 		return;
+	}
 
 	/* Single clk mode, mii mode off(for serdes communication) */
 	phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 992406624b7c..c340e412b38f 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -291,8 +291,11 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
 
 error:
 	while (--i >= 0) {
-		if (bus->phy_map[i])
-			device_unregister(&bus->phy_map[i]->dev);
+		struct phy_device *phydev = bus->phy_map[i];
+		if (phydev) {
+			phy_device_remove(phydev);
+			phy_device_free(phydev);
+		}
 	}
 	device_del(&bus->dev);
 	return err;
@@ -307,9 +310,11 @@ void mdiobus_unregister(struct mii_bus *bus)
 	bus->state = MDIOBUS_UNREGISTERED;
 
 	for (i = 0; i < PHY_MAX_ADDR; i++) {
-		if (bus->phy_map[i])
-			device_unregister(&bus->phy_map[i]->dev);
-		bus->phy_map[i] = NULL;
+		struct phy_device *phydev = bus->phy_map[i];
+		if (phydev) {
+			phy_device_remove(phydev);
+			phy_device_free(phydev);
+		}
 	}
 	device_del(&bus->dev);
 }
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 97a4f52addac..f761288abe66 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -384,6 +384,24 @@ int phy_device_register(struct phy_device *phydev)
 EXPORT_SYMBOL(phy_device_register);
 
 /**
+ * phy_device_remove - Remove a previously registered phy device from the MDIO bus
+ * @phydev: phy_device structure to remove
+ *
+ * This doesn't free the phy_device itself, it merely reverses the effects
+ * of phy_device_register(). Use phy_device_free() to free the device
+ * after calling this function.
+ */
+void phy_device_remove(struct phy_device *phydev)
+{
+	struct mii_bus *bus = phydev->bus;
+	int addr = phydev->addr;
+
+	device_del(&phydev->dev);
+	bus->phy_map[addr] = NULL;
+}
+EXPORT_SYMBOL(phy_device_remove);
+
+/**
  * phy_find_first - finds the first PHY device on the bus
  * @bus: the target MII bus
  */
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 11bce44f6d65..4a4e3a092337 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -745,6 +745,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
 				     struct phy_c45_device_ids *c45_ids);
 struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45);
 int phy_device_register(struct phy_device *phy);
+void phy_device_remove(struct phy_device *phydev);
 int phy_init_hw(struct phy_device *phydev);
 int phy_suspend(struct phy_device *phydev);
 int phy_resume(struct phy_device *phydev);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/7] Phy and mdiobus fixes
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (13 preceding siblings ...)
  2015-09-18  9:55 ` [PATCH 7/7] phy: add phy_device_remove() Russell King
@ 2015-09-18  9:56 ` Russell King - ARM Linux
  2015-09-18 15:01   ` Sören Brinkmann
  2015-09-19 20:49 ` Florian Fainelli
  15 siblings, 1 reply; 22+ messages in thread
From: Russell King - ARM Linux @ 2015-09-18  9:56 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Thomas Petazzoni, devicetree, Sunil Goutham, Robert Richter,
	Frank Rowand, linuxppc-dev, linux-kernel, Rob Herring,
	Michal Simek, netdev, Sören Brinkmann, Iyappan Subramanian,
	Grant Likely, Li Yang, Keyur Chudgar, linux-arm-kernel

Sorry guys, some of you will get the patches twice, as Sören's name
in the header caused vger to reject all the patches.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/7] Phy and mdiobus fixes
  2015-09-18  9:56 ` [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
@ 2015-09-18 15:01   ` Sören Brinkmann
  2015-09-18 15:20     ` Russell King - ARM Linux
  0 siblings, 1 reply; 22+ messages in thread
From: Sören Brinkmann @ 2015-09-18 15:01 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Florian Fainelli, Thomas Petazzoni, devicetree, Sunil Goutham,
	Robert Richter, Frank Rowand, linuxppc-dev, linux-kernel,
	Rob Herring, Michal Simek, netdev, Iyappan Subramanian,
	Grant Likely, Li Yang, Keyur Chudgar, linux-arm-kernel

Hi Russell,

On Fri, 2015-09-18 at 10:56AM +0100, Russell King - ARM Linux wrote:
> Sorry guys, some of you will get the patches twice, as Sören's name
> in the header caused vger to reject all the patches.

That is the first time I hear about an issue like that. I've been
receiving patches fine thus far and nobody reported any rejections (by
vger) to me. Is it some bounce on Xilinx/my side or is vger suddenly
rejecting non-ascii chars or is something in the mail processing chain
not properly encoding those chars?
Please, let me know if I can help with the problem.

	Thanks,
	Sören

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/7] Phy and mdiobus fixes
  2015-09-18 15:01   ` Sören Brinkmann
@ 2015-09-18 15:20     ` Russell King - ARM Linux
  0 siblings, 0 replies; 22+ messages in thread
From: Russell King - ARM Linux @ 2015-09-18 15:20 UTC (permalink / raw)
  To: Sören Brinkmann
  Cc: Florian Fainelli, Thomas Petazzoni, devicetree, Sunil Goutham,
	Robert Richter, Frank Rowand, linuxppc-dev, linux-kernel,
	Rob Herring, Michal Simek, netdev, Iyappan Subramanian,
	Grant Likely, Li Yang, Keyur Chudgar, linux-arm-kernel

On Fri, Sep 18, 2015 at 08:01:28AM -0700, Sören Brinkmann wrote:
> Hi Russell,
> 
> On Fri, 2015-09-18 at 10:56AM +0100, Russell King - ARM Linux wrote:
> > Sorry guys, some of you will get the patches twice, as Sören's name
> > in the header caused vger to reject all the patches.
> 
> That is the first time I hear about an issue like that. I've been
> receiving patches fine thus far and nobody reported any rejections (by
> vger) to me. Is it some bounce on Xilinx/my side or is vger suddenly
> rejecting non-ascii chars or is something in the mail processing chain
> not properly encoding those chars?
> Please, let me know if I can help with the problem.

It's to do with how I generate the patch set and submit it to my MTA.
The result is that the header fields contain an ö.  Almost all MTAs
accept this despite it being questionable, but zmailer is extra fussy
and rejects this - and rejected the messages to lkml, netdev, etc.

Normally, the MUA would encode "Sören Brinkmann" in a header into a
7-bit ascii representation as "=?utf-8?B?U8O2cmVu?= Brinkmann".

It's one of the many issues of not using a MUA to send out patches.

It's also worth noting that git-send-email misses out mime headers
(or used to - which is why I have my own scripts for doing this)
which technically makes the bodies it mails out in violation of the
RFCs.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/7] Phy and mdiobus fixes
  2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
                   ` (14 preceding siblings ...)
  2015-09-18  9:56 ` [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
@ 2015-09-19 20:49 ` Florian Fainelli
  15 siblings, 0 replies; 22+ messages in thread
From: Florian Fainelli @ 2015-09-19 20:49 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: devicetree, Frank Rowand, Grant Likely, Iyappan Subramanian,
	Keyur Chudgar, linux-arm-kernel, linux-kernel, linuxppc-dev,
	Li Yang, Michal Simek, netdev, Robert Richter, Rob Herring,
	Sören Brinkmann, Sunil Goutham, Thomas Petazzoni

Le 09/18/15 02:46, Russell King - ARM Linux a écrit :
> Hi,
> 
> While looking at the phy code, I identified a number of weaknesses
> where refcounting on device structures was being leaked, where
> modules could be removed while in-use, and where the fixed-phy could
> end up having unintended consequences caused by incorrect calls to
> fixed_phy_update_state().
> 
> This patch series resolves those issues, some of which were discovered
> with testing on an Armada 388 board.  Not all patches are fully tested,
> particularly the one which touches several network drivers.
> 
> When resolving the struct device refcounting problems, several different
> solutions were considered before settling on the implementation here -
> one of the considerations was to avoid touching many network drivers.
> The solution here is:
> 
> 	phy_attach*() - takes a refcount
> 	phy_detach*() - drops the phy_attach refcount
> 
> Provided drivers always attach and detach their phys, which they should
> already be doing, this should change nothing, even if they leak a refcount.
> 
> 	of_phy_find_device() and of_* functions which use that take
> 	a refcount.  Arrange for this refcount to be dropped once
> 	the phy is attached.
> 
> This is the reason why the previous change is important - we can't drop
> this refcount taken by of_phy_find_device() until something else holds
> a reference on the device.  This resolves the leaked refcount caused by
> using of_phy_connect() or of_phy_attach().
> 
> Even without the above changes, these drivers are leaking by calling
> of_phy_find_device().  These drivers are addressed by adding the
> appropriate release of that refcount.
> 
> The mdiobus code also suffered from the same kind of leak, but thankfully
> this only happened in one place - the mdio-mux code.
> 
> I also found that the try_module_get() in the phy layer code was utterly
> useless: phydev->dev.driver was guaranteed to always be NULL, so
> try_module_get() was always being called with a NULL argument.  I proved
> this with my SFP code, which declares its own MDIO bus - the module use
> count was never incremented irrespective of how I set the MDIO bus up.
> This allowed the MDIO bus code to be removed from the kernel while there
> were still PHYs attached to it.
> 
> One other bug was discovered: while using in-band-status with mvneta, it
> was found that if a real phy is attached with in-band-status enabled,
> and another ethernet interface is using the fixed-phy infrastructure, the
> interface using the fixed-phy infrastructure is configured according to
> the other interface using the in-band-status - which is caused by the
> fixed-phy code not verifying that the phy_device passed in is actually
> a fixed-phy device, rather than a real MDIO phy.
> 
> Lastly, having mdio_bus reversing phy_device_register() internals seems
> like a layering violation - it's trivial to move that code to the phy
> device layer.

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>

Thanks!
-- 
Florian

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak
  2015-09-18  9:54 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
@ 2015-09-21 19:01   ` David Miller
  2015-09-21 19:32     ` Russell King - ARM Linux
  0 siblings, 1 reply; 22+ messages in thread
From: David Miller @ 2015-09-21 19:01 UTC (permalink / raw)
  To: rmk+kernel
  Cc: f.fainelli, devicetree, frowand.list, grant.likely, isubramanian,
	kchudgar, linux-arm-kernel, linuxppc-dev, leoli, michal.simek,
	netdev, rric, robh+dt, soren.brinkmann, sgoutham,
	thomas.petazzoni, linux-kernel

From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Fri, 18 Sep 2015 10:54:55 +0100

> Update the comment, and arrange for the only user of this function
> to drop this refcount when disposing of a reference to it.

mdio_mux is not the only user of of_mdio_find_bus(), DSA uses it as
well.

So if anything this commit message is inaccurate.

I also wonder about this refcounting scheme.

If you are going to drop the inner device reference, then we take the
mdio bus returned from of_mdio_find_bus() what holds onto it and keeps
it from disappearing on us?

Don't we have to hold onto some reference count of some kind here?

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak
  2015-09-21 19:01   ` David Miller
@ 2015-09-21 19:32     ` Russell King - ARM Linux
  2015-09-21 22:08       ` David Miller
  0 siblings, 1 reply; 22+ messages in thread
From: Russell King - ARM Linux @ 2015-09-21 19:32 UTC (permalink / raw)
  To: David Miller
  Cc: f.fainelli, devicetree, frowand.list, grant.likely, isubramanian,
	kchudgar, linux-arm-kernel, linuxppc-dev, leoli, michal.simek,
	netdev, rric, robh+dt, soren.brinkmann, sgoutham,
	thomas.petazzoni, linux-kernel

On Mon, Sep 21, 2015 at 12:01:59PM -0700, David Miller wrote:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
> Date: Fri, 18 Sep 2015 10:54:55 +0100
> 
> > Update the comment, and arrange for the only user of this function
> > to drop this refcount when disposing of a reference to it.
> 
> mdio_mux is not the only user of of_mdio_find_bus(), DSA uses it as
> well.
> 
> So if anything this commit message is inaccurate.

Yes, I missed that as it wasn't under drivers/net.  It doesn't change
the validity of this patch, the existing code is wrong and I'm not
introducing anything that makes the code any more wrong than it is.

I'll fix the commit message, and I'll fix the DSA code too but in a
separate patch.  Thanks for pointing it out.

> I also wonder about this refcounting scheme.

It's the standard driver model refcounting rules that we've lived with
for about a decade, ever since the driver model was introduced by
Patrick Mochel.

> If you are going to drop the inner device reference, then we take the
> mdio bus returned from of_mdio_find_bus() what holds onto it and keeps
> it from disappearing on us?
> 
> Don't we have to hold onto some reference count of some kind here?

In the case of the mdio mux code, I'm dropping the reference when
either (a) we've encountered an error during initialisation and we're
cleaning up, or (b) when the mdio mux code is being torn down after
the mdiomux bus has been unregistered and freed.  In both cases, we're
done with the mdio bus that was returned from of_mdio_find_bus().

In case (a), the devres code will release the kmalloc'd memory when
mdio_mux_gpio_probe() or mdio_mux_mmioreg_probe() propagates the error
out of their probe() function.

I'm not sure why you think anything is wrong here - maybe it's the odd
code structure to the success path at the bottom of mdio_mux_init()?

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak
  2015-09-21 19:32     ` Russell King - ARM Linux
@ 2015-09-21 22:08       ` David Miller
  0 siblings, 0 replies; 22+ messages in thread
From: David Miller @ 2015-09-21 22:08 UTC (permalink / raw)
  To: linux
  Cc: f.fainelli, devicetree, frowand.list, grant.likely, isubramanian,
	kchudgar, linux-arm-kernel, linuxppc-dev, leoli, michal.simek,
	netdev, rric, robh+dt, soren.brinkmann, sgoutham,
	thomas.petazzoni, linux-kernel

From: Russell King - ARM Linux <linux@arm.linux.org.uk>
Date: Mon, 21 Sep 2015 20:32:07 +0100

> In the case of the mdio mux code, I'm dropping the reference when
> either (a) we've encountered an error during initialisation and
> we're cleaning up, or (b) when the mdio mux code is being torn down
> after the mdiomux bus has been unregistered and freed.  In both
> cases, we're done with the mdio bus that was returned from
> of_mdio_find_bus().
> 
> In case (a), the devres code will release the kmalloc'd memory when
> mdio_mux_gpio_probe() or mdio_mux_mmioreg_probe() propagates the error
> out of their probe() function.
> 
> I'm not sure why you think anything is wrong here - maybe it's the odd
> code structure to the success path at the bottom of mdio_mux_init()?

Ok I may have misread your change.  I'll restudy it when you respin
the series with the commit message fixed and the DSA change added.

Thanks.

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2015-09-21 22:08 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-18  9:46 [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
2015-09-18  9:47 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
2015-09-18  9:47 ` [PATCH 2/7] phy: fix mdiobus module safety Russell King
2015-09-18  9:47 ` [PATCH 3/7] phy: add proper phy struct device refcounting Russell King
2015-09-18  9:47 ` [PATCH 4/7] of_mdio: fix MDIO phy " Russell King
2015-09-18  9:47 ` [PATCH 5/7] net: fix phy refcounting in a bunch of drivers Russell King
2015-09-18  9:47 ` [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
2015-09-18  9:47 ` [PATCH 7/7] phy: add phy_device_remove() Russell King
2015-09-18  9:54 ` [PATCH 1/7] phy: fix of_mdio_find_bus() device refcount leak Russell King
2015-09-21 19:01   ` David Miller
2015-09-21 19:32     ` Russell King - ARM Linux
2015-09-21 22:08       ` David Miller
2015-09-18  9:55 ` [PATCH 2/7] phy: fix mdiobus module safety Russell King
2015-09-18  9:55 ` [PATCH 3/7] phy: add proper phy struct device refcounting Russell King
2015-09-18  9:55 ` [PATCH 4/7] of_mdio: fix MDIO phy " Russell King
2015-09-18  9:55 ` [PATCH 5/7] net: fix phy refcounting in a bunch of drivers Russell King
2015-09-18  9:55 ` [PATCH 6/7] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
2015-09-18  9:55 ` [PATCH 7/7] phy: add phy_device_remove() Russell King
2015-09-18  9:56 ` [PATCH 0/7] Phy and mdiobus fixes Russell King - ARM Linux
2015-09-18 15:01   ` Sören Brinkmann
2015-09-18 15:20     ` Russell King - ARM Linux
2015-09-19 20:49 ` Florian Fainelli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).