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