Commit c0096a28 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-provide-a-devres-variant-of-register_netdev'

Bartosz Golaszewski says:

====================
net: provide a devres variant of register_netdev()

Using devres helpers allows to shrink the probing code, avoid memory leaks in
error paths make sure the order in which resources are freed is the exact
opposite of their allocation. This series proposes to add a devres variant
of register_netdev() that will only work with net_device structures whose
memory is also managed.

First we add the missing documentation entry for the only other networking
devres helper: devm_alloc_etherdev().

Next we move devm_alloc_etherdev() into a separate source file.

We then use a proxy structure in devm_alloc_etherdev() to improve readability.

Last: we implement devm_register_netdev() and use it in mtk-eth-mac driver.

v1 -> v2:
- rebase on top of net-next after driver rename, no functional changes
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 54b9aca0 9250dccc
...@@ -372,6 +372,11 @@ MUX ...@@ -372,6 +372,11 @@ MUX
devm_mux_chip_register() devm_mux_chip_register()
devm_mux_control_get() devm_mux_control_get()
NET
devm_alloc_etherdev()
devm_alloc_etherdev_mqs()
devm_register_netdev()
PER-CPU MEM PER-CPU MEM
devm_alloc_percpu() devm_alloc_percpu()
devm_free_percpu() devm_free_percpu()
......
...@@ -1519,13 +1519,6 @@ static void mtk_star_mdiobus_unregister(void *data) ...@@ -1519,13 +1519,6 @@ static void mtk_star_mdiobus_unregister(void *data)
mdiobus_unregister(priv->mii); mdiobus_unregister(priv->mii);
} }
static void mtk_star_unregister_netdev(void *data)
{
struct net_device *ndev = data;
unregister_netdev(ndev);
}
static int mtk_star_probe(struct platform_device *pdev) static int mtk_star_probe(struct platform_device *pdev)
{ {
struct device_node *of_node; struct device_node *of_node;
...@@ -1641,15 +1634,7 @@ static int mtk_star_probe(struct platform_device *pdev) ...@@ -1641,15 +1634,7 @@ static int mtk_star_probe(struct platform_device *pdev)
netif_napi_add(ndev, &priv->napi, mtk_star_poll, MTK_STAR_NAPI_WEIGHT); netif_napi_add(ndev, &priv->napi, mtk_star_poll, MTK_STAR_NAPI_WEIGHT);
ret = register_netdev(ndev); return devm_register_netdev(dev, ndev);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, mtk_star_unregister_netdev, ndev);
if (ret)
return ret;
return 0;
} }
static const struct of_device_id mtk_star_of_match[] = { static const struct of_device_id mtk_star_of_match[] = {
......
...@@ -4280,6 +4280,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, ...@@ -4280,6 +4280,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
int register_netdev(struct net_device *dev); int register_netdev(struct net_device *dev);
void unregister_netdev(struct net_device *dev); void unregister_netdev(struct net_device *dev);
int devm_register_netdev(struct device *dev, struct net_device *ndev);
/* General hardware address lists handling functions */ /* General hardware address lists handling functions */
int __hw_addr_sync(struct netdev_hw_addr_list *to_list, int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list, int addr_len); struct netdev_hw_addr_list *from_list, int addr_len);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
# Rewritten to use lists instead of if-statements. # Rewritten to use lists instead of if-statements.
# #
obj-$(CONFIG_NET) := socket.o core/ obj-$(CONFIG_NET) := devres.o socket.o core/
tmp-$(CONFIG_COMPAT) := compat.o tmp-$(CONFIG_COMPAT) := compat.o
obj-$(CONFIG_NET) += $(tmp-y) obj-$(CONFIG_NET) += $(tmp-y)
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file contains all networking devres helpers.
*/
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
struct net_device_devres {
struct net_device *ndev;
};
static void devm_free_netdev(struct device *dev, void *this)
{
struct net_device_devres *res = this;
free_netdev(res->ndev);
}
struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
unsigned int txqs, unsigned int rxqs)
{
struct net_device_devres *dr;
dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
if (!dr)
return NULL;
dr->ndev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
if (!dr->ndev) {
devres_free(dr);
return NULL;
}
devres_add(dev, dr);
return dr->ndev;
}
EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
static void devm_netdev_release(struct device *dev, void *this)
{
struct net_device_devres *res = this;
unregister_netdev(res->ndev);
}
static int netdev_devres_match(struct device *dev, void *this, void *match_data)
{
struct net_device_devres *res = this;
struct net_device *ndev = match_data;
return ndev == res->ndev;
}
/**
* devm_register_netdev - resource managed variant of register_netdev()
* @dev: managing device for this netdev - usually the parent device
* @ndev: device to register
*
* This is a devres variant of register_netdev() for which the unregister
* function will be call automatically when the managing device is
* detached. Note: the net_device used must also be resource managed by
* the same struct device.
*/
int devm_register_netdev(struct device *dev, struct net_device *ndev)
{
struct net_device_devres *dr;
int ret;
/* struct net_device must itself be managed. For now a managed netdev
* can only be allocated by devm_alloc_etherdev_mqs() so the check is
* straightforward.
*/
if (WARN_ON(!devres_find(dev, devm_free_netdev,
netdev_devres_match, ndev)))
return -EINVAL;
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);
...@@ -400,34 +400,6 @@ struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, ...@@ -400,34 +400,6 @@ struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
} }
EXPORT_SYMBOL(alloc_etherdev_mqs); EXPORT_SYMBOL(alloc_etherdev_mqs);
static void devm_free_netdev(struct device *dev, void *res)
{
free_netdev(*(struct net_device **)res);
}
struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
unsigned int txqs, unsigned int rxqs)
{
struct net_device **dr;
struct net_device *netdev;
dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
if (!dr)
return NULL;
netdev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
if (!netdev) {
devres_free(dr);
return NULL;
}
*dr = netdev;
devres_add(dev, dr);
return netdev;
}
EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len) ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len)
{ {
return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr); return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment