net: phy: add device-managed devm_mdiobus_register
authorHeiner Kallweit <hkallweit1@gmail.com>
Mon, 20 Apr 2020 21:29:05 +0000 (23:29 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 22 Apr 2020 19:19:54 +0000 (12:19 -0700)
If there's no special ordering requirement for mdiobus_unregister(),
then driver code can be simplified by using a device-managed version
of mdiobus_register(). Prerequisite is that bus allocation has been
done device-managed too. Else mdiobus_free() may be called whilst
bus is still registered, resulting in a BUG_ON(). Therefore let
devm_mdiobus_register() return -EPERM if bus was allocated
non-managed.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/mdio_bus.c
include/linux/phy.h

index 346e884..26b00af 100644 (file)
@@ -170,7 +170,12 @@ EXPORT_SYMBOL(mdiobus_alloc_size);
 
 static void _devm_mdiobus_free(struct device *dev, void *res)
 {
-       mdiobus_free(*(struct mii_bus **)res);
+       struct mii_bus *bus = *(struct mii_bus **)res;
+
+       if (bus->is_managed_registered && bus->state == MDIOBUS_REGISTERED)
+               mdiobus_unregister(bus);
+
+       mdiobus_free(bus);
 }
 
 static int devm_mdiobus_match(struct device *dev, void *res, void *data)
@@ -210,6 +215,7 @@ struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv)
        if (bus) {
                *ptr = bus;
                devres_add(dev, ptr);
+               bus->is_managed = 1;
        } else {
                devres_free(ptr);
        }
index 2432ca4..3941a6b 100644 (file)
@@ -241,6 +241,9 @@ struct mii_bus {
        int (*reset)(struct mii_bus *bus);
        struct mdio_bus_stats stats[PHY_MAX_ADDR];
 
+       unsigned int is_managed:1;      /* is device-managed */
+       unsigned int is_managed_registered:1;
+
        /*
         * A lock to ensure that only one thing can read/write
         * the MDIO bus at a time
@@ -286,6 +289,20 @@ static inline struct mii_bus *mdiobus_alloc(void)
 
 int __mdiobus_register(struct mii_bus *bus, struct module *owner);
 #define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)
+static inline int devm_mdiobus_register(struct mii_bus *bus)
+{
+       int ret;
+
+       if (!bus->is_managed)
+               return -EPERM;
+
+       ret = mdiobus_register(bus);
+       if (!ret)
+               bus->is_managed_registered = 1;
+
+       return ret;
+}
+
 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);