linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes
@ 2015-09-22 16:17 Russell King - ARM Linux
  2015-09-22 16:18 ` [PATCH 1/9] phy: fix of_mdio_find_bus() device refcount leak Russell King
                   ` (18 more replies)
  0 siblings, 19 replies; 22+ messages in thread
From: Russell King - ARM Linux @ 2015-09-22 16:17 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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,
	Soren Brinkmann, Sunil Goutham, Thomas Petazzoni

This is the second version of the series, with the comments David had
on the first patch fixed up.  Original series description with updated
diffstat below.

While looking at the DSA code, I noticed we have a
of_find_net_device_by_node(), and it looks like users of that are
similarly buggy - it looks like net/dsa/dsa.c is the only user.  Fix
that too.

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 ++-
 net/core/net-sysfs.c                              |  9 ++++
 net/dsa/dsa.c                                     | 41 ++++++++++++---
 14 files changed, 199 insertions(+), 51 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/9] phy: fix of_mdio_find_bus() device refcount leak
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 2/9] net: dsa: " Russell King
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 phy code 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/9] net: dsa: fix of_mdio_find_bus() device refcount leak
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
  2015-09-22 16:18 ` [PATCH 1/9] phy: fix of_mdio_find_bus() device refcount leak Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 3/9] phy: fix mdiobus module safety Russell King
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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

Current users of of_mdio_find_bus() leak a struct device refcount, as
they fail to clean up the reference obtained inside class_find_device().

Fix the DSA code to properly refcount the returned MDIO bus by:
1. taking a reference on the struct device whenever we assign it to
   pd->chip[x].host_dev.
2. dropping the reference when we overwrite the existing reference.
3. dropping the reference when we free the data structure.
4. dropping the initial reference we obtained after setting up the
   platform data structure, or on failure.

In step 2 above, where we obtain a new MDIO bus, there is no need to
take a reference on it as we would only have to drop it immediately
after assignment again, iow:

	put_device(cd->host_dev);	/* drop original assignment ref */
	cd->host_dev = get_device(&mdio_bus_switch->dev); /* get our ref */
	put_device(&mdio_bus_switch->dev); /* drop of_mdio_find_bus ref */

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 net/dsa/dsa.c | 38 +++++++++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 76e3800765f8..bf4ba15fb780 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -634,6 +634,10 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
 			port_index++;
 		}
 		kfree(pd->chip[i].rtable);
+
+		/* Drop our reference to the MDIO bus device */
+		if (pd->chip[i].host_dev)
+			put_device(pd->chip[i].host_dev);
 	}
 	kfree(pd->chip);
 }
@@ -661,16 +665,22 @@ static int dsa_of_probe(struct device *dev)
 		return -EPROBE_DEFER;
 
 	ethernet = of_parse_phandle(np, "dsa,ethernet", 0);
-	if (!ethernet)
-		return -EINVAL;
+	if (!ethernet) {
+		ret = -EINVAL;
+		goto out_put_mdio;
+	}
 
 	ethernet_dev = of_find_net_device_by_node(ethernet);
-	if (!ethernet_dev)
-		return -EPROBE_DEFER;
+	if (!ethernet_dev) {
+		ret = -EPROBE_DEFER;
+		goto out_put_mdio;
+	}
 
 	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
-	if (!pd)
-		return -ENOMEM;
+	if (!pd) {
+		ret = -ENOMEM;
+		goto out_put_mdio;
+	}
 
 	dev->platform_data = pd;
 	pd->of_netdev = ethernet_dev;
@@ -691,7 +701,9 @@ static int dsa_of_probe(struct device *dev)
 		cd = &pd->chip[chip_index];
 
 		cd->of_node = child;
-		cd->host_dev = &mdio_bus->dev;
+
+		/* When assigning the host device, increment its refcount */
+		cd->host_dev = get_device(&mdio_bus->dev);
 
 		sw_addr = of_get_property(child, "reg", NULL);
 		if (!sw_addr)
@@ -711,6 +723,12 @@ static int dsa_of_probe(struct device *dev)
 				ret = -EPROBE_DEFER;
 				goto out_free_chip;
 			}
+
+			/* Drop the mdio_bus device ref, replacing the host
+			 * device with the mdio_bus_switch device, keeping
+			 * the refcount from of_mdio_find_bus() above.
+			 */
+			put_device(cd->host_dev);
 			cd->host_dev = &mdio_bus_switch->dev;
 		}
 
@@ -744,6 +762,10 @@ static int dsa_of_probe(struct device *dev)
 		}
 	}
 
+	/* The individual chips hold their own refcount on the mdio bus,
+	 * so drop ours */
+	put_device(&mdio_bus->dev);
+
 	return 0;
 
 out_free_chip:
@@ -751,6 +773,8 @@ static int dsa_of_probe(struct device *dev)
 out_free:
 	kfree(pd);
 	dev->platform_data = NULL;
+out_put_mdio:
+	put_device(&mdio_bus->dev);
 	return ret;
 }
 
-- 
2.1.0

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

* [PATCH 3/9] phy: fix mdiobus module safety
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
  2015-09-22 16:18 ` [PATCH 1/9] phy: fix of_mdio_find_bus() device refcount leak Russell King
  2015-09-22 16:18 ` [PATCH 2/9] net: dsa: " Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 4/9] phy: add proper phy struct device refcounting Russell King
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 4/9] phy: add proper phy struct device refcounting
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (2 preceding siblings ...)
  2015-09-22 16:18 ` [PATCH 3/9] phy: fix mdiobus module safety Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 5/9] of_mdio: fix MDIO phy " Russell King
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 5/9] of_mdio: fix MDIO phy device refcounting
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (3 preceding siblings ...)
  2015-09-22 16:18 ` [PATCH 4/9] phy: add proper phy struct device refcounting Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 6/9] net: fix phy refcounting in a bunch of drivers Russell King
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 6/9] net: fix phy refcounting in a bunch of drivers
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (4 preceding siblings ...)
  2015-09-22 16:18 ` [PATCH 5/9] of_mdio: fix MDIO phy " Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state()
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (5 preceding siblings ...)
  2015-09-22 16:18 ` [PATCH 6/9] net: fix phy refcounting in a bunch of drivers Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 8/9] phy: add phy_device_remove() Russell King
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 8/9] phy: add phy_device_remove()
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (6 preceding siblings ...)
  2015-09-22 16:18 ` [PATCH 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-22 16:18 ` [PATCH 9/9] net: fix net_device refcounting Russell King
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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

* [PATCH 9/9] net: fix net_device refcounting
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (7 preceding siblings ...)
  2015-09-22 16:18 ` [PATCH 8/9] phy: add phy_device_remove() Russell King
@ 2015-09-22 16:18 ` Russell King
  2015-09-23 23:24 ` [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes David Miller
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-22 16:18 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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_find_net_device_by_node() uses class_find_device() internally to
lookup the corresponding network device.  class_find_device() returns
a reference to the embedded struct device, with its refcount
incremented.

Add a comment to the definition in net/core/net-sysfs.c indicating the
need to drop this refcount, and fix the DSA code to drop this refcount
when the OF-generated platform data is cleaned up and freed.  Also
arrange for the ref to be dropped when handling errors.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 net/core/net-sysfs.c | 9 +++++++++
 net/dsa/dsa.c        | 5 ++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index b279077c3089..805a95a48107 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -1481,6 +1481,15 @@ static int of_dev_node_match(struct device *dev, const void *data)
 	return ret == 0 ? dev->of_node == data : ret;
 }
 
+/*
+ * of_find_net_device_by_node - lookup the net device for the device node
+ * @np: OF device node
+ *
+ * Looks up the net_device structure corresponding with the device node.
+ * If successful, returns a pointer to the net_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure. The
+ * refcount must be dropped when done with the net_device.
+ */
 struct net_device *of_find_net_device_by_node(struct device_node *np)
 {
 	struct device *dev;
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index bf4ba15fb780..c59fa5d9c22c 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -679,7 +679,7 @@ static int dsa_of_probe(struct device *dev)
 	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
 	if (!pd) {
 		ret = -ENOMEM;
-		goto out_put_mdio;
+		goto out_put_ethernet;
 	}
 
 	dev->platform_data = pd;
@@ -773,6 +773,8 @@ static int dsa_of_probe(struct device *dev)
 out_free:
 	kfree(pd);
 	dev->platform_data = NULL;
+out_put_ethernet:
+	put_device(&ethernet_dev->dev);
 out_put_mdio:
 	put_device(&mdio_bus->dev);
 	return ret;
@@ -786,6 +788,7 @@ static void dsa_of_remove(struct device *dev)
 		return;
 
 	dsa_of_free_platform_data(pd);
+	put_device(&pd->of_netdev->dev);
 	kfree(pd);
 }
 #else
-- 
2.1.0

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

* Re: [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (8 preceding siblings ...)
  2015-09-22 16:18 ` [PATCH 9/9] net: fix net_device refcounting Russell King
@ 2015-09-23 23:24 ` David Miller
  2015-09-24 16:00   ` Russell King - ARM Linux
  2015-09-24 19:35 ` [PATCH RESEND v3 1/9] phy: fix of_mdio_find_bus() device refcount leak Russell King
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 22+ messages in thread
From: David Miller @ 2015-09-23 23:24 UTC (permalink / raw)
  To: linux
  Cc: f.fainelli, devicetree, frowand.list, grant.likely, isubramanian,
	kchudgar, linux-arm-kernel, linux-kernel, linuxppc-dev, leoli,
	michal.simek, netdev, rric, robh+dt, soren.brinkmann, sgoutham,
	thomas.petazzoni

RnJvbTogUnVzc2VsbCBLaW5nIC0gQVJNIExpbnV4IDxsaW51eEBhcm0ubGludXgub3JnLnVrPg0K
RGF0ZTogVHVlLCAyMiBTZXAgMjAxNSAxNzoxNzoxMCArMDEwMA0KDQo+IFRoaXMgaXMgdGhlIHNl
Y29uZCB2ZXJzaW9uIG9mIHRoZSBzZXJpZXMsIHdpdGggdGhlIGNvbW1lbnRzIERhdmlkIGhhZA0K
PiBvbiB0aGUgZmlyc3QgcGF0Y2ggZml4ZWQgdXAuICBPcmlnaW5hbCBzZXJpZXMgZGVzY3JpcHRp
b24gd2l0aCB1cGRhdGVkDQo+IGRpZmZzdGF0IGJlbG93Lg0KDQpUaGlzIG5lZWRzIHNvbWUgYnVp
bGQgZml4ZXM6DQoNCmRyaXZlcnMvbmV0L2V0aGVybmV0L2FwbS94Z2VuZS94Z2VuZV9lbmV0X2h3
LmM6IEluIGZ1bmN0aW9uIKF4Z2VuZV9lbmV0X3BoeV9jb25uZWN0ojoNCmRyaXZlcnMvbmV0L2V0
aGVybmV0L2FwbS94Z2VuZS94Z2VuZV9lbmV0X2h3LmM6Njk0OjIwOiBlcnJvcjogdG9vIGZldyBh
cmd1bWVudHMgdG8gZnVuY3Rpb24goW9mX3BoeV9jb25uZWN0og0KICAgcGRhdGEtPnBoeV9kZXYg
PSBvZl9waHlfY29ubmVjdChuZGV2LCBwaHlfbnAsDQogICAgICAgICAgICAgICAgICAgIF4NCklu
IGZpbGUgaW5jbHVkZWQgZnJvbSBkcml2ZXJzL25ldC9ldGhlcm5ldC9hcG0veGdlbmUveGdlbmVf
ZW5ldF9tYWluLmg6MzE6MCwNCiAgICAgICAgICAgICAgICAgZnJvbSBkcml2ZXJzL25ldC9ldGhl
cm5ldC9hcG0veGdlbmUveGdlbmVfZW5ldF9ody5jOjIyOg0KaW5jbHVkZS9saW51eC9vZl9tZGlv
Lmg6MTg6Mjc6IG5vdGU6IGRlY2xhcmVkIGhlcmUNCiBleHRlcm4gc3RydWN0IHBoeV9kZXZpY2Ug
Km9mX3BoeV9jb25uZWN0KHN0cnVjdCBuZXRfZGV2aWNlICpkZXYsDQogICAgICAgICAgICAgICAg
ICAgICAgICAgICBeDQo=

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

* Re: [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes
  2015-09-23 23:24 ` [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes David Miller
@ 2015-09-24 16:00   ` Russell King - ARM Linux
  0 siblings, 0 replies; 22+ messages in thread
From: Russell King - ARM Linux @ 2015-09-24 16:00 UTC (permalink / raw)
  To: David Miller
  Cc: f.fainelli, devicetree, frowand.list, grant.likely, isubramanian,
	kchudgar, linux-arm-kernel, linux-kernel, linuxppc-dev, leoli,
	michal.simek, netdev, rric, robh+dt, soren.brinkmann, sgoutham,
	thomas.petazzoni

On Wed, Sep 23, 2015 at 04:24:17PM -0700, David Miller wrote:
> From: Russell King - ARM Linux <linux@arm.linux.org.uk>
> Date: Tue, 22 Sep 2015 17:17:10 +0100
> 
> > This is the second version of the series, with the comments David had
> > on the first patch fixed up.  Original series description with updated
> > diffstat below.
> 
> This needs some build fixes:
> 
> drivers/net/ethernet/apm/xgene/xgene_enet_hw.c: In function ‘xgene_enet_phy_connect’:
> drivers/net/ethernet/apm/xgene/xgene_enet_hw.c:694:20: error: too few arguments to function ‘of_phy_connect’
>    pdata->phy_dev = of_phy_connect(ndev, phy_np,
>                     ^
> In file included from drivers/net/ethernet/apm/xgene/xgene_enet_main.h:31:0,
>                  from drivers/net/ethernet/apm/xgene/xgene_enet_hw.c:22:
> include/linux/of_mdio.h:18:27: note: declared here
>  extern struct phy_device *of_phy_connect(struct net_device *dev,
>                            ^

Sorry about that, I'll send a new series later this evening.

Unfortunately, that's also not the only one - Cavium Thunder BGX also
fails.  Looking again at that driver, the driver structure doesn't lend
itself well to having the refcount balancing added to it due to the
multiple different paths which it obtains a phy_device.  I think this is
best left to the maintainers of this driver to fix, so I'm intending to
drop the change to that driver.

That's not a problem: the driver is already buggy in this regard today,
and that's won't be any different as a result of omitting it.  It's just
a shame to leave one instance unfixed.

-- 
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 RESEND v3 1/9] phy: fix of_mdio_find_bus() device refcount leak
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (9 preceding siblings ...)
  2015-09-23 23:24 ` [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes David Miller
@ 2015-09-24 19:35 ` Russell King
  2015-09-24 19:35 ` [PATCH RESEND v3 2/9] net: dsa: " Russell King
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:35 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 phy code 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 RESEND v3 2/9] net: dsa: fix of_mdio_find_bus() device refcount leak
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (10 preceding siblings ...)
  2015-09-24 19:35 ` [PATCH RESEND v3 1/9] phy: fix of_mdio_find_bus() device refcount leak Russell King
@ 2015-09-24 19:35 ` Russell King
  2015-09-24 19:36 ` [PATCH RESEND v3 3/9] phy: fix mdiobus module safety Russell King
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:35 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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

Current users of of_mdio_find_bus() leak a struct device refcount, as
they fail to clean up the reference obtained inside class_find_device().

Fix the DSA code to properly refcount the returned MDIO bus by:
1. taking a reference on the struct device whenever we assign it to
   pd->chip[x].host_dev.
2. dropping the reference when we overwrite the existing reference.
3. dropping the reference when we free the data structure.
4. dropping the initial reference we obtained after setting up the
   platform data structure, or on failure.

In step 2 above, where we obtain a new MDIO bus, there is no need to
take a reference on it as we would only have to drop it immediately
after assignment again, iow:

	put_device(cd->host_dev);	/* drop original assignment ref */
	cd->host_dev = get_device(&mdio_bus_switch->dev); /* get our ref */
	put_device(&mdio_bus_switch->dev); /* drop of_mdio_find_bus ref */

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 net/dsa/dsa.c | 38 +++++++++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 76e3800765f8..bf4ba15fb780 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -634,6 +634,10 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
 			port_index++;
 		}
 		kfree(pd->chip[i].rtable);
+
+		/* Drop our reference to the MDIO bus device */
+		if (pd->chip[i].host_dev)
+			put_device(pd->chip[i].host_dev);
 	}
 	kfree(pd->chip);
 }
@@ -661,16 +665,22 @@ static int dsa_of_probe(struct device *dev)
 		return -EPROBE_DEFER;
 
 	ethernet = of_parse_phandle(np, "dsa,ethernet", 0);
-	if (!ethernet)
-		return -EINVAL;
+	if (!ethernet) {
+		ret = -EINVAL;
+		goto out_put_mdio;
+	}
 
 	ethernet_dev = of_find_net_device_by_node(ethernet);
-	if (!ethernet_dev)
-		return -EPROBE_DEFER;
+	if (!ethernet_dev) {
+		ret = -EPROBE_DEFER;
+		goto out_put_mdio;
+	}
 
 	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
-	if (!pd)
-		return -ENOMEM;
+	if (!pd) {
+		ret = -ENOMEM;
+		goto out_put_mdio;
+	}
 
 	dev->platform_data = pd;
 	pd->of_netdev = ethernet_dev;
@@ -691,7 +701,9 @@ static int dsa_of_probe(struct device *dev)
 		cd = &pd->chip[chip_index];
 
 		cd->of_node = child;
-		cd->host_dev = &mdio_bus->dev;
+
+		/* When assigning the host device, increment its refcount */
+		cd->host_dev = get_device(&mdio_bus->dev);
 
 		sw_addr = of_get_property(child, "reg", NULL);
 		if (!sw_addr)
@@ -711,6 +723,12 @@ static int dsa_of_probe(struct device *dev)
 				ret = -EPROBE_DEFER;
 				goto out_free_chip;
 			}
+
+			/* Drop the mdio_bus device ref, replacing the host
+			 * device with the mdio_bus_switch device, keeping
+			 * the refcount from of_mdio_find_bus() above.
+			 */
+			put_device(cd->host_dev);
 			cd->host_dev = &mdio_bus_switch->dev;
 		}
 
@@ -744,6 +762,10 @@ static int dsa_of_probe(struct device *dev)
 		}
 	}
 
+	/* The individual chips hold their own refcount on the mdio bus,
+	 * so drop ours */
+	put_device(&mdio_bus->dev);
+
 	return 0;
 
 out_free_chip:
@@ -751,6 +773,8 @@ static int dsa_of_probe(struct device *dev)
 out_free:
 	kfree(pd);
 	dev->platform_data = NULL;
+out_put_mdio:
+	put_device(&mdio_bus->dev);
 	return ret;
 }
 
-- 
2.1.0

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

* [PATCH RESEND v3 3/9] phy: fix mdiobus module safety
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (11 preceding siblings ...)
  2015-09-24 19:35 ` [PATCH RESEND v3 2/9] net: dsa: " Russell King
@ 2015-09-24 19:36 ` Russell King
  2015-09-24 19:36 ` [PATCH RESEND v3 4/9] phy: add proper phy struct device refcounting Russell King
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:36 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 RESEND v3 4/9] phy: add proper phy struct device refcounting
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (12 preceding siblings ...)
  2015-09-24 19:36 ` [PATCH RESEND v3 3/9] phy: fix mdiobus module safety Russell King
@ 2015-09-24 19:36 ` Russell King
  2015-09-24 19:36 ` [PATCH RESEND v3 5/9] of_mdio: fix MDIO phy " Russell King
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:36 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 RESEND v3 5/9] of_mdio: fix MDIO phy device refcounting
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (13 preceding siblings ...)
  2015-09-24 19:36 ` [PATCH RESEND v3 4/9] phy: add proper phy struct device refcounting Russell King
@ 2015-09-24 19:36 ` Russell King
  2015-09-24 22:21   ` Rob Herring
  2015-09-24 19:36 ` [PATCH RESEND v3 6/9] net: fix phy refcounting in a bunch of drivers Russell King
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 22+ messages in thread
From: Russell King @ 2015-09-24 19:36 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 RESEND v3 6/9] net: fix phy refcounting in a bunch of drivers
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (14 preceding siblings ...)
  2015-09-24 19:36 ` [PATCH RESEND v3 5/9] of_mdio: fix MDIO phy " Russell King
@ 2015-09-24 19:36 ` Russell King
  2015-09-24 19:36 ` [PATCH RESEND v3 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:36 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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.

For xgene, looking back in the history, we should be able to use
of_phy_connect() with a zero flags argument for the DT case as this is
how the driver used to operate prior to de7b5b3d790a ("net: eth: xgene:
change APM X-Gene SoC platform ethernet to support ACPI").

This leaves the Cavium Thunder BGX unfixed; fixing this driver is a
complicated task, one which the maintainers need to be involved with.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 24 ++++++++++++++++--------
 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 ++
 5 files changed, 30 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..c4bb8027b3fb 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -689,16 +689,24 @@ 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;
+		phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link,
+					 0, pdata->phy_mode);
+		if (!phy_dev) {
+			netdev_err(ndev, "Could not connect to PHY\n");
+			return -ENODEV;
+		}
+
+		pdata->phy_dev = phy_dev;
+	} 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/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 RESEND v3 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state()
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (15 preceding siblings ...)
  2015-09-24 19:36 ` [PATCH RESEND v3 6/9] net: fix phy refcounting in a bunch of drivers Russell King
@ 2015-09-24 19:36 ` Russell King
  2015-09-24 19:36 ` [PATCH RESEND v3 8/9] phy: add phy_device_remove() Russell King
  2015-09-24 19:36 ` [PATCH RESEND v3 9/9] net: fix net_device refcounting Russell King
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:36 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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 RESEND v3 8/9] phy: add phy_device_remove()
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (16 preceding siblings ...)
  2015-09-24 19:36 ` [PATCH RESEND v3 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
@ 2015-09-24 19:36 ` Russell King
  2015-09-24 19:36 ` [PATCH RESEND v3 9/9] net: fix net_device refcounting Russell King
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:36 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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

* [PATCH RESEND v3 9/9] net: fix net_device refcounting
  2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
                   ` (17 preceding siblings ...)
  2015-09-24 19:36 ` [PATCH RESEND v3 8/9] phy: add phy_device_remove() Russell King
@ 2015-09-24 19:36 ` Russell King
  18 siblings, 0 replies; 22+ messages in thread
From: Russell King @ 2015-09-24 19:36 UTC (permalink / raw)
  To: Florian Fainelli, David Miller
  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_find_net_device_by_node() uses class_find_device() internally to
lookup the corresponding network device.  class_find_device() returns
a reference to the embedded struct device, with its refcount
incremented.

Add a comment to the definition in net/core/net-sysfs.c indicating the
need to drop this refcount, and fix the DSA code to drop this refcount
when the OF-generated platform data is cleaned up and freed.  Also
arrange for the ref to be dropped when handling errors.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 net/core/net-sysfs.c | 9 +++++++++
 net/dsa/dsa.c        | 5 ++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index b279077c3089..805a95a48107 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -1481,6 +1481,15 @@ static int of_dev_node_match(struct device *dev, const void *data)
 	return ret == 0 ? dev->of_node == data : ret;
 }
 
+/*
+ * of_find_net_device_by_node - lookup the net device for the device node
+ * @np: OF device node
+ *
+ * Looks up the net_device structure corresponding with the device node.
+ * If successful, returns a pointer to the net_device with the embedded
+ * struct device refcount incremented by one, or NULL on failure. The
+ * refcount must be dropped when done with the net_device.
+ */
 struct net_device *of_find_net_device_by_node(struct device_node *np)
 {
 	struct device *dev;
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index bf4ba15fb780..c59fa5d9c22c 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -679,7 +679,7 @@ static int dsa_of_probe(struct device *dev)
 	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
 	if (!pd) {
 		ret = -ENOMEM;
-		goto out_put_mdio;
+		goto out_put_ethernet;
 	}
 
 	dev->platform_data = pd;
@@ -773,6 +773,8 @@ static int dsa_of_probe(struct device *dev)
 out_free:
 	kfree(pd);
 	dev->platform_data = NULL;
+out_put_ethernet:
+	put_device(&ethernet_dev->dev);
 out_put_mdio:
 	put_device(&mdio_bus->dev);
 	return ret;
@@ -786,6 +788,7 @@ static void dsa_of_remove(struct device *dev)
 		return;
 
 	dsa_of_free_platform_data(pd);
+	put_device(&pd->of_netdev->dev);
 	kfree(pd);
 }
 #else
-- 
2.1.0

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

* Re: [PATCH RESEND v3 5/9] of_mdio: fix MDIO phy device refcounting
  2015-09-24 19:36 ` [PATCH RESEND v3 5/9] of_mdio: fix MDIO phy " Russell King
@ 2015-09-24 22:21   ` Rob Herring
  0 siblings, 0 replies; 22+ messages in thread
From: Rob Herring @ 2015-09-24 22:21 UTC (permalink / raw)
  To: Russell King
  Cc: Florian Fainelli, David Miller, devicetree, Frank Rowand,
	Grant Likely, Iyappan Subramanian, Keyur Chudgar,
	linux-arm-kernel, linuxppc-dev, Li Yang, Michal Simek, netdev,
	Robert Richter, Soren Brinkmann, Sunil Goutham, Thomas Petazzoni,
	linux-kernel

On Thu, Sep 24, 2015 at 2:36 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> 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>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  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	[flat|nested] 22+ messages in thread

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

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-22 16:17 [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes Russell King - ARM Linux
2015-09-22 16:18 ` [PATCH 1/9] phy: fix of_mdio_find_bus() device refcount leak Russell King
2015-09-22 16:18 ` [PATCH 2/9] net: dsa: " Russell King
2015-09-22 16:18 ` [PATCH 3/9] phy: fix mdiobus module safety Russell King
2015-09-22 16:18 ` [PATCH 4/9] phy: add proper phy struct device refcounting Russell King
2015-09-22 16:18 ` [PATCH 5/9] of_mdio: fix MDIO phy " Russell King
2015-09-22 16:18 ` [PATCH 6/9] net: fix phy refcounting in a bunch of drivers Russell King
2015-09-22 16:18 ` [PATCH 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
2015-09-22 16:18 ` [PATCH 8/9] phy: add phy_device_remove() Russell King
2015-09-22 16:18 ` [PATCH 9/9] net: fix net_device refcounting Russell King
2015-09-23 23:24 ` [PATCH v2 0/9] Phy, mdiobus, and netdev struct device fixes David Miller
2015-09-24 16:00   ` Russell King - ARM Linux
2015-09-24 19:35 ` [PATCH RESEND v3 1/9] phy: fix of_mdio_find_bus() device refcount leak Russell King
2015-09-24 19:35 ` [PATCH RESEND v3 2/9] net: dsa: " Russell King
2015-09-24 19:36 ` [PATCH RESEND v3 3/9] phy: fix mdiobus module safety Russell King
2015-09-24 19:36 ` [PATCH RESEND v3 4/9] phy: add proper phy struct device refcounting Russell King
2015-09-24 19:36 ` [PATCH RESEND v3 5/9] of_mdio: fix MDIO phy " Russell King
2015-09-24 22:21   ` Rob Herring
2015-09-24 19:36 ` [PATCH RESEND v3 6/9] net: fix phy refcounting in a bunch of drivers Russell King
2015-09-24 19:36 ` [PATCH RESEND v3 7/9] phy: fixed-phy: properly validate phy in fixed_phy_update_state() Russell King
2015-09-24 19:36 ` [PATCH RESEND v3 8/9] phy: add phy_device_remove() Russell King
2015-09-24 19:36 ` [PATCH RESEND v3 9/9] net: fix net_device refcounting Russell King

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).