From: Danilo Krummrich <dakr@kernel.org>
To: gregkh@linuxfoundation.org, rafael@kernel.org
Cc: driver-core@lists.linux.dev, linux-kernel@vger.kernel.org,
Danilo Krummrich <dakr@kernel.org>
Subject: [PATCH 5/7] devres: add free_node callback to struct devres_node
Date: Tue, 3 Feb 2026 00:48:18 +0100 [thread overview]
Message-ID: <20260202235210.55176-6-dakr@kernel.org> (raw)
In-Reply-To: <20260202235210.55176-1-dakr@kernel.org>
Currently, there are three "subclasses" of struct devres_node, which are
struct devres, struct devres_group, struct devres_action.
release_nodes(), which only knows about the base struct devres_node,
assumes that for all "subclasses" struct devres_node is the first member
in the structure and calls kfree() on struct devres_node.
While this technically works, we can still improve semantical
correctness and type safety with a corresponding free_node() callback.
Additionally, we will need this callback soon in the Rust Devres code,
to allocate and free the required memory on the Rust side.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/base/devres.c | 57 ++++++++++++++++++++++++++++++++++---------
1 file changed, 45 insertions(+), 12 deletions(-)
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index df015ef155e0..2006fe411b49 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -18,10 +18,12 @@
struct devres_node;
typedef void (*dr_node_release_t)(struct device *dev, struct devres_node *node);
+typedef void (*dr_node_free_t)(struct devres_node *node);
struct devres_node {
struct list_head entry;
dr_node_release_t release;
+ dr_node_free_t free_node;
const char *name;
size_t size;
};
@@ -46,10 +48,18 @@ struct devres_group {
/* -- 8 pointers */
};
-static void devres_node_init(struct devres_node *node, dr_node_release_t release)
+static void devres_node_init(struct devres_node *node,
+ dr_node_release_t release,
+ dr_node_free_t free_node)
{
INIT_LIST_HEAD(&node->entry);
node->release = release;
+ node->free_node = free_node;
+}
+
+static inline void free_node(struct devres_node *node)
+{
+ node->free_node(node);
}
static void set_node_dbginfo(struct devres_node *node, const char *name,
@@ -124,6 +134,13 @@ static void dr_node_release(struct device *dev, struct devres_node *node)
dr->release(dev, dr->data);
}
+static void dr_node_free(struct devres_node *node)
+{
+ struct devres *dr = container_of(node, struct devres, node);
+
+ kfree(dr);
+}
+
static __always_inline struct devres *alloc_dr(dr_release_t release,
size_t size, gfp_t gfp, int nid)
{
@@ -141,7 +158,7 @@ static __always_inline struct devres *alloc_dr(dr_release_t release,
if (!(gfp & __GFP_ZERO))
memset(dr, 0, offsetof(struct devres, data));
- devres_node_init(&dr->node, dr_node_release);
+ devres_node_init(&dr->node, dr_node_release, dr_node_free);
dr->release = release;
return dr;
}
@@ -231,6 +248,11 @@ void devres_for_each_res(struct device *dev, dr_release_t release,
}
EXPORT_SYMBOL_GPL(devres_for_each_res);
+static inline void free_dr(struct devres *dr)
+{
+ free_node(&dr->node);
+}
+
/**
* devres_free - Free device resource data
* @res: Pointer to devres data to free
@@ -243,7 +265,7 @@ void devres_free(void *res)
struct devres *dr = container_of(res, struct devres, data);
BUG_ON(!list_empty(&dr->node.entry));
- kfree(dr);
+ free_dr(dr);
}
}
EXPORT_SYMBOL_GPL(devres_free);
@@ -518,13 +540,10 @@ static void release_nodes(struct device *dev, struct list_head *todo)
{
struct devres_node *node, *tmp;
- /* Release. Note that devres, devres_action and devres_group are
- * handled as devres_node in the following loop. This is safe.
- */
list_for_each_entry_safe_reverse(node, tmp, todo, entry) {
devres_log(dev, node, "REL");
node->release(dev, node);
- kfree(node);
+ free_node(node);
}
}
@@ -557,6 +576,13 @@ int devres_release_all(struct device *dev)
return cnt;
}
+static void devres_group_free(struct devres_node *node)
+{
+ struct devres_group *grp = container_of(node, struct devres_group, node[0]);
+
+ kfree(grp);
+}
+
/**
* devres_open_group - Open a new devres group
* @dev: Device to open devres group for
@@ -578,8 +604,8 @@ void *devres_open_group(struct device *dev, void *id, gfp_t gfp)
if (unlikely(!grp))
return NULL;
- devres_node_init(&grp->node[0], &group_open_release);
- devres_node_init(&grp->node[1], &group_close_release);
+ devres_node_init(&grp->node[0], &group_open_release, devres_group_free);
+ devres_node_init(&grp->node[1], &group_close_release, NULL);
set_node_dbginfo(&grp->node[0], "grp<", 0);
set_node_dbginfo(&grp->node[1], "grp>", 0);
grp->id = grp;
@@ -750,6 +776,13 @@ static void devm_action_release(struct device *dev, struct devres_node *node)
devres->action.action(devres->action.data);
}
+static void devm_action_free(struct devres_node *node)
+{
+ struct devres_action *action = container_of(node, struct devres_action, node);
+
+ kfree(action);
+}
+
/**
* __devm_add_action() - add a custom action to list of managed resources
* @dev: Device that owns the action
@@ -768,7 +801,7 @@ int __devm_add_action(struct device *dev, void (*action)(void *), void *data, co
if (!devres)
return -ENOMEM;
- devres_node_init(&devres->node, devm_action_release);
+ devres_node_init(&devres->node, devm_action_release, devm_action_free);
set_node_dbginfo(&devres->node, name, sizeof(*devres));
devres->action.data = data;
@@ -1012,7 +1045,7 @@ void *devm_krealloc(struct device *dev, void *ptr, size_t new_size, gfp_t gfp)
old_dr = find_dr(dev, devm_kmalloc_release, devm_kmalloc_match, ptr);
if (!old_dr) {
spin_unlock_irqrestore(&dev->devres_lock, flags);
- kfree(new_dr);
+ free_dr(new_dr);
WARN(1, "Memory chunk not managed or managed by a different device.");
return NULL;
}
@@ -1032,7 +1065,7 @@ void *devm_krealloc(struct device *dev, void *ptr, size_t new_size, gfp_t gfp)
* list. This is also the reason why we must not use devm_kfree() - the
* links are no longer valid.
*/
- kfree(old_dr);
+ free_dr(old_dr);
return new_dr->data;
}
--
2.52.0
next prev parent reply other threads:[~2026-02-02 23:52 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-02 23:48 [PATCH 0/7] Miscellaneous devres improvements Danilo Krummrich
2026-02-02 23:48 ` [PATCH 1/7] devres: fix missing node debug info in devm_krealloc() Danilo Krummrich
2026-02-02 23:48 ` [PATCH 2/7] devres: add devres_node_add() Danilo Krummrich
2026-02-02 23:48 ` [PATCH 3/7] devres: add devres_node_init() Danilo Krummrich
2026-02-02 23:48 ` [PATCH 4/7] devres: don't require ARCH_DMA_MINALIGN for devres actions Danilo Krummrich
2026-02-02 23:48 ` Danilo Krummrich [this message]
2026-02-02 23:48 ` [PATCH 6/7] devres: use guard(spinlock_irqsave) where applicable Danilo Krummrich
2026-02-02 23:48 ` [PATCH 7/7] devres: remove unnecessary unlocks in devres_release_group() Danilo Krummrich
2026-03-12 15:13 ` [PATCH 0/7] Miscellaneous devres improvements Greg KH
2026-03-17 22:59 ` Danilo Krummrich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260202235210.55176-6-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=driver-core@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rafael@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.