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;
};
/* -- 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,
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)
{
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;
}
}
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
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);
{
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);
}
}
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
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;
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
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;
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;
}
* 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;
}