18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * I/O Address Space ID allocator. There is one global IOASID space, split into
48c2ecf20Sopenharmony_ci * subsets. Users create a subset with DECLARE_IOASID_SET, then allocate and
58c2ecf20Sopenharmony_ci * free IOASIDs with ioasid_alloc and ioasid_free.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/ioasid.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
118c2ecf20Sopenharmony_ci#include <linux/xarray.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistruct ioasid_data {
148c2ecf20Sopenharmony_ci	ioasid_t id;
158c2ecf20Sopenharmony_ci	struct ioasid_set *set;
168c2ecf20Sopenharmony_ci	void *private;
178c2ecf20Sopenharmony_ci	struct rcu_head rcu;
188c2ecf20Sopenharmony_ci};
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * struct ioasid_allocator_data - Internal data structure to hold information
228c2ecf20Sopenharmony_ci * about an allocator. There are two types of allocators:
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * - Default allocator always has its own XArray to track the IOASIDs allocated.
258c2ecf20Sopenharmony_ci * - Custom allocators may share allocation helpers with different private data.
268c2ecf20Sopenharmony_ci *   Custom allocators that share the same helper functions also share the same
278c2ecf20Sopenharmony_ci *   XArray.
288c2ecf20Sopenharmony_ci * Rules:
298c2ecf20Sopenharmony_ci * 1. Default allocator is always available, not dynamically registered. This is
308c2ecf20Sopenharmony_ci *    to prevent race conditions with early boot code that want to register
318c2ecf20Sopenharmony_ci *    custom allocators or allocate IOASIDs.
328c2ecf20Sopenharmony_ci * 2. Custom allocators take precedence over the default allocator.
338c2ecf20Sopenharmony_ci * 3. When all custom allocators sharing the same helper functions are
348c2ecf20Sopenharmony_ci *    unregistered (e.g. due to hotplug), all outstanding IOASIDs must be
358c2ecf20Sopenharmony_ci *    freed. Otherwise, outstanding IOASIDs will be lost and orphaned.
368c2ecf20Sopenharmony_ci * 4. When switching between custom allocators sharing the same helper
378c2ecf20Sopenharmony_ci *    functions, outstanding IOASIDs are preserved.
388c2ecf20Sopenharmony_ci * 5. When switching between custom allocator and default allocator, all IOASIDs
398c2ecf20Sopenharmony_ci *    must be freed to ensure unadulterated space for the new allocator.
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * @ops:	allocator helper functions and its data
428c2ecf20Sopenharmony_ci * @list:	registered custom allocators
438c2ecf20Sopenharmony_ci * @slist:	allocators share the same ops but different data
448c2ecf20Sopenharmony_ci * @flags:	attributes of the allocator
458c2ecf20Sopenharmony_ci * @xa:		xarray holds the IOASID space
468c2ecf20Sopenharmony_ci * @rcu:	used for kfree_rcu when unregistering allocator
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistruct ioasid_allocator_data {
498c2ecf20Sopenharmony_ci	struct ioasid_allocator_ops *ops;
508c2ecf20Sopenharmony_ci	struct list_head list;
518c2ecf20Sopenharmony_ci	struct list_head slist;
528c2ecf20Sopenharmony_ci#define IOASID_ALLOCATOR_CUSTOM BIT(0) /* Needs framework to track results */
538c2ecf20Sopenharmony_ci	unsigned long flags;
548c2ecf20Sopenharmony_ci	struct xarray xa;
558c2ecf20Sopenharmony_ci	struct rcu_head rcu;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ioasid_allocator_lock);
598c2ecf20Sopenharmony_cistatic LIST_HEAD(allocators_list);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic ioasid_t default_alloc(ioasid_t min, ioasid_t max, void *opaque);
628c2ecf20Sopenharmony_cistatic void default_free(ioasid_t ioasid, void *opaque);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic struct ioasid_allocator_ops default_ops = {
658c2ecf20Sopenharmony_ci	.alloc = default_alloc,
668c2ecf20Sopenharmony_ci	.free = default_free,
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct ioasid_allocator_data default_allocator = {
708c2ecf20Sopenharmony_ci	.ops = &default_ops,
718c2ecf20Sopenharmony_ci	.flags = 0,
728c2ecf20Sopenharmony_ci	.xa = XARRAY_INIT(ioasid_xa, XA_FLAGS_ALLOC),
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic struct ioasid_allocator_data *active_allocator = &default_allocator;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic ioasid_t default_alloc(ioasid_t min, ioasid_t max, void *opaque)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	ioasid_t id;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (xa_alloc(&default_allocator.xa, &id, opaque, XA_LIMIT(min, max), GFP_ATOMIC)) {
828c2ecf20Sopenharmony_ci		pr_err("Failed to alloc ioasid from %d to %d\n", min, max);
838c2ecf20Sopenharmony_ci		return INVALID_IOASID;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return id;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void default_free(ioasid_t ioasid, void *opaque)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct ioasid_data *ioasid_data;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ioasid_data = xa_erase(&default_allocator.xa, ioasid);
948c2ecf20Sopenharmony_ci	kfree_rcu(ioasid_data, rcu);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* Allocate and initialize a new custom allocator with its helper functions */
988c2ecf20Sopenharmony_cistatic struct ioasid_allocator_data *ioasid_alloc_allocator(struct ioasid_allocator_ops *ops)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct ioasid_allocator_data *ia_data;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	ia_data = kzalloc(sizeof(*ia_data), GFP_ATOMIC);
1038c2ecf20Sopenharmony_ci	if (!ia_data)
1048c2ecf20Sopenharmony_ci		return NULL;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	xa_init_flags(&ia_data->xa, XA_FLAGS_ALLOC);
1078c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ia_data->slist);
1088c2ecf20Sopenharmony_ci	ia_data->flags |= IOASID_ALLOCATOR_CUSTOM;
1098c2ecf20Sopenharmony_ci	ia_data->ops = ops;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* For tracking custom allocators that share the same ops */
1128c2ecf20Sopenharmony_ci	list_add_tail(&ops->list, &ia_data->slist);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return ia_data;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic bool use_same_ops(struct ioasid_allocator_ops *a, struct ioasid_allocator_ops *b)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	return (a->free == b->free) && (a->alloc == b->alloc);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/**
1238c2ecf20Sopenharmony_ci * ioasid_register_allocator - register a custom allocator
1248c2ecf20Sopenharmony_ci * @ops: the custom allocator ops to be registered
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * Custom allocators take precedence over the default xarray based allocator.
1278c2ecf20Sopenharmony_ci * Private data associated with the IOASID allocated by the custom allocators
1288c2ecf20Sopenharmony_ci * are managed by IOASID framework similar to data stored in xa by default
1298c2ecf20Sopenharmony_ci * allocator.
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci * There can be multiple allocators registered but only one is active. In case
1328c2ecf20Sopenharmony_ci * of runtime removal of a custom allocator, the next one is activated based
1338c2ecf20Sopenharmony_ci * on the registration ordering.
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci * Multiple allocators can share the same alloc() function, in this case the
1368c2ecf20Sopenharmony_ci * IOASID space is shared.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_ciint ioasid_register_allocator(struct ioasid_allocator_ops *ops)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct ioasid_allocator_data *ia_data;
1418c2ecf20Sopenharmony_ci	struct ioasid_allocator_data *pallocator;
1428c2ecf20Sopenharmony_ci	int ret = 0;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	spin_lock(&ioasid_allocator_lock);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	ia_data = ioasid_alloc_allocator(ops);
1478c2ecf20Sopenharmony_ci	if (!ia_data) {
1488c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1498c2ecf20Sopenharmony_ci		goto out_unlock;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/*
1538c2ecf20Sopenharmony_ci	 * No particular preference, we activate the first one and keep
1548c2ecf20Sopenharmony_ci	 * the later registered allocators in a list in case the first one gets
1558c2ecf20Sopenharmony_ci	 * removed due to hotplug.
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci	if (list_empty(&allocators_list)) {
1588c2ecf20Sopenharmony_ci		WARN_ON(active_allocator != &default_allocator);
1598c2ecf20Sopenharmony_ci		/* Use this new allocator if default is not active */
1608c2ecf20Sopenharmony_ci		if (xa_empty(&active_allocator->xa)) {
1618c2ecf20Sopenharmony_ci			rcu_assign_pointer(active_allocator, ia_data);
1628c2ecf20Sopenharmony_ci			list_add_tail(&ia_data->list, &allocators_list);
1638c2ecf20Sopenharmony_ci			goto out_unlock;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci		pr_warn("Default allocator active with outstanding IOASID\n");
1668c2ecf20Sopenharmony_ci		ret = -EAGAIN;
1678c2ecf20Sopenharmony_ci		goto out_free;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Check if the allocator is already registered */
1718c2ecf20Sopenharmony_ci	list_for_each_entry(pallocator, &allocators_list, list) {
1728c2ecf20Sopenharmony_ci		if (pallocator->ops == ops) {
1738c2ecf20Sopenharmony_ci			pr_err("IOASID allocator already registered\n");
1748c2ecf20Sopenharmony_ci			ret = -EEXIST;
1758c2ecf20Sopenharmony_ci			goto out_free;
1768c2ecf20Sopenharmony_ci		} else if (use_same_ops(pallocator->ops, ops)) {
1778c2ecf20Sopenharmony_ci			/*
1788c2ecf20Sopenharmony_ci			 * If the new allocator shares the same ops,
1798c2ecf20Sopenharmony_ci			 * then they will share the same IOASID space.
1808c2ecf20Sopenharmony_ci			 * We should put them under the same xarray.
1818c2ecf20Sopenharmony_ci			 */
1828c2ecf20Sopenharmony_ci			list_add_tail(&ops->list, &pallocator->slist);
1838c2ecf20Sopenharmony_ci			goto out_free;
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	list_add_tail(&ia_data->list, &allocators_list);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	spin_unlock(&ioasid_allocator_lock);
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ciout_free:
1918c2ecf20Sopenharmony_ci	kfree(ia_data);
1928c2ecf20Sopenharmony_ciout_unlock:
1938c2ecf20Sopenharmony_ci	spin_unlock(&ioasid_allocator_lock);
1948c2ecf20Sopenharmony_ci	return ret;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ioasid_register_allocator);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/**
1998c2ecf20Sopenharmony_ci * ioasid_unregister_allocator - Remove a custom IOASID allocator ops
2008c2ecf20Sopenharmony_ci * @ops: the custom allocator to be removed
2018c2ecf20Sopenharmony_ci *
2028c2ecf20Sopenharmony_ci * Remove an allocator from the list, activate the next allocator in
2038c2ecf20Sopenharmony_ci * the order it was registered. Or revert to default allocator if all
2048c2ecf20Sopenharmony_ci * custom allocators are unregistered without outstanding IOASIDs.
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_civoid ioasid_unregister_allocator(struct ioasid_allocator_ops *ops)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct ioasid_allocator_data *pallocator;
2098c2ecf20Sopenharmony_ci	struct ioasid_allocator_ops *sops;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	spin_lock(&ioasid_allocator_lock);
2128c2ecf20Sopenharmony_ci	if (list_empty(&allocators_list)) {
2138c2ecf20Sopenharmony_ci		pr_warn("No custom IOASID allocators active!\n");
2148c2ecf20Sopenharmony_ci		goto exit_unlock;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	list_for_each_entry(pallocator, &allocators_list, list) {
2188c2ecf20Sopenharmony_ci		if (!use_same_ops(pallocator->ops, ops))
2198c2ecf20Sopenharmony_ci			continue;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		if (list_is_singular(&pallocator->slist)) {
2228c2ecf20Sopenharmony_ci			/* No shared helper functions */
2238c2ecf20Sopenharmony_ci			list_del(&pallocator->list);
2248c2ecf20Sopenharmony_ci			/*
2258c2ecf20Sopenharmony_ci			 * All IOASIDs should have been freed before
2268c2ecf20Sopenharmony_ci			 * the last allocator that shares the same ops
2278c2ecf20Sopenharmony_ci			 * is unregistered.
2288c2ecf20Sopenharmony_ci			 */
2298c2ecf20Sopenharmony_ci			WARN_ON(!xa_empty(&pallocator->xa));
2308c2ecf20Sopenharmony_ci			if (list_empty(&allocators_list)) {
2318c2ecf20Sopenharmony_ci				pr_info("No custom IOASID allocators, switch to default.\n");
2328c2ecf20Sopenharmony_ci				rcu_assign_pointer(active_allocator, &default_allocator);
2338c2ecf20Sopenharmony_ci			} else if (pallocator == active_allocator) {
2348c2ecf20Sopenharmony_ci				rcu_assign_pointer(active_allocator,
2358c2ecf20Sopenharmony_ci						list_first_entry(&allocators_list,
2368c2ecf20Sopenharmony_ci								struct ioasid_allocator_data, list));
2378c2ecf20Sopenharmony_ci				pr_info("IOASID allocator changed");
2388c2ecf20Sopenharmony_ci			}
2398c2ecf20Sopenharmony_ci			kfree_rcu(pallocator, rcu);
2408c2ecf20Sopenharmony_ci			break;
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci		/*
2438c2ecf20Sopenharmony_ci		 * Find the matching shared ops to delete,
2448c2ecf20Sopenharmony_ci		 * but keep outstanding IOASIDs
2458c2ecf20Sopenharmony_ci		 */
2468c2ecf20Sopenharmony_ci		list_for_each_entry(sops, &pallocator->slist, list) {
2478c2ecf20Sopenharmony_ci			if (sops == ops) {
2488c2ecf20Sopenharmony_ci				list_del(&ops->list);
2498c2ecf20Sopenharmony_ci				break;
2508c2ecf20Sopenharmony_ci			}
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci		break;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ciexit_unlock:
2568c2ecf20Sopenharmony_ci	spin_unlock(&ioasid_allocator_lock);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ioasid_unregister_allocator);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/**
2618c2ecf20Sopenharmony_ci * ioasid_set_data - Set private data for an allocated ioasid
2628c2ecf20Sopenharmony_ci * @ioasid: the ID to set data
2638c2ecf20Sopenharmony_ci * @data:   the private data
2648c2ecf20Sopenharmony_ci *
2658c2ecf20Sopenharmony_ci * For IOASID that is already allocated, private data can be set
2668c2ecf20Sopenharmony_ci * via this API. Future lookup can be done via ioasid_find.
2678c2ecf20Sopenharmony_ci */
2688c2ecf20Sopenharmony_ciint ioasid_set_data(ioasid_t ioasid, void *data)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct ioasid_data *ioasid_data;
2718c2ecf20Sopenharmony_ci	int ret = 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	spin_lock(&ioasid_allocator_lock);
2748c2ecf20Sopenharmony_ci	ioasid_data = xa_load(&active_allocator->xa, ioasid);
2758c2ecf20Sopenharmony_ci	if (ioasid_data)
2768c2ecf20Sopenharmony_ci		rcu_assign_pointer(ioasid_data->private, data);
2778c2ecf20Sopenharmony_ci	else
2788c2ecf20Sopenharmony_ci		ret = -ENOENT;
2798c2ecf20Sopenharmony_ci	spin_unlock(&ioasid_allocator_lock);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/*
2828c2ecf20Sopenharmony_ci	 * Wait for readers to stop accessing the old private data, so the
2838c2ecf20Sopenharmony_ci	 * caller can free it.
2848c2ecf20Sopenharmony_ci	 */
2858c2ecf20Sopenharmony_ci	if (!ret)
2868c2ecf20Sopenharmony_ci		synchronize_rcu();
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return ret;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ioasid_set_data);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/**
2938c2ecf20Sopenharmony_ci * ioasid_alloc - Allocate an IOASID
2948c2ecf20Sopenharmony_ci * @set: the IOASID set
2958c2ecf20Sopenharmony_ci * @min: the minimum ID (inclusive)
2968c2ecf20Sopenharmony_ci * @max: the maximum ID (inclusive)
2978c2ecf20Sopenharmony_ci * @private: data private to the caller
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * Allocate an ID between @min and @max. The @private pointer is stored
3008c2ecf20Sopenharmony_ci * internally and can be retrieved with ioasid_find().
3018c2ecf20Sopenharmony_ci *
3028c2ecf20Sopenharmony_ci * Return: the allocated ID on success, or %INVALID_IOASID on failure.
3038c2ecf20Sopenharmony_ci */
3048c2ecf20Sopenharmony_ciioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
3058c2ecf20Sopenharmony_ci		      void *private)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct ioasid_data *data;
3088c2ecf20Sopenharmony_ci	void *adata;
3098c2ecf20Sopenharmony_ci	ioasid_t id;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_ATOMIC);
3128c2ecf20Sopenharmony_ci	if (!data)
3138c2ecf20Sopenharmony_ci		return INVALID_IOASID;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	data->set = set;
3168c2ecf20Sopenharmony_ci	data->private = private;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * Custom allocator needs allocator data to perform platform specific
3208c2ecf20Sopenharmony_ci	 * operations.
3218c2ecf20Sopenharmony_ci	 */
3228c2ecf20Sopenharmony_ci	spin_lock(&ioasid_allocator_lock);
3238c2ecf20Sopenharmony_ci	adata = active_allocator->flags & IOASID_ALLOCATOR_CUSTOM ? active_allocator->ops->pdata : data;
3248c2ecf20Sopenharmony_ci	id = active_allocator->ops->alloc(min, max, adata);
3258c2ecf20Sopenharmony_ci	if (id == INVALID_IOASID) {
3268c2ecf20Sopenharmony_ci		pr_err("Failed ASID allocation %lu\n", active_allocator->flags);
3278c2ecf20Sopenharmony_ci		goto exit_free;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if ((active_allocator->flags & IOASID_ALLOCATOR_CUSTOM) &&
3318c2ecf20Sopenharmony_ci	     xa_alloc(&active_allocator->xa, &id, data, XA_LIMIT(id, id), GFP_ATOMIC)) {
3328c2ecf20Sopenharmony_ci		/* Custom allocator needs framework to store and track allocation results */
3338c2ecf20Sopenharmony_ci		pr_err("Failed to alloc ioasid from %d\n", id);
3348c2ecf20Sopenharmony_ci		active_allocator->ops->free(id, active_allocator->ops->pdata);
3358c2ecf20Sopenharmony_ci		goto exit_free;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	data->id = id;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	spin_unlock(&ioasid_allocator_lock);
3408c2ecf20Sopenharmony_ci	return id;
3418c2ecf20Sopenharmony_ciexit_free:
3428c2ecf20Sopenharmony_ci	spin_unlock(&ioasid_allocator_lock);
3438c2ecf20Sopenharmony_ci	kfree(data);
3448c2ecf20Sopenharmony_ci	return INVALID_IOASID;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ioasid_alloc);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci/**
3498c2ecf20Sopenharmony_ci * ioasid_free - Free an IOASID
3508c2ecf20Sopenharmony_ci * @ioasid: the ID to remove
3518c2ecf20Sopenharmony_ci */
3528c2ecf20Sopenharmony_civoid ioasid_free(ioasid_t ioasid)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct ioasid_data *ioasid_data;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	spin_lock(&ioasid_allocator_lock);
3578c2ecf20Sopenharmony_ci	ioasid_data = xa_load(&active_allocator->xa, ioasid);
3588c2ecf20Sopenharmony_ci	if (!ioasid_data) {
3598c2ecf20Sopenharmony_ci		pr_err("Trying to free unknown IOASID %u\n", ioasid);
3608c2ecf20Sopenharmony_ci		goto exit_unlock;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	active_allocator->ops->free(ioasid, active_allocator->ops->pdata);
3648c2ecf20Sopenharmony_ci	/* Custom allocator needs additional steps to free the xa element */
3658c2ecf20Sopenharmony_ci	if (active_allocator->flags & IOASID_ALLOCATOR_CUSTOM) {
3668c2ecf20Sopenharmony_ci		ioasid_data = xa_erase(&active_allocator->xa, ioasid);
3678c2ecf20Sopenharmony_ci		kfree_rcu(ioasid_data, rcu);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ciexit_unlock:
3718c2ecf20Sopenharmony_ci	spin_unlock(&ioasid_allocator_lock);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ioasid_free);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/**
3768c2ecf20Sopenharmony_ci * ioasid_find - Find IOASID data
3778c2ecf20Sopenharmony_ci * @set: the IOASID set
3788c2ecf20Sopenharmony_ci * @ioasid: the IOASID to find
3798c2ecf20Sopenharmony_ci * @getter: function to call on the found object
3808c2ecf20Sopenharmony_ci *
3818c2ecf20Sopenharmony_ci * The optional getter function allows to take a reference to the found object
3828c2ecf20Sopenharmony_ci * under the rcu lock. The function can also check if the object is still valid:
3838c2ecf20Sopenharmony_ci * if @getter returns false, then the object is invalid and NULL is returned.
3848c2ecf20Sopenharmony_ci *
3858c2ecf20Sopenharmony_ci * If the IOASID exists, return the private pointer passed to ioasid_alloc.
3868c2ecf20Sopenharmony_ci * Private data can be NULL if not set. Return an error if the IOASID is not
3878c2ecf20Sopenharmony_ci * found, or if @set is not NULL and the IOASID does not belong to the set.
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_civoid *ioasid_find(struct ioasid_set *set, ioasid_t ioasid,
3908c2ecf20Sopenharmony_ci		  bool (*getter)(void *))
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	void *priv;
3938c2ecf20Sopenharmony_ci	struct ioasid_data *ioasid_data;
3948c2ecf20Sopenharmony_ci	struct ioasid_allocator_data *idata;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	rcu_read_lock();
3978c2ecf20Sopenharmony_ci	idata = rcu_dereference(active_allocator);
3988c2ecf20Sopenharmony_ci	ioasid_data = xa_load(&idata->xa, ioasid);
3998c2ecf20Sopenharmony_ci	if (!ioasid_data) {
4008c2ecf20Sopenharmony_ci		priv = ERR_PTR(-ENOENT);
4018c2ecf20Sopenharmony_ci		goto unlock;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci	if (set && ioasid_data->set != set) {
4048c2ecf20Sopenharmony_ci		/* data found but does not belong to the set */
4058c2ecf20Sopenharmony_ci		priv = ERR_PTR(-EACCES);
4068c2ecf20Sopenharmony_ci		goto unlock;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci	/* Now IOASID and its set is verified, we can return the private data */
4098c2ecf20Sopenharmony_ci	priv = rcu_dereference(ioasid_data->private);
4108c2ecf20Sopenharmony_ci	if (getter && !getter(priv))
4118c2ecf20Sopenharmony_ci		priv = NULL;
4128c2ecf20Sopenharmony_ciunlock:
4138c2ecf20Sopenharmony_ci	rcu_read_unlock();
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return priv;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ioasid_find);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jean-Philippe Brucker <jean-philippe.brucker@arm.com>");
4208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
4218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IO Address Space ID (IOASID) allocator");
4228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
423