summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/driver-api/driver-model/devres.rst5
-rw-r--r--drivers/net/ethernet/mediatek/mtk_star_emac.c17
-rw-r--r--include/linux/netdevice.h2
-rw-r--r--net/Makefile2
-rw-r--r--net/devres.c95
-rw-r--r--net/ethernet/eth.c28
6 files changed, 104 insertions, 45 deletions
diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 46c13780994c..fc242ed4bde5 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -372,6 +372,11 @@ MUX
devm_mux_chip_register()
devm_mux_control_get()
+NET
+ devm_alloc_etherdev()
+ devm_alloc_etherdev_mqs()
+ devm_register_netdev()
+
PER-CPU MEM
devm_alloc_percpu()
devm_free_percpu()
diff --git a/drivers/net/ethernet/mediatek/mtk_star_emac.c b/drivers/net/ethernet/mediatek/mtk_star_emac.c
index 789c77af501f..b74349cede28 100644
--- a/drivers/net/ethernet/mediatek/mtk_star_emac.c
+++ b/drivers/net/ethernet/mediatek/mtk_star_emac.c
@@ -1519,13 +1519,6 @@ static void mtk_star_mdiobus_unregister(void *data)
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)
{
struct device_node *of_node;
@@ -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);
- ret = register_netdev(ndev);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, mtk_star_unregister_netdev, ndev);
- if (ret)
- return ret;
-
- return 0;
+ return devm_register_netdev(dev, ndev);
}
static const struct of_device_id mtk_star_of_match[] = {
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a18f8fdf4260..1a96e9c4ec36 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4280,6 +4280,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
int register_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 */
int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list, int addr_len);
diff --git a/net/Makefile b/net/Makefile
index 07ea48160874..5744bf1997fd 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -6,7 +6,7 @@
# 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
obj-$(CONFIG_NET) += $(tmp-y)
diff --git a/net/devres.c b/net/devres.c
new file mode 100644
index 000000000000..57a6a88d11f6
--- /dev/null
+++ b/net/devres.c
@@ -0,0 +1,95 @@
+// 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);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index c8b903302ff2..dac65180c4ef 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -400,34 +400,6 @@ struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
}
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)
{
return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr);