Path: blob/main/sys/compat/linuxkpi/common/src/linux_xarray.c
39586 views
/*-1* Copyright (c) 2020 Mellanox Technologies, Ltd.2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice unmodified, this list of conditions, and the following9* disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526#include <sys/cdefs.h>27#include <linux/xarray.h>2829#include <vm/vm_pageout.h>3031/*32* Linux' XArray allows to store a NULL pointer as a value. xa_load() would33* return NULL for both an unused index and an index set to NULL. But it34* impacts xa_alloc() which needs to find the next available index.35*36* However, our implementation relies on a radix tree (see `linux_radix.c`)37* which does not accept NULL pointers as values. I'm not sure this is a38* limitation or a feature, so to work around this, a NULL value is replaced by39* `NULL_VALUE`, an unlikely address, when we pass it to linux_radix.40*/41#define NULL_VALUE (void *)0x14243/*44* This function removes the element at the given index and returns45* the pointer to the removed element, if any.46*/47void *48__xa_erase(struct xarray *xa, uint32_t index)49{50void *retval;5152XA_ASSERT_LOCKED(xa);5354retval = radix_tree_delete(&xa->xa_head, index);55if (retval == NULL_VALUE)56retval = NULL;5758return (retval);59}6061void *62xa_erase(struct xarray *xa, uint32_t index)63{64void *retval;6566xa_lock(xa);67retval = __xa_erase(xa, index);68xa_unlock(xa);6970return (retval);71}7273/*74* This function returns the element pointer at the given index. A75* value of NULL is returned if the element does not exist.76*/77void *78xa_load(struct xarray *xa, uint32_t index)79{80void *retval;8182xa_lock(xa);83retval = radix_tree_lookup(&xa->xa_head, index);84xa_unlock(xa);8586if (retval == NULL_VALUE)87retval = NULL;8889return (retval);90}9192/*93* This is an internal function used to sleep until more memory94* becomes available.95*/96static void97xa_vm_wait_locked(struct xarray *xa)98{99xa_unlock(xa);100vm_wait(NULL);101xa_lock(xa);102}103104/*105* This function iterates the xarray until it finds a free slot where106* it can insert the element pointer to by "ptr". It starts at the107* index pointed to by "pindex" and updates this value at return. The108* "mask" argument defines the maximum index allowed, inclusivly, and109* must be a power of two minus one value. The "gfp" argument110* basically tells if we can wait for more memory to become available111* or not. This function returns zero upon success or a negative error112* code on failure. A typical error code is -ENOMEM which means either113* the xarray is full, or there was not enough internal memory114* available to complete the radix tree insertion.115*/116int117__xa_alloc(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask, gfp_t gfp)118{119int retval;120121XA_ASSERT_LOCKED(xa);122123/* mask should allow to allocate at least one item */124MPASS(mask > ((xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0));125126/* mask can be any power of two value minus one */127MPASS((mask & (mask + 1)) == 0);128129*pindex = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;130if (ptr == NULL)131ptr = NULL_VALUE;132retry:133retval = radix_tree_insert(&xa->xa_head, *pindex, ptr);134135switch (retval) {136case -EEXIST:137if (likely(*pindex != mask)) {138(*pindex)++;139goto retry;140}141retval = -ENOMEM;142break;143case -ENOMEM:144if (likely(gfp & M_WAITOK)) {145xa_vm_wait_locked(xa);146goto retry;147}148break;149default:150break;151}152return (retval);153}154155int156xa_alloc(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask, gfp_t gfp)157{158int retval;159160if (ptr == NULL)161ptr = NULL_VALUE;162163xa_lock(xa);164retval = __xa_alloc(xa, pindex, ptr, mask, gfp);165xa_unlock(xa);166167return (retval);168}169170/*171* This function works the same like the "xa_alloc" function, except172* it wraps the next index value to zero when there are no entries173* left at the end of the xarray searching for a free slot from the174* beginning of the array. If the xarray is full -ENOMEM is returned.175*/176int177__xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask,178uint32_t *pnext_index, gfp_t gfp)179{180int retval;181int timeout = 1;182183XA_ASSERT_LOCKED(xa);184185/* mask should allow to allocate at least one item */186MPASS(mask > ((xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0));187188/* mask can be any power of two value minus one */189MPASS((mask & (mask + 1)) == 0);190191*pnext_index = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;192if (ptr == NULL)193ptr = NULL_VALUE;194retry:195retval = radix_tree_insert(&xa->xa_head, *pnext_index, ptr);196197switch (retval) {198case -EEXIST:199if (unlikely(*pnext_index == mask) && !timeout--) {200retval = -ENOMEM;201break;202}203(*pnext_index)++;204(*pnext_index) &= mask;205if (*pnext_index == 0 && (xa->xa_flags & XA_FLAGS_ALLOC1) != 0)206(*pnext_index)++;207goto retry;208case -ENOMEM:209if (likely(gfp & M_WAITOK)) {210xa_vm_wait_locked(xa);211goto retry;212}213break;214default:215break;216}217*pindex = *pnext_index;218219return (retval);220}221222int223xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask,224uint32_t *pnext_index, gfp_t gfp)225{226int retval;227228xa_lock(xa);229retval = __xa_alloc_cyclic(xa, pindex, ptr, mask, pnext_index, gfp);230xa_unlock(xa);231232return (retval);233}234235int236xa_alloc_cyclic_irq(struct xarray *xa, uint32_t *pindex, void *ptr,237uint32_t mask, uint32_t *pnext_index, gfp_t gfp)238{239int retval;240241xa_lock_irq(xa);242retval = __xa_alloc_cyclic(xa, pindex, ptr, mask, pnext_index, gfp);243xa_unlock_irq(xa);244245return (retval);246}247248/*249* This function tries to insert an element at the given index. The250* "gfp" argument basically decides of this function can sleep or not251* trying to allocate internal memory for its radix tree. The252* function returns an error code upon failure. Typical error codes253* are element exists (-EEXIST) or out of memory (-ENOMEM).254*/255int256__xa_insert(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)257{258int retval;259260XA_ASSERT_LOCKED(xa);261if (ptr == NULL)262ptr = NULL_VALUE;263retry:264retval = radix_tree_insert(&xa->xa_head, index, ptr);265266switch (retval) {267case -ENOMEM:268if (likely(gfp & M_WAITOK)) {269xa_vm_wait_locked(xa);270goto retry;271}272break;273default:274break;275}276return (retval);277}278279int280xa_insert(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)281{282int retval;283284xa_lock(xa);285retval = __xa_insert(xa, index, ptr, gfp);286xa_unlock(xa);287288return (retval);289}290291/*292* This function updates the element at the given index and returns a293* pointer to the old element. The "gfp" argument basically decides of294* this function can sleep or not trying to allocate internal memory295* for its radix tree. The function returns an XA_ERROR() pointer code296* upon failure. Code using this function must always check if the297* return value is an XA_ERROR() code before using the returned value.298*/299void *300__xa_store(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)301{302int retval;303304XA_ASSERT_LOCKED(xa);305if (ptr == NULL)306ptr = NULL_VALUE;307retry:308retval = radix_tree_store(&xa->xa_head, index, &ptr);309310switch (retval) {311case 0:312if (ptr == NULL_VALUE)313ptr = NULL;314break;315case -ENOMEM:316if (likely(gfp & M_WAITOK)) {317xa_vm_wait_locked(xa);318goto retry;319}320ptr = XA_ERROR(retval);321break;322default:323ptr = XA_ERROR(retval);324break;325}326return (ptr);327}328329void *330xa_store(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)331{332void *retval;333334xa_lock(xa);335retval = __xa_store(xa, index, ptr, gfp);336xa_unlock(xa);337338return (retval);339}340341/*342* This function initialize an xarray structure.343*/344void345xa_init_flags(struct xarray *xa, uint32_t flags)346{347memset(xa, 0, sizeof(*xa));348349mtx_init(&xa->xa_lock, "lkpi-xarray", NULL, MTX_DEF | MTX_RECURSE);350xa->xa_head.gfp_mask = GFP_NOWAIT;351xa->xa_flags = flags;352}353354/*355* This function destroys an xarray structure and all its internal356* memory and locks.357*/358void359xa_destroy(struct xarray *xa)360{361struct radix_tree_iter iter;362void **ppslot;363364xa_lock(xa);365radix_tree_for_each_slot(ppslot, &xa->xa_head, &iter, 0)366radix_tree_iter_delete(&xa->xa_head, &iter, ppslot);367xa_unlock(xa);368369/*370* The mutex initialized in `xa_init_flags()` is not destroyed here on371* purpose. The reason is that on Linux, the xarray remains usable372* after a call to `xa_destroy()`. For instance the i915 DRM driver373* relies on that during the initialixation of its GuC. Basically,374* `xa_destroy()` "resets" the structure to zero but doesn't really375* destroy it.376*/377}378379/*380* This function checks if an xarray is empty or not.381* It returns true if empty, else false.382*/383bool384__xa_empty(struct xarray *xa)385{386struct radix_tree_iter iter = {};387void **temp;388389XA_ASSERT_LOCKED(xa);390391return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp));392}393394bool395xa_empty(struct xarray *xa)396{397bool retval;398399xa_lock(xa);400retval = __xa_empty(xa);401xa_unlock(xa);402403return (retval);404}405406/*407* This function returns the next valid xarray entry based on the408* index given by "pindex". The valued pointed to by "pindex" is409* updated before return.410*/411void *412__xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)413{414struct radix_tree_iter iter = { .index = *pindex };415void **ppslot;416void *retval;417bool found;418419XA_ASSERT_LOCKED(xa);420421if (not_first) {422/* advance to next index, if any */423iter.index++;424if (iter.index == 0)425return (NULL);426}427428found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot);429if (likely(found)) {430retval = *ppslot;431if (retval == NULL_VALUE)432retval = NULL;433*pindex = iter.index;434} else {435retval = NULL;436}437return (retval);438}439440void *441xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)442{443void *retval;444445xa_lock(xa);446retval = __xa_next(xa, pindex, not_first);447xa_unlock(xa);448449return (retval);450}451452453