From: Bartosz Golaszewski <bgolaszewski@xxxxxxxxxxxx> Provide devm_register_netdev() - a device resource managed variant of register_netdev(). This new helper will only work for net_device structs that have a parent device assigned and are devres managed too. Signed-off-by: Bartosz Golaszewski <bgolaszewski@xxxxxxxxxxxx> --- include/linux/netdevice.h | 4 ++++ net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++ net/ethernet/eth.c | 1 + 3 files changed, 53 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 130a668049ab..433bd5ca2efc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1515,6 +1515,8 @@ struct net_device_ops { * @IFF_FAILOVER_SLAVE: device is lower dev of a failover master device * @IFF_L3MDEV_RX_HANDLER: only invoke the rx handler of L3 master device * @IFF_LIVE_RENAME_OK: rename is allowed while device is up and running + * @IFF_IS_DEVRES: this structure was allocated dynamically and is managed by + * devres */ enum netdev_priv_flags { IFF_802_1Q_VLAN = 1<<0, @@ -1548,6 +1550,7 @@ enum netdev_priv_flags { IFF_FAILOVER_SLAVE = 1<<28, IFF_L3MDEV_RX_HANDLER = 1<<29, IFF_LIVE_RENAME_OK = 1<<30, + IFF_IS_DEVRES = 1<<31, }; #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN @@ -4206,6 +4209,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, count) int register_netdev(struct net_device *dev); +int devm_register_netdev(struct net_device *ndev); void unregister_netdev(struct net_device *dev); /* General hardware address lists handling functions */ diff --git a/net/core/dev.c b/net/core/dev.c index 522288177bbd..99db537c9468 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -9519,6 +9519,54 @@ int register_netdev(struct net_device *dev) } EXPORT_SYMBOL(register_netdev); +struct netdevice_devres { + struct net_device *ndev; +}; + +static void devm_netdev_release(struct device *dev, void *this) +{ + struct netdevice_devres *res = this; + + unregister_netdev(res->ndev); +} + +/** + * devm_register_netdev - resource managed variant of register_netdev() + * @ndev: device to register + * + * This is a devres variant of register_netdev() for which the unregister + * function will be call automatically when the parent device of ndev + * is detached. + */ +int devm_register_netdev(struct net_device *ndev) +{ + struct netdevice_devres *dr; + int ret; + + /* struct net_device itself must be devres managed. */ + BUG_ON(!(ndev->priv_flags & IFF_IS_DEVRES)); + /* struct net_device must have a parent device - it will be the device + * managing this resource. + */ + BUG_ON(!ndev->dev.parent); + + dr = devres_alloc(devm_netdev_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = register_netdev(ndev); + if (ret) { + devres_free(dr); + return ret; + } + + dr->ndev = ndev; + devres_add(ndev->dev.parent, dr); + + return 0; +} +EXPORT_SYMBOL(devm_register_netdev); + int netdev_refcnt_read(const struct net_device *dev) { int i, refcnt = 0; diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index c8b903302ff2..ce9b5e576f20 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -423,6 +423,7 @@ struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv, *dr = netdev; devres_add(dev, dr); + netdev->priv_flags |= IFF_IS_DEVRES; return netdev; } -- 2.25.0