162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/base/devres.c - device resource management
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2006  SUSE Linux Products GmbH
662306a36Sopenharmony_ci * Copyright (c) 2006  Tejun Heo <teheo@suse.de>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/percpu.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <asm/sections.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "base.h"
1762306a36Sopenharmony_ci#include "trace.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct devres_node {
2062306a36Sopenharmony_ci	struct list_head		entry;
2162306a36Sopenharmony_ci	dr_release_t			release;
2262306a36Sopenharmony_ci	const char			*name;
2362306a36Sopenharmony_ci	size_t				size;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct devres {
2762306a36Sopenharmony_ci	struct devres_node		node;
2862306a36Sopenharmony_ci	/*
2962306a36Sopenharmony_ci	 * Some archs want to perform DMA into kmalloc caches
3062306a36Sopenharmony_ci	 * and need a guaranteed alignment larger than
3162306a36Sopenharmony_ci	 * the alignment of a 64-bit integer.
3262306a36Sopenharmony_ci	 * Thus we use ARCH_DMA_MINALIGN for data[] which will force the same
3362306a36Sopenharmony_ci	 * alignment for struct devres when allocated by kmalloc().
3462306a36Sopenharmony_ci	 */
3562306a36Sopenharmony_ci	u8 __aligned(ARCH_DMA_MINALIGN) data[];
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct devres_group {
3962306a36Sopenharmony_ci	struct devres_node		node[2];
4062306a36Sopenharmony_ci	void				*id;
4162306a36Sopenharmony_ci	int				color;
4262306a36Sopenharmony_ci	/* -- 8 pointers */
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void set_node_dbginfo(struct devres_node *node, const char *name,
4662306a36Sopenharmony_ci			     size_t size)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	node->name = name;
4962306a36Sopenharmony_ci	node->size = size;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_DEVRES
5362306a36Sopenharmony_cistatic int log_devres = 0;
5462306a36Sopenharmony_cimodule_param_named(log, log_devres, int, S_IRUGO | S_IWUSR);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void devres_dbg(struct device *dev, struct devres_node *node,
5762306a36Sopenharmony_ci		       const char *op)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	if (unlikely(log_devres))
6062306a36Sopenharmony_ci		dev_err(dev, "DEVRES %3s %p %s (%zu bytes)\n",
6162306a36Sopenharmony_ci			op, node, node->name, node->size);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci#else /* CONFIG_DEBUG_DEVRES */
6462306a36Sopenharmony_ci#define devres_dbg(dev, node, op)	do {} while (0)
6562306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_DEVRES */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void devres_log(struct device *dev, struct devres_node *node,
6862306a36Sopenharmony_ci		       const char *op)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	trace_devres_log(dev, op, node, node->name, node->size);
7162306a36Sopenharmony_ci	devres_dbg(dev, node, op);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * Release functions for devres group.  These callbacks are used only
7662306a36Sopenharmony_ci * for identification.
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_cistatic void group_open_release(struct device *dev, void *res)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	/* noop */
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void group_close_release(struct device *dev, void *res)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	/* noop */
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic struct devres_group * node_to_group(struct devres_node *node)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	if (node->release == &group_open_release)
9162306a36Sopenharmony_ci		return container_of(node, struct devres_group, node[0]);
9262306a36Sopenharmony_ci	if (node->release == &group_close_release)
9362306a36Sopenharmony_ci		return container_of(node, struct devres_group, node[1]);
9462306a36Sopenharmony_ci	return NULL;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic bool check_dr_size(size_t size, size_t *tot_size)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	/* We must catch any near-SIZE_MAX cases that could overflow. */
10062306a36Sopenharmony_ci	if (unlikely(check_add_overflow(sizeof(struct devres),
10162306a36Sopenharmony_ci					size, tot_size)))
10262306a36Sopenharmony_ci		return false;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Actually allocate the full kmalloc bucket size. */
10562306a36Sopenharmony_ci	*tot_size = kmalloc_size_roundup(*tot_size);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return true;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic __always_inline struct devres * alloc_dr(dr_release_t release,
11162306a36Sopenharmony_ci						size_t size, gfp_t gfp, int nid)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	size_t tot_size;
11462306a36Sopenharmony_ci	struct devres *dr;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!check_dr_size(size, &tot_size))
11762306a36Sopenharmony_ci		return NULL;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	dr = kmalloc_node_track_caller(tot_size, gfp, nid);
12062306a36Sopenharmony_ci	if (unlikely(!dr))
12162306a36Sopenharmony_ci		return NULL;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* No need to clear memory twice */
12462306a36Sopenharmony_ci	if (!(gfp & __GFP_ZERO))
12562306a36Sopenharmony_ci		memset(dr, 0, offsetof(struct devres, data));
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	INIT_LIST_HEAD(&dr->node.entry);
12862306a36Sopenharmony_ci	dr->node.release = release;
12962306a36Sopenharmony_ci	return dr;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void add_dr(struct device *dev, struct devres_node *node)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	devres_log(dev, node, "ADD");
13562306a36Sopenharmony_ci	BUG_ON(!list_empty(&node->entry));
13662306a36Sopenharmony_ci	list_add_tail(&node->entry, &dev->devres_head);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void replace_dr(struct device *dev,
14062306a36Sopenharmony_ci		       struct devres_node *old, struct devres_node *new)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	devres_log(dev, old, "REPLACE");
14362306a36Sopenharmony_ci	BUG_ON(!list_empty(&new->entry));
14462306a36Sopenharmony_ci	list_replace(&old->entry, &new->entry);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/**
14862306a36Sopenharmony_ci * __devres_alloc_node - Allocate device resource data
14962306a36Sopenharmony_ci * @release: Release function devres will be associated with
15062306a36Sopenharmony_ci * @size: Allocation size
15162306a36Sopenharmony_ci * @gfp: Allocation flags
15262306a36Sopenharmony_ci * @nid: NUMA node
15362306a36Sopenharmony_ci * @name: Name of the resource
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci * Allocate devres of @size bytes.  The allocated area is zeroed, then
15662306a36Sopenharmony_ci * associated with @release.  The returned pointer can be passed to
15762306a36Sopenharmony_ci * other devres_*() functions.
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * RETURNS:
16062306a36Sopenharmony_ci * Pointer to allocated devres on success, NULL on failure.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_civoid *__devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid,
16362306a36Sopenharmony_ci			  const char *name)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct devres *dr;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	dr = alloc_dr(release, size, gfp | __GFP_ZERO, nid);
16862306a36Sopenharmony_ci	if (unlikely(!dr))
16962306a36Sopenharmony_ci		return NULL;
17062306a36Sopenharmony_ci	set_node_dbginfo(&dr->node, name, size);
17162306a36Sopenharmony_ci	return dr->data;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devres_alloc_node);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * devres_for_each_res - Resource iterator
17762306a36Sopenharmony_ci * @dev: Device to iterate resource from
17862306a36Sopenharmony_ci * @release: Look for resources associated with this release function
17962306a36Sopenharmony_ci * @match: Match function (optional)
18062306a36Sopenharmony_ci * @match_data: Data for the match function
18162306a36Sopenharmony_ci * @fn: Function to be called for each matched resource.
18262306a36Sopenharmony_ci * @data: Data for @fn, the 3rd parameter of @fn
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci * Call @fn for each devres of @dev which is associated with @release
18562306a36Sopenharmony_ci * and for which @match returns 1.
18662306a36Sopenharmony_ci *
18762306a36Sopenharmony_ci * RETURNS:
18862306a36Sopenharmony_ci * 	void
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_civoid devres_for_each_res(struct device *dev, dr_release_t release,
19162306a36Sopenharmony_ci			dr_match_t match, void *match_data,
19262306a36Sopenharmony_ci			void (*fn)(struct device *, void *, void *),
19362306a36Sopenharmony_ci			void *data)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct devres_node *node;
19662306a36Sopenharmony_ci	struct devres_node *tmp;
19762306a36Sopenharmony_ci	unsigned long flags;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!fn)
20062306a36Sopenharmony_ci		return;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
20362306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(node, tmp,
20462306a36Sopenharmony_ci			&dev->devres_head, entry) {
20562306a36Sopenharmony_ci		struct devres *dr = container_of(node, struct devres, node);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		if (node->release != release)
20862306a36Sopenharmony_ci			continue;
20962306a36Sopenharmony_ci		if (match && !match(dev, dr->data, match_data))
21062306a36Sopenharmony_ci			continue;
21162306a36Sopenharmony_ci		fn(dev, dr->data, data);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_for_each_res);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/**
21862306a36Sopenharmony_ci * devres_free - Free device resource data
21962306a36Sopenharmony_ci * @res: Pointer to devres data to free
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * Free devres created with devres_alloc().
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_civoid devres_free(void *res)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	if (res) {
22662306a36Sopenharmony_ci		struct devres *dr = container_of(res, struct devres, data);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		BUG_ON(!list_empty(&dr->node.entry));
22962306a36Sopenharmony_ci		kfree(dr);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_free);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * devres_add - Register device resource
23662306a36Sopenharmony_ci * @dev: Device to add resource to
23762306a36Sopenharmony_ci * @res: Resource to register
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci * Register devres @res to @dev.  @res should have been allocated
24062306a36Sopenharmony_ci * using devres_alloc().  On driver detach, the associated release
24162306a36Sopenharmony_ci * function will be invoked and devres will be freed automatically.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_civoid devres_add(struct device *dev, void *res)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct devres *dr = container_of(res, struct devres, data);
24662306a36Sopenharmony_ci	unsigned long flags;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
24962306a36Sopenharmony_ci	add_dr(dev, &dr->node);
25062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_add);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic struct devres *find_dr(struct device *dev, dr_release_t release,
25562306a36Sopenharmony_ci			      dr_match_t match, void *match_data)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct devres_node *node;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	list_for_each_entry_reverse(node, &dev->devres_head, entry) {
26062306a36Sopenharmony_ci		struct devres *dr = container_of(node, struct devres, node);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		if (node->release != release)
26362306a36Sopenharmony_ci			continue;
26462306a36Sopenharmony_ci		if (match && !match(dev, dr->data, match_data))
26562306a36Sopenharmony_ci			continue;
26662306a36Sopenharmony_ci		return dr;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return NULL;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/**
27362306a36Sopenharmony_ci * devres_find - Find device resource
27462306a36Sopenharmony_ci * @dev: Device to lookup resource from
27562306a36Sopenharmony_ci * @release: Look for resources associated with this release function
27662306a36Sopenharmony_ci * @match: Match function (optional)
27762306a36Sopenharmony_ci * @match_data: Data for the match function
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * Find the latest devres of @dev which is associated with @release
28062306a36Sopenharmony_ci * and for which @match returns 1.  If @match is NULL, it's considered
28162306a36Sopenharmony_ci * to match all.
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * RETURNS:
28462306a36Sopenharmony_ci * Pointer to found devres, NULL if not found.
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_civoid * devres_find(struct device *dev, dr_release_t release,
28762306a36Sopenharmony_ci		   dr_match_t match, void *match_data)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct devres *dr;
29062306a36Sopenharmony_ci	unsigned long flags;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
29362306a36Sopenharmony_ci	dr = find_dr(dev, release, match, match_data);
29462306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (dr)
29762306a36Sopenharmony_ci		return dr->data;
29862306a36Sopenharmony_ci	return NULL;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_find);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/**
30362306a36Sopenharmony_ci * devres_get - Find devres, if non-existent, add one atomically
30462306a36Sopenharmony_ci * @dev: Device to lookup or add devres for
30562306a36Sopenharmony_ci * @new_res: Pointer to new initialized devres to add if not found
30662306a36Sopenharmony_ci * @match: Match function (optional)
30762306a36Sopenharmony_ci * @match_data: Data for the match function
30862306a36Sopenharmony_ci *
30962306a36Sopenharmony_ci * Find the latest devres of @dev which has the same release function
31062306a36Sopenharmony_ci * as @new_res and for which @match return 1.  If found, @new_res is
31162306a36Sopenharmony_ci * freed; otherwise, @new_res is added atomically.
31262306a36Sopenharmony_ci *
31362306a36Sopenharmony_ci * RETURNS:
31462306a36Sopenharmony_ci * Pointer to found or added devres.
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_civoid * devres_get(struct device *dev, void *new_res,
31762306a36Sopenharmony_ci		  dr_match_t match, void *match_data)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct devres *new_dr = container_of(new_res, struct devres, data);
32062306a36Sopenharmony_ci	struct devres *dr;
32162306a36Sopenharmony_ci	unsigned long flags;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
32462306a36Sopenharmony_ci	dr = find_dr(dev, new_dr->node.release, match, match_data);
32562306a36Sopenharmony_ci	if (!dr) {
32662306a36Sopenharmony_ci		add_dr(dev, &new_dr->node);
32762306a36Sopenharmony_ci		dr = new_dr;
32862306a36Sopenharmony_ci		new_res = NULL;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
33162306a36Sopenharmony_ci	devres_free(new_res);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return dr->data;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_get);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/**
33862306a36Sopenharmony_ci * devres_remove - Find a device resource and remove it
33962306a36Sopenharmony_ci * @dev: Device to find resource from
34062306a36Sopenharmony_ci * @release: Look for resources associated with this release function
34162306a36Sopenharmony_ci * @match: Match function (optional)
34262306a36Sopenharmony_ci * @match_data: Data for the match function
34362306a36Sopenharmony_ci *
34462306a36Sopenharmony_ci * Find the latest devres of @dev associated with @release and for
34562306a36Sopenharmony_ci * which @match returns 1.  If @match is NULL, it's considered to
34662306a36Sopenharmony_ci * match all.  If found, the resource is removed atomically and
34762306a36Sopenharmony_ci * returned.
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * RETURNS:
35062306a36Sopenharmony_ci * Pointer to removed devres on success, NULL if not found.
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_civoid * devres_remove(struct device *dev, dr_release_t release,
35362306a36Sopenharmony_ci		     dr_match_t match, void *match_data)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct devres *dr;
35662306a36Sopenharmony_ci	unsigned long flags;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
35962306a36Sopenharmony_ci	dr = find_dr(dev, release, match, match_data);
36062306a36Sopenharmony_ci	if (dr) {
36162306a36Sopenharmony_ci		list_del_init(&dr->node.entry);
36262306a36Sopenharmony_ci		devres_log(dev, &dr->node, "REM");
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (dr)
36762306a36Sopenharmony_ci		return dr->data;
36862306a36Sopenharmony_ci	return NULL;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_remove);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/**
37362306a36Sopenharmony_ci * devres_destroy - Find a device resource and destroy it
37462306a36Sopenharmony_ci * @dev: Device to find resource from
37562306a36Sopenharmony_ci * @release: Look for resources associated with this release function
37662306a36Sopenharmony_ci * @match: Match function (optional)
37762306a36Sopenharmony_ci * @match_data: Data for the match function
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * Find the latest devres of @dev associated with @release and for
38062306a36Sopenharmony_ci * which @match returns 1.  If @match is NULL, it's considered to
38162306a36Sopenharmony_ci * match all.  If found, the resource is removed atomically and freed.
38262306a36Sopenharmony_ci *
38362306a36Sopenharmony_ci * Note that the release function for the resource will not be called,
38462306a36Sopenharmony_ci * only the devres-allocated data will be freed.  The caller becomes
38562306a36Sopenharmony_ci * responsible for freeing any other data.
38662306a36Sopenharmony_ci *
38762306a36Sopenharmony_ci * RETURNS:
38862306a36Sopenharmony_ci * 0 if devres is found and freed, -ENOENT if not found.
38962306a36Sopenharmony_ci */
39062306a36Sopenharmony_ciint devres_destroy(struct device *dev, dr_release_t release,
39162306a36Sopenharmony_ci		   dr_match_t match, void *match_data)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	void *res;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	res = devres_remove(dev, release, match, match_data);
39662306a36Sopenharmony_ci	if (unlikely(!res))
39762306a36Sopenharmony_ci		return -ENOENT;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	devres_free(res);
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_destroy);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/**
40662306a36Sopenharmony_ci * devres_release - Find a device resource and destroy it, calling release
40762306a36Sopenharmony_ci * @dev: Device to find resource from
40862306a36Sopenharmony_ci * @release: Look for resources associated with this release function
40962306a36Sopenharmony_ci * @match: Match function (optional)
41062306a36Sopenharmony_ci * @match_data: Data for the match function
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * Find the latest devres of @dev associated with @release and for
41362306a36Sopenharmony_ci * which @match returns 1.  If @match is NULL, it's considered to
41462306a36Sopenharmony_ci * match all.  If found, the resource is removed atomically, the
41562306a36Sopenharmony_ci * release function called and the resource freed.
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * RETURNS:
41862306a36Sopenharmony_ci * 0 if devres is found and freed, -ENOENT if not found.
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_ciint devres_release(struct device *dev, dr_release_t release,
42162306a36Sopenharmony_ci		   dr_match_t match, void *match_data)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	void *res;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	res = devres_remove(dev, release, match, match_data);
42662306a36Sopenharmony_ci	if (unlikely(!res))
42762306a36Sopenharmony_ci		return -ENOENT;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	(*release)(dev, res);
43062306a36Sopenharmony_ci	devres_free(res);
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_release);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int remove_nodes(struct device *dev,
43662306a36Sopenharmony_ci			struct list_head *first, struct list_head *end,
43762306a36Sopenharmony_ci			struct list_head *todo)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct devres_node *node, *n;
44062306a36Sopenharmony_ci	int cnt = 0, nr_groups = 0;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* First pass - move normal devres entries to @todo and clear
44362306a36Sopenharmony_ci	 * devres_group colors.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	node = list_entry(first, struct devres_node, entry);
44662306a36Sopenharmony_ci	list_for_each_entry_safe_from(node, n, end, entry) {
44762306a36Sopenharmony_ci		struct devres_group *grp;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		grp = node_to_group(node);
45062306a36Sopenharmony_ci		if (grp) {
45162306a36Sopenharmony_ci			/* clear color of group markers in the first pass */
45262306a36Sopenharmony_ci			grp->color = 0;
45362306a36Sopenharmony_ci			nr_groups++;
45462306a36Sopenharmony_ci		} else {
45562306a36Sopenharmony_ci			/* regular devres entry */
45662306a36Sopenharmony_ci			if (&node->entry == first)
45762306a36Sopenharmony_ci				first = first->next;
45862306a36Sopenharmony_ci			list_move_tail(&node->entry, todo);
45962306a36Sopenharmony_ci			cnt++;
46062306a36Sopenharmony_ci		}
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (!nr_groups)
46462306a36Sopenharmony_ci		return cnt;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Second pass - Scan groups and color them.  A group gets
46762306a36Sopenharmony_ci	 * color value of two iff the group is wholly contained in
46862306a36Sopenharmony_ci	 * [current node, end). That is, for a closed group, both opening
46962306a36Sopenharmony_ci	 * and closing markers should be in the range, while just the
47062306a36Sopenharmony_ci	 * opening marker is enough for an open group.
47162306a36Sopenharmony_ci	 */
47262306a36Sopenharmony_ci	node = list_entry(first, struct devres_node, entry);
47362306a36Sopenharmony_ci	list_for_each_entry_safe_from(node, n, end, entry) {
47462306a36Sopenharmony_ci		struct devres_group *grp;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		grp = node_to_group(node);
47762306a36Sopenharmony_ci		BUG_ON(!grp || list_empty(&grp->node[0].entry));
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		grp->color++;
48062306a36Sopenharmony_ci		if (list_empty(&grp->node[1].entry))
48162306a36Sopenharmony_ci			grp->color++;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		BUG_ON(grp->color <= 0 || grp->color > 2);
48462306a36Sopenharmony_ci		if (grp->color == 2) {
48562306a36Sopenharmony_ci			/* No need to update current node or end. The removed
48662306a36Sopenharmony_ci			 * nodes are always before both.
48762306a36Sopenharmony_ci			 */
48862306a36Sopenharmony_ci			list_move_tail(&grp->node[0].entry, todo);
48962306a36Sopenharmony_ci			list_del_init(&grp->node[1].entry);
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	return cnt;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic void release_nodes(struct device *dev, struct list_head *todo)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct devres *dr, *tmp;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Release.  Note that both devres and devres_group are
50162306a36Sopenharmony_ci	 * handled as devres in the following loop.  This is safe.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(dr, tmp, todo, node.entry) {
50462306a36Sopenharmony_ci		devres_log(dev, &dr->node, "REL");
50562306a36Sopenharmony_ci		dr->node.release(dev, dr->data);
50662306a36Sopenharmony_ci		kfree(dr);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci/**
51162306a36Sopenharmony_ci * devres_release_all - Release all managed resources
51262306a36Sopenharmony_ci * @dev: Device to release resources for
51362306a36Sopenharmony_ci *
51462306a36Sopenharmony_ci * Release all resources associated with @dev.  This function is
51562306a36Sopenharmony_ci * called on driver detach.
51662306a36Sopenharmony_ci */
51762306a36Sopenharmony_ciint devres_release_all(struct device *dev)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	unsigned long flags;
52062306a36Sopenharmony_ci	LIST_HEAD(todo);
52162306a36Sopenharmony_ci	int cnt;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Looks like an uninitialized device structure */
52462306a36Sopenharmony_ci	if (WARN_ON(dev->devres_head.next == NULL))
52562306a36Sopenharmony_ci		return -ENODEV;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Nothing to release if list is empty */
52862306a36Sopenharmony_ci	if (list_empty(&dev->devres_head))
52962306a36Sopenharmony_ci		return 0;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
53262306a36Sopenharmony_ci	cnt = remove_nodes(dev, dev->devres_head.next, &dev->devres_head, &todo);
53362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	release_nodes(dev, &todo);
53662306a36Sopenharmony_ci	return cnt;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci/**
54062306a36Sopenharmony_ci * devres_open_group - Open a new devres group
54162306a36Sopenharmony_ci * @dev: Device to open devres group for
54262306a36Sopenharmony_ci * @id: Separator ID
54362306a36Sopenharmony_ci * @gfp: Allocation flags
54462306a36Sopenharmony_ci *
54562306a36Sopenharmony_ci * Open a new devres group for @dev with @id.  For @id, using a
54662306a36Sopenharmony_ci * pointer to an object which won't be used for another group is
54762306a36Sopenharmony_ci * recommended.  If @id is NULL, address-wise unique ID is created.
54862306a36Sopenharmony_ci *
54962306a36Sopenharmony_ci * RETURNS:
55062306a36Sopenharmony_ci * ID of the new group, NULL on failure.
55162306a36Sopenharmony_ci */
55262306a36Sopenharmony_civoid * devres_open_group(struct device *dev, void *id, gfp_t gfp)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct devres_group *grp;
55562306a36Sopenharmony_ci	unsigned long flags;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	grp = kmalloc(sizeof(*grp), gfp);
55862306a36Sopenharmony_ci	if (unlikely(!grp))
55962306a36Sopenharmony_ci		return NULL;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	grp->node[0].release = &group_open_release;
56262306a36Sopenharmony_ci	grp->node[1].release = &group_close_release;
56362306a36Sopenharmony_ci	INIT_LIST_HEAD(&grp->node[0].entry);
56462306a36Sopenharmony_ci	INIT_LIST_HEAD(&grp->node[1].entry);
56562306a36Sopenharmony_ci	set_node_dbginfo(&grp->node[0], "grp<", 0);
56662306a36Sopenharmony_ci	set_node_dbginfo(&grp->node[1], "grp>", 0);
56762306a36Sopenharmony_ci	grp->id = grp;
56862306a36Sopenharmony_ci	if (id)
56962306a36Sopenharmony_ci		grp->id = id;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
57262306a36Sopenharmony_ci	add_dr(dev, &grp->node[0]);
57362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
57462306a36Sopenharmony_ci	return grp->id;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_open_group);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/* Find devres group with ID @id.  If @id is NULL, look for the latest. */
57962306a36Sopenharmony_cistatic struct devres_group * find_group(struct device *dev, void *id)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	struct devres_node *node;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	list_for_each_entry_reverse(node, &dev->devres_head, entry) {
58462306a36Sopenharmony_ci		struct devres_group *grp;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		if (node->release != &group_open_release)
58762306a36Sopenharmony_ci			continue;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		grp = container_of(node, struct devres_group, node[0]);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		if (id) {
59262306a36Sopenharmony_ci			if (grp->id == id)
59362306a36Sopenharmony_ci				return grp;
59462306a36Sopenharmony_ci		} else if (list_empty(&grp->node[1].entry))
59562306a36Sopenharmony_ci			return grp;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return NULL;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci/**
60262306a36Sopenharmony_ci * devres_close_group - Close a devres group
60362306a36Sopenharmony_ci * @dev: Device to close devres group for
60462306a36Sopenharmony_ci * @id: ID of target group, can be NULL
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * Close the group identified by @id.  If @id is NULL, the latest open
60762306a36Sopenharmony_ci * group is selected.
60862306a36Sopenharmony_ci */
60962306a36Sopenharmony_civoid devres_close_group(struct device *dev, void *id)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct devres_group *grp;
61262306a36Sopenharmony_ci	unsigned long flags;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	grp = find_group(dev, id);
61762306a36Sopenharmony_ci	if (grp)
61862306a36Sopenharmony_ci		add_dr(dev, &grp->node[1]);
61962306a36Sopenharmony_ci	else
62062306a36Sopenharmony_ci		WARN_ON(1);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_close_group);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/**
62762306a36Sopenharmony_ci * devres_remove_group - Remove a devres group
62862306a36Sopenharmony_ci * @dev: Device to remove group for
62962306a36Sopenharmony_ci * @id: ID of target group, can be NULL
63062306a36Sopenharmony_ci *
63162306a36Sopenharmony_ci * Remove the group identified by @id.  If @id is NULL, the latest
63262306a36Sopenharmony_ci * open group is selected.  Note that removing a group doesn't affect
63362306a36Sopenharmony_ci * any other resources.
63462306a36Sopenharmony_ci */
63562306a36Sopenharmony_civoid devres_remove_group(struct device *dev, void *id)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct devres_group *grp;
63862306a36Sopenharmony_ci	unsigned long flags;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	grp = find_group(dev, id);
64362306a36Sopenharmony_ci	if (grp) {
64462306a36Sopenharmony_ci		list_del_init(&grp->node[0].entry);
64562306a36Sopenharmony_ci		list_del_init(&grp->node[1].entry);
64662306a36Sopenharmony_ci		devres_log(dev, &grp->node[0], "REM");
64762306a36Sopenharmony_ci	} else
64862306a36Sopenharmony_ci		WARN_ON(1);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	kfree(grp);
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_remove_group);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci/**
65762306a36Sopenharmony_ci * devres_release_group - Release resources in a devres group
65862306a36Sopenharmony_ci * @dev: Device to release group for
65962306a36Sopenharmony_ci * @id: ID of target group, can be NULL
66062306a36Sopenharmony_ci *
66162306a36Sopenharmony_ci * Release all resources in the group identified by @id.  If @id is
66262306a36Sopenharmony_ci * NULL, the latest open group is selected.  The selected group and
66362306a36Sopenharmony_ci * groups properly nested inside the selected group are removed.
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * RETURNS:
66662306a36Sopenharmony_ci * The number of released non-group resources.
66762306a36Sopenharmony_ci */
66862306a36Sopenharmony_ciint devres_release_group(struct device *dev, void *id)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct devres_group *grp;
67162306a36Sopenharmony_ci	unsigned long flags;
67262306a36Sopenharmony_ci	LIST_HEAD(todo);
67362306a36Sopenharmony_ci	int cnt = 0;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	grp = find_group(dev, id);
67862306a36Sopenharmony_ci	if (grp) {
67962306a36Sopenharmony_ci		struct list_head *first = &grp->node[0].entry;
68062306a36Sopenharmony_ci		struct list_head *end = &dev->devres_head;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		if (!list_empty(&grp->node[1].entry))
68362306a36Sopenharmony_ci			end = grp->node[1].entry.next;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		cnt = remove_nodes(dev, first, end, &todo);
68662306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->devres_lock, flags);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		release_nodes(dev, &todo);
68962306a36Sopenharmony_ci	} else {
69062306a36Sopenharmony_ci		WARN_ON(1);
69162306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->devres_lock, flags);
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return cnt;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devres_release_group);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci/*
69962306a36Sopenharmony_ci * Custom devres actions allow inserting a simple function call
70062306a36Sopenharmony_ci * into the teardown sequence.
70162306a36Sopenharmony_ci */
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistruct action_devres {
70462306a36Sopenharmony_ci	void *data;
70562306a36Sopenharmony_ci	void (*action)(void *);
70662306a36Sopenharmony_ci};
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic int devm_action_match(struct device *dev, void *res, void *p)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct action_devres *devres = res;
71162306a36Sopenharmony_ci	struct action_devres *target = p;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return devres->action == target->action &&
71462306a36Sopenharmony_ci	       devres->data == target->data;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic void devm_action_release(struct device *dev, void *res)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct action_devres *devres = res;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	devres->action(devres->data);
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci/**
72562306a36Sopenharmony_ci * __devm_add_action() - add a custom action to list of managed resources
72662306a36Sopenharmony_ci * @dev: Device that owns the action
72762306a36Sopenharmony_ci * @action: Function that should be called
72862306a36Sopenharmony_ci * @data: Pointer to data passed to @action implementation
72962306a36Sopenharmony_ci * @name: Name of the resource (for debugging purposes)
73062306a36Sopenharmony_ci *
73162306a36Sopenharmony_ci * This adds a custom action to the list of managed resources so that
73262306a36Sopenharmony_ci * it gets executed as part of standard resource unwinding.
73362306a36Sopenharmony_ci */
73462306a36Sopenharmony_ciint __devm_add_action(struct device *dev, void (*action)(void *), void *data, const char *name)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct action_devres *devres;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	devres = __devres_alloc_node(devm_action_release, sizeof(struct action_devres),
73962306a36Sopenharmony_ci				     GFP_KERNEL, NUMA_NO_NODE, name);
74062306a36Sopenharmony_ci	if (!devres)
74162306a36Sopenharmony_ci		return -ENOMEM;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	devres->data = data;
74462306a36Sopenharmony_ci	devres->action = action;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	devres_add(dev, devres);
74762306a36Sopenharmony_ci	return 0;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_add_action);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci/**
75262306a36Sopenharmony_ci * devm_remove_action() - removes previously added custom action
75362306a36Sopenharmony_ci * @dev: Device that owns the action
75462306a36Sopenharmony_ci * @action: Function implementing the action
75562306a36Sopenharmony_ci * @data: Pointer to data passed to @action implementation
75662306a36Sopenharmony_ci *
75762306a36Sopenharmony_ci * Removes instance of @action previously added by devm_add_action().
75862306a36Sopenharmony_ci * Both action and data should match one of the existing entries.
75962306a36Sopenharmony_ci */
76062306a36Sopenharmony_civoid devm_remove_action(struct device *dev, void (*action)(void *), void *data)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct action_devres devres = {
76362306a36Sopenharmony_ci		.data = data,
76462306a36Sopenharmony_ci		.action = action,
76562306a36Sopenharmony_ci	};
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
76862306a36Sopenharmony_ci			       &devres));
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_remove_action);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci/**
77362306a36Sopenharmony_ci * devm_release_action() - release previously added custom action
77462306a36Sopenharmony_ci * @dev: Device that owns the action
77562306a36Sopenharmony_ci * @action: Function implementing the action
77662306a36Sopenharmony_ci * @data: Pointer to data passed to @action implementation
77762306a36Sopenharmony_ci *
77862306a36Sopenharmony_ci * Releases and removes instance of @action previously added by
77962306a36Sopenharmony_ci * devm_add_action().  Both action and data should match one of the
78062306a36Sopenharmony_ci * existing entries.
78162306a36Sopenharmony_ci */
78262306a36Sopenharmony_civoid devm_release_action(struct device *dev, void (*action)(void *), void *data)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	struct action_devres devres = {
78562306a36Sopenharmony_ci		.data = data,
78662306a36Sopenharmony_ci		.action = action,
78762306a36Sopenharmony_ci	};
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	WARN_ON(devres_release(dev, devm_action_release, devm_action_match,
79062306a36Sopenharmony_ci			       &devres));
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_release_action);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci/*
79662306a36Sopenharmony_ci * Managed kmalloc/kfree
79762306a36Sopenharmony_ci */
79862306a36Sopenharmony_cistatic void devm_kmalloc_release(struct device *dev, void *res)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	/* noop */
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int devm_kmalloc_match(struct device *dev, void *res, void *data)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	return res == data;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/**
80962306a36Sopenharmony_ci * devm_kmalloc - Resource-managed kmalloc
81062306a36Sopenharmony_ci * @dev: Device to allocate memory for
81162306a36Sopenharmony_ci * @size: Allocation size
81262306a36Sopenharmony_ci * @gfp: Allocation gfp flags
81362306a36Sopenharmony_ci *
81462306a36Sopenharmony_ci * Managed kmalloc.  Memory allocated with this function is
81562306a36Sopenharmony_ci * automatically freed on driver detach.  Like all other devres
81662306a36Sopenharmony_ci * resources, guaranteed alignment is unsigned long long.
81762306a36Sopenharmony_ci *
81862306a36Sopenharmony_ci * RETURNS:
81962306a36Sopenharmony_ci * Pointer to allocated memory on success, NULL on failure.
82062306a36Sopenharmony_ci */
82162306a36Sopenharmony_civoid *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct devres *dr;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (unlikely(!size))
82662306a36Sopenharmony_ci		return ZERO_SIZE_PTR;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/* use raw alloc_dr for kmalloc caller tracing */
82962306a36Sopenharmony_ci	dr = alloc_dr(devm_kmalloc_release, size, gfp, dev_to_node(dev));
83062306a36Sopenharmony_ci	if (unlikely(!dr))
83162306a36Sopenharmony_ci		return NULL;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/*
83462306a36Sopenharmony_ci	 * This is named devm_kzalloc_release for historical reasons
83562306a36Sopenharmony_ci	 * The initial implementation did not support kmalloc, only kzalloc
83662306a36Sopenharmony_ci	 */
83762306a36Sopenharmony_ci	set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
83862306a36Sopenharmony_ci	devres_add(dev, dr->data);
83962306a36Sopenharmony_ci	return dr->data;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_kmalloc);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci/**
84462306a36Sopenharmony_ci * devm_krealloc - Resource-managed krealloc()
84562306a36Sopenharmony_ci * @dev: Device to re-allocate memory for
84662306a36Sopenharmony_ci * @ptr: Pointer to the memory chunk to re-allocate
84762306a36Sopenharmony_ci * @new_size: New allocation size
84862306a36Sopenharmony_ci * @gfp: Allocation gfp flags
84962306a36Sopenharmony_ci *
85062306a36Sopenharmony_ci * Managed krealloc(). Resizes the memory chunk allocated with devm_kmalloc().
85162306a36Sopenharmony_ci * Behaves similarly to regular krealloc(): if @ptr is NULL or ZERO_SIZE_PTR,
85262306a36Sopenharmony_ci * it's the equivalent of devm_kmalloc(). If new_size is zero, it frees the
85362306a36Sopenharmony_ci * previously allocated memory and returns ZERO_SIZE_PTR. This function doesn't
85462306a36Sopenharmony_ci * change the order in which the release callback for the re-alloc'ed devres
85562306a36Sopenharmony_ci * will be called (except when falling back to devm_kmalloc() or when freeing
85662306a36Sopenharmony_ci * resources when new_size is zero). The contents of the memory are preserved
85762306a36Sopenharmony_ci * up to the lesser of new and old sizes.
85862306a36Sopenharmony_ci */
85962306a36Sopenharmony_civoid *devm_krealloc(struct device *dev, void *ptr, size_t new_size, gfp_t gfp)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	size_t total_new_size, total_old_size;
86262306a36Sopenharmony_ci	struct devres *old_dr, *new_dr;
86362306a36Sopenharmony_ci	unsigned long flags;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	if (unlikely(!new_size)) {
86662306a36Sopenharmony_ci		devm_kfree(dev, ptr);
86762306a36Sopenharmony_ci		return ZERO_SIZE_PTR;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (unlikely(ZERO_OR_NULL_PTR(ptr)))
87162306a36Sopenharmony_ci		return devm_kmalloc(dev, new_size, gfp);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	if (WARN_ON(is_kernel_rodata((unsigned long)ptr)))
87462306a36Sopenharmony_ci		/*
87562306a36Sopenharmony_ci		 * We cannot reliably realloc a const string returned by
87662306a36Sopenharmony_ci		 * devm_kstrdup_const().
87762306a36Sopenharmony_ci		 */
87862306a36Sopenharmony_ci		return NULL;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (!check_dr_size(new_size, &total_new_size))
88162306a36Sopenharmony_ci		return NULL;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	total_old_size = ksize(container_of(ptr, struct devres, data));
88462306a36Sopenharmony_ci	if (total_old_size == 0) {
88562306a36Sopenharmony_ci		WARN(1, "Pointer doesn't point to dynamically allocated memory.");
88662306a36Sopenharmony_ci		return NULL;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/*
89062306a36Sopenharmony_ci	 * If new size is smaller or equal to the actual number of bytes
89162306a36Sopenharmony_ci	 * allocated previously - just return the same pointer.
89262306a36Sopenharmony_ci	 */
89362306a36Sopenharmony_ci	if (total_new_size <= total_old_size)
89462306a36Sopenharmony_ci		return ptr;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/*
89762306a36Sopenharmony_ci	 * Otherwise: allocate new, larger chunk. We need to allocate before
89862306a36Sopenharmony_ci	 * taking the lock as most probably the caller uses GFP_KERNEL.
89962306a36Sopenharmony_ci	 */
90062306a36Sopenharmony_ci	new_dr = alloc_dr(devm_kmalloc_release,
90162306a36Sopenharmony_ci			  total_new_size, gfp, dev_to_node(dev));
90262306a36Sopenharmony_ci	if (!new_dr)
90362306a36Sopenharmony_ci		return NULL;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/*
90662306a36Sopenharmony_ci	 * The spinlock protects the linked list against concurrent
90762306a36Sopenharmony_ci	 * modifications but not the resource itself.
90862306a36Sopenharmony_ci	 */
90962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->devres_lock, flags);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	old_dr = find_dr(dev, devm_kmalloc_release, devm_kmalloc_match, ptr);
91262306a36Sopenharmony_ci	if (!old_dr) {
91362306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->devres_lock, flags);
91462306a36Sopenharmony_ci		kfree(new_dr);
91562306a36Sopenharmony_ci		WARN(1, "Memory chunk not managed or managed by a different device.");
91662306a36Sopenharmony_ci		return NULL;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	replace_dr(dev, &old_dr->node, &new_dr->node);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->devres_lock, flags);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/*
92462306a36Sopenharmony_ci	 * We can copy the memory contents after releasing the lock as we're
92562306a36Sopenharmony_ci	 * no longer modifying the list links.
92662306a36Sopenharmony_ci	 */
92762306a36Sopenharmony_ci	memcpy(new_dr->data, old_dr->data,
92862306a36Sopenharmony_ci	       total_old_size - offsetof(struct devres, data));
92962306a36Sopenharmony_ci	/*
93062306a36Sopenharmony_ci	 * Same for releasing the old devres - it's now been removed from the
93162306a36Sopenharmony_ci	 * list. This is also the reason why we must not use devm_kfree() - the
93262306a36Sopenharmony_ci	 * links are no longer valid.
93362306a36Sopenharmony_ci	 */
93462306a36Sopenharmony_ci	kfree(old_dr);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	return new_dr->data;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_krealloc);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci/**
94162306a36Sopenharmony_ci * devm_kstrdup - Allocate resource managed space and
94262306a36Sopenharmony_ci *                copy an existing string into that.
94362306a36Sopenharmony_ci * @dev: Device to allocate memory for
94462306a36Sopenharmony_ci * @s: the string to duplicate
94562306a36Sopenharmony_ci * @gfp: the GFP mask used in the devm_kmalloc() call when
94662306a36Sopenharmony_ci *       allocating memory
94762306a36Sopenharmony_ci * RETURNS:
94862306a36Sopenharmony_ci * Pointer to allocated string on success, NULL on failure.
94962306a36Sopenharmony_ci */
95062306a36Sopenharmony_cichar *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	size_t size;
95362306a36Sopenharmony_ci	char *buf;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (!s)
95662306a36Sopenharmony_ci		return NULL;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	size = strlen(s) + 1;
95962306a36Sopenharmony_ci	buf = devm_kmalloc(dev, size, gfp);
96062306a36Sopenharmony_ci	if (buf)
96162306a36Sopenharmony_ci		memcpy(buf, s, size);
96262306a36Sopenharmony_ci	return buf;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_kstrdup);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/**
96762306a36Sopenharmony_ci * devm_kstrdup_const - resource managed conditional string duplication
96862306a36Sopenharmony_ci * @dev: device for which to duplicate the string
96962306a36Sopenharmony_ci * @s: the string to duplicate
97062306a36Sopenharmony_ci * @gfp: the GFP mask used in the kmalloc() call when allocating memory
97162306a36Sopenharmony_ci *
97262306a36Sopenharmony_ci * Strings allocated by devm_kstrdup_const will be automatically freed when
97362306a36Sopenharmony_ci * the associated device is detached.
97462306a36Sopenharmony_ci *
97562306a36Sopenharmony_ci * RETURNS:
97662306a36Sopenharmony_ci * Source string if it is in .rodata section otherwise it falls back to
97762306a36Sopenharmony_ci * devm_kstrdup.
97862306a36Sopenharmony_ci */
97962306a36Sopenharmony_ciconst char *devm_kstrdup_const(struct device *dev, const char *s, gfp_t gfp)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	if (is_kernel_rodata((unsigned long)s))
98262306a36Sopenharmony_ci		return s;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	return devm_kstrdup(dev, s, gfp);
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_kstrdup_const);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci/**
98962306a36Sopenharmony_ci * devm_kvasprintf - Allocate resource managed space and format a string
99062306a36Sopenharmony_ci *		     into that.
99162306a36Sopenharmony_ci * @dev: Device to allocate memory for
99262306a36Sopenharmony_ci * @gfp: the GFP mask used in the devm_kmalloc() call when
99362306a36Sopenharmony_ci *       allocating memory
99462306a36Sopenharmony_ci * @fmt: The printf()-style format string
99562306a36Sopenharmony_ci * @ap: Arguments for the format string
99662306a36Sopenharmony_ci * RETURNS:
99762306a36Sopenharmony_ci * Pointer to allocated string on success, NULL on failure.
99862306a36Sopenharmony_ci */
99962306a36Sopenharmony_cichar *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt,
100062306a36Sopenharmony_ci		      va_list ap)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	unsigned int len;
100362306a36Sopenharmony_ci	char *p;
100462306a36Sopenharmony_ci	va_list aq;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	va_copy(aq, ap);
100762306a36Sopenharmony_ci	len = vsnprintf(NULL, 0, fmt, aq);
100862306a36Sopenharmony_ci	va_end(aq);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	p = devm_kmalloc(dev, len+1, gfp);
101162306a36Sopenharmony_ci	if (!p)
101262306a36Sopenharmony_ci		return NULL;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	vsnprintf(p, len+1, fmt, ap);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	return p;
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ciEXPORT_SYMBOL(devm_kvasprintf);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci/**
102162306a36Sopenharmony_ci * devm_kasprintf - Allocate resource managed space and format a string
102262306a36Sopenharmony_ci *		    into that.
102362306a36Sopenharmony_ci * @dev: Device to allocate memory for
102462306a36Sopenharmony_ci * @gfp: the GFP mask used in the devm_kmalloc() call when
102562306a36Sopenharmony_ci *       allocating memory
102662306a36Sopenharmony_ci * @fmt: The printf()-style format string
102762306a36Sopenharmony_ci * @...: Arguments for the format string
102862306a36Sopenharmony_ci * RETURNS:
102962306a36Sopenharmony_ci * Pointer to allocated string on success, NULL on failure.
103062306a36Sopenharmony_ci */
103162306a36Sopenharmony_cichar *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	va_list ap;
103462306a36Sopenharmony_ci	char *p;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	va_start(ap, fmt);
103762306a36Sopenharmony_ci	p = devm_kvasprintf(dev, gfp, fmt, ap);
103862306a36Sopenharmony_ci	va_end(ap);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	return p;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_kasprintf);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci/**
104562306a36Sopenharmony_ci * devm_kfree - Resource-managed kfree
104662306a36Sopenharmony_ci * @dev: Device this memory belongs to
104762306a36Sopenharmony_ci * @p: Memory to free
104862306a36Sopenharmony_ci *
104962306a36Sopenharmony_ci * Free memory allocated with devm_kmalloc().
105062306a36Sopenharmony_ci */
105162306a36Sopenharmony_civoid devm_kfree(struct device *dev, const void *p)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	int rc;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	/*
105662306a36Sopenharmony_ci	 * Special cases: pointer to a string in .rodata returned by
105762306a36Sopenharmony_ci	 * devm_kstrdup_const() or NULL/ZERO ptr.
105862306a36Sopenharmony_ci	 */
105962306a36Sopenharmony_ci	if (unlikely(is_kernel_rodata((unsigned long)p) || ZERO_OR_NULL_PTR(p)))
106062306a36Sopenharmony_ci		return;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	rc = devres_destroy(dev, devm_kmalloc_release,
106362306a36Sopenharmony_ci			    devm_kmalloc_match, (void *)p);
106462306a36Sopenharmony_ci	WARN_ON(rc);
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_kfree);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci/**
106962306a36Sopenharmony_ci * devm_kmemdup - Resource-managed kmemdup
107062306a36Sopenharmony_ci * @dev: Device this memory belongs to
107162306a36Sopenharmony_ci * @src: Memory region to duplicate
107262306a36Sopenharmony_ci * @len: Memory region length
107362306a36Sopenharmony_ci * @gfp: GFP mask to use
107462306a36Sopenharmony_ci *
107562306a36Sopenharmony_ci * Duplicate region of a memory using resource managed kmalloc
107662306a36Sopenharmony_ci */
107762306a36Sopenharmony_civoid *devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	void *p;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	p = devm_kmalloc(dev, len, gfp);
108262306a36Sopenharmony_ci	if (p)
108362306a36Sopenharmony_ci		memcpy(p, src, len);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	return p;
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_kmemdup);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistruct pages_devres {
109062306a36Sopenharmony_ci	unsigned long addr;
109162306a36Sopenharmony_ci	unsigned int order;
109262306a36Sopenharmony_ci};
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic int devm_pages_match(struct device *dev, void *res, void *p)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	struct pages_devres *devres = res;
109762306a36Sopenharmony_ci	struct pages_devres *target = p;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return devres->addr == target->addr;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic void devm_pages_release(struct device *dev, void *res)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	struct pages_devres *devres = res;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	free_pages(devres->addr, devres->order);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci/**
111062306a36Sopenharmony_ci * devm_get_free_pages - Resource-managed __get_free_pages
111162306a36Sopenharmony_ci * @dev: Device to allocate memory for
111262306a36Sopenharmony_ci * @gfp_mask: Allocation gfp flags
111362306a36Sopenharmony_ci * @order: Allocation size is (1 << order) pages
111462306a36Sopenharmony_ci *
111562306a36Sopenharmony_ci * Managed get_free_pages.  Memory allocated with this function is
111662306a36Sopenharmony_ci * automatically freed on driver detach.
111762306a36Sopenharmony_ci *
111862306a36Sopenharmony_ci * RETURNS:
111962306a36Sopenharmony_ci * Address of allocated memory on success, 0 on failure.
112062306a36Sopenharmony_ci */
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ciunsigned long devm_get_free_pages(struct device *dev,
112362306a36Sopenharmony_ci				  gfp_t gfp_mask, unsigned int order)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	struct pages_devres *devres;
112662306a36Sopenharmony_ci	unsigned long addr;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	addr = __get_free_pages(gfp_mask, order);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (unlikely(!addr))
113162306a36Sopenharmony_ci		return 0;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	devres = devres_alloc(devm_pages_release,
113462306a36Sopenharmony_ci			      sizeof(struct pages_devres), GFP_KERNEL);
113562306a36Sopenharmony_ci	if (unlikely(!devres)) {
113662306a36Sopenharmony_ci		free_pages(addr, order);
113762306a36Sopenharmony_ci		return 0;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	devres->addr = addr;
114162306a36Sopenharmony_ci	devres->order = order;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	devres_add(dev, devres);
114462306a36Sopenharmony_ci	return addr;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_get_free_pages);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci/**
114962306a36Sopenharmony_ci * devm_free_pages - Resource-managed free_pages
115062306a36Sopenharmony_ci * @dev: Device this memory belongs to
115162306a36Sopenharmony_ci * @addr: Memory to free
115262306a36Sopenharmony_ci *
115362306a36Sopenharmony_ci * Free memory allocated with devm_get_free_pages(). Unlike free_pages,
115462306a36Sopenharmony_ci * there is no need to supply the @order.
115562306a36Sopenharmony_ci */
115662306a36Sopenharmony_civoid devm_free_pages(struct device *dev, unsigned long addr)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct pages_devres devres = { .addr = addr };
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	WARN_ON(devres_release(dev, devm_pages_release, devm_pages_match,
116162306a36Sopenharmony_ci			       &devres));
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_free_pages);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_cistatic void devm_percpu_release(struct device *dev, void *pdata)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	void __percpu *p;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	p = *(void __percpu **)pdata;
117062306a36Sopenharmony_ci	free_percpu(p);
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic int devm_percpu_match(struct device *dev, void *data, void *p)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct devres *devr = container_of(data, struct devres, data);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	return *(void **)devr->data == p;
117862306a36Sopenharmony_ci}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci/**
118162306a36Sopenharmony_ci * __devm_alloc_percpu - Resource-managed alloc_percpu
118262306a36Sopenharmony_ci * @dev: Device to allocate per-cpu memory for
118362306a36Sopenharmony_ci * @size: Size of per-cpu memory to allocate
118462306a36Sopenharmony_ci * @align: Alignment of per-cpu memory to allocate
118562306a36Sopenharmony_ci *
118662306a36Sopenharmony_ci * Managed alloc_percpu. Per-cpu memory allocated with this function is
118762306a36Sopenharmony_ci * automatically freed on driver detach.
118862306a36Sopenharmony_ci *
118962306a36Sopenharmony_ci * RETURNS:
119062306a36Sopenharmony_ci * Pointer to allocated memory on success, NULL on failure.
119162306a36Sopenharmony_ci */
119262306a36Sopenharmony_civoid __percpu *__devm_alloc_percpu(struct device *dev, size_t size,
119362306a36Sopenharmony_ci		size_t align)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	void *p;
119662306a36Sopenharmony_ci	void __percpu *pcpu;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	pcpu = __alloc_percpu(size, align);
119962306a36Sopenharmony_ci	if (!pcpu)
120062306a36Sopenharmony_ci		return NULL;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	p = devres_alloc(devm_percpu_release, sizeof(void *), GFP_KERNEL);
120362306a36Sopenharmony_ci	if (!p) {
120462306a36Sopenharmony_ci		free_percpu(pcpu);
120562306a36Sopenharmony_ci		return NULL;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	*(void __percpu **)p = pcpu;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	devres_add(dev, p);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return pcpu;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_alloc_percpu);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci/**
121762306a36Sopenharmony_ci * devm_free_percpu - Resource-managed free_percpu
121862306a36Sopenharmony_ci * @dev: Device this memory belongs to
121962306a36Sopenharmony_ci * @pdata: Per-cpu memory to free
122062306a36Sopenharmony_ci *
122162306a36Sopenharmony_ci * Free memory allocated with devm_alloc_percpu().
122262306a36Sopenharmony_ci */
122362306a36Sopenharmony_civoid devm_free_percpu(struct device *dev, void __percpu *pdata)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match,
122662306a36Sopenharmony_ci			       (__force void *)pdata));
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_free_percpu);
1229