Path: blob/main/sys/compat/linuxkpi/common/src/linux_idr.c
39586 views
/*-1* Copyright (c) 2010 Isilon Systems, Inc.2* Copyright (c) 2010 iX Systems, Inc.3* Copyright (c) 2010 Panasas, Inc.4* Copyright (c) 2013-2017 Mellanox Technologies, Ltd.5* All rights reserved.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10* 1. Redistributions of source code must retain the above copyright11* notice unmodified, this list of conditions, and the following12* disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR18* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES19* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.20* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,21* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT22* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*/2829#include <sys/param.h>30#include <sys/systm.h>31#include <sys/malloc.h>32#include <sys/kernel.h>33#include <sys/sysctl.h>34#include <sys/lock.h>35#include <sys/mutex.h>36#include <sys/stdarg.h>3738#include <linux/bitmap.h>39#include <linux/kobject.h>40#include <linux/slab.h>41#include <linux/idr.h>42#include <linux/err.h>4344#define MAX_IDR_LEVEL ((MAX_IDR_SHIFT + IDR_BITS - 1) / IDR_BITS)45#define MAX_IDR_FREE (MAX_IDR_LEVEL * 2)4647struct linux_idr_cache {48spinlock_t lock;49struct idr_layer *head;50unsigned count;51};5253DPCPU_DEFINE_STATIC(struct linux_idr_cache, linux_idr_cache);5455/*56* IDR Implementation.57*58* This is quick and dirty and not as re-entrant as the linux version59* however it should be fairly fast. It is basically a radix tree with60* a builtin bitmap for allocation.61*/62static MALLOC_DEFINE(M_IDR, "idr", "Linux IDR compat");6364static struct idr_layer *65idr_preload_dequeue_locked(struct linux_idr_cache *lic)66{67struct idr_layer *retval;6869/* check if wrong thread is trying to dequeue */70if (mtx_owned(&lic->lock) == 0)71return (NULL);7273retval = lic->head;74if (likely(retval != NULL)) {75lic->head = retval->ary[0];76lic->count--;77retval->ary[0] = NULL;78}79return (retval);80}8182static void83idr_preload_init(void *arg)84{85int cpu;8687CPU_FOREACH(cpu) {88struct linux_idr_cache *lic =89DPCPU_ID_PTR(cpu, linux_idr_cache);9091spin_lock_init(&lic->lock);92}93}94SYSINIT(idr_preload_init, SI_SUB_CPU, SI_ORDER_ANY, idr_preload_init, NULL);9596static void97idr_preload_uninit(void *arg)98{99int cpu;100101CPU_FOREACH(cpu) {102struct idr_layer *cacheval;103struct linux_idr_cache *lic =104DPCPU_ID_PTR(cpu, linux_idr_cache);105106while (1) {107spin_lock(&lic->lock);108cacheval = idr_preload_dequeue_locked(lic);109spin_unlock(&lic->lock);110111if (cacheval == NULL)112break;113free(cacheval, M_IDR);114}115spin_lock_destroy(&lic->lock);116}117}118SYSUNINIT(idr_preload_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, idr_preload_uninit, NULL);119120void121idr_preload(gfp_t gfp_mask)122{123struct linux_idr_cache *lic;124struct idr_layer *cacheval;125126sched_pin();127128lic = &DPCPU_GET(linux_idr_cache);129130/* fill up cache */131spin_lock(&lic->lock);132while (lic->count < MAX_IDR_FREE) {133spin_unlock(&lic->lock);134cacheval = malloc(sizeof(*cacheval), M_IDR, M_ZERO | gfp_mask);135spin_lock(&lic->lock);136if (cacheval == NULL)137break;138cacheval->ary[0] = lic->head;139lic->head = cacheval;140lic->count++;141}142}143144void145idr_preload_end(void)146{147struct linux_idr_cache *lic;148149lic = &DPCPU_GET(linux_idr_cache);150spin_unlock(&lic->lock);151sched_unpin();152}153154static inline int155idr_max(struct idr *idr)156{157return (1 << (idr->layers * IDR_BITS)) - 1;158}159160static inline int161idr_pos(int id, int layer)162{163return (id >> (IDR_BITS * layer)) & IDR_MASK;164}165166void167idr_init(struct idr *idr)168{169bzero(idr, sizeof(*idr));170mtx_init(&idr->lock, "idr", NULL, MTX_DEF);171}172173/* Only frees cached pages. */174void175idr_destroy(struct idr *idr)176{177struct idr_layer *il, *iln;178179/*180* This idr can be reused, and this function might be called multiple times181* without a idr_init(). Check if this is the case. If we do not do this182* then the mutex will panic while asserting that it is valid.183*/184if (mtx_initialized(&idr->lock) == 0)185return;186187idr_remove_all(idr);188mtx_lock(&idr->lock);189for (il = idr->free; il != NULL; il = iln) {190iln = il->ary[0];191free(il, M_IDR);192}193mtx_unlock(&idr->lock);194mtx_destroy(&idr->lock);195}196197static void198idr_remove_layer(struct idr_layer *il, int layer)199{200int i;201202if (il == NULL)203return;204if (layer == 0) {205free(il, M_IDR);206return;207}208for (i = 0; i < IDR_SIZE; i++)209if (il->ary[i])210idr_remove_layer(il->ary[i], layer - 1);211}212213void214idr_remove_all(struct idr *idr)215{216217mtx_lock(&idr->lock);218idr_remove_layer(idr->top, idr->layers - 1);219idr->top = NULL;220idr->layers = 0;221mtx_unlock(&idr->lock);222}223224static void *225idr_remove_locked(struct idr *idr, int id)226{227struct idr_layer *il;228void *res;229int layer;230int idx;231232id &= MAX_ID_MASK;233il = idr->top;234layer = idr->layers - 1;235if (il == NULL || id > idr_max(idr))236return (NULL);237/*238* Walk down the tree to this item setting bitmaps along the way239* as we know at least one item will be free along this path.240*/241while (layer && il) {242idx = idr_pos(id, layer);243il->bitmap |= 1 << idx;244il = il->ary[idx];245layer--;246}247idx = id & IDR_MASK;248/*249* At this point we've set free space bitmaps up the whole tree.250* We could make this non-fatal and unwind but linux dumps a stack251* and a warning so I don't think it's necessary.252*/253if (il == NULL || (il->bitmap & (1 << idx)) != 0)254panic("idr_remove: Item %d not allocated (%p, %p)\n",255id, idr, il);256res = il->ary[idx];257il->ary[idx] = NULL;258il->bitmap |= 1 << idx;259260return (res);261}262263void *264idr_remove(struct idr *idr, int id)265{266void *res;267268mtx_lock(&idr->lock);269res = idr_remove_locked(idr, id);270mtx_unlock(&idr->lock);271272return (res);273}274275static inline struct idr_layer *276idr_find_layer_locked(struct idr *idr, int id)277{278struct idr_layer *il;279int layer;280281id &= MAX_ID_MASK;282il = idr->top;283layer = idr->layers - 1;284if (il == NULL || id > idr_max(idr))285return (NULL);286while (layer && il) {287il = il->ary[idr_pos(id, layer)];288layer--;289}290return (il);291}292293void *294idr_replace(struct idr *idr, void *ptr, int id)295{296struct idr_layer *il;297void *res;298int idx;299300mtx_lock(&idr->lock);301il = idr_find_layer_locked(idr, id);302idx = id & IDR_MASK;303304/* Replace still returns an error if the item was not allocated. */305if (il == NULL || (il->bitmap & (1 << idx))) {306res = ERR_PTR(-ENOENT);307} else {308res = il->ary[idx];309il->ary[idx] = ptr;310}311mtx_unlock(&idr->lock);312return (res);313}314315static inline void *316idr_find_locked(struct idr *idr, int id)317{318struct idr_layer *il;319void *res;320321mtx_assert(&idr->lock, MA_OWNED);322il = idr_find_layer_locked(idr, id);323if (il != NULL)324res = il->ary[id & IDR_MASK];325else326res = NULL;327return (res);328}329330void *331idr_find(struct idr *idr, int id)332{333void *res;334335mtx_lock(&idr->lock);336res = idr_find_locked(idr, id);337mtx_unlock(&idr->lock);338return (res);339}340341void *342idr_get_next(struct idr *idr, int *nextidp)343{344void *res = NULL;345int id = *nextidp;346347mtx_lock(&idr->lock);348for (; id <= idr_max(idr); id++) {349res = idr_find_locked(idr, id);350if (res == NULL)351continue;352*nextidp = id;353break;354}355mtx_unlock(&idr->lock);356return (res);357}358359int360idr_pre_get(struct idr *idr, gfp_t gfp_mask)361{362struct idr_layer *il, *iln;363struct idr_layer *head;364int need;365366mtx_lock(&idr->lock);367for (;;) {368need = idr->layers + 1;369for (il = idr->free; il != NULL; il = il->ary[0])370need--;371mtx_unlock(&idr->lock);372if (need <= 0)373break;374for (head = NULL; need; need--) {375iln = malloc(sizeof(*il), M_IDR, M_ZERO | gfp_mask);376if (iln == NULL)377break;378bitmap_fill(&iln->bitmap, IDR_SIZE);379if (head != NULL) {380il->ary[0] = iln;381il = iln;382} else383head = il = iln;384}385if (head == NULL)386return (0);387mtx_lock(&idr->lock);388il->ary[0] = idr->free;389idr->free = head;390}391return (1);392}393394static struct idr_layer *395idr_free_list_get(struct idr *idp)396{397struct idr_layer *il;398399if ((il = idp->free) != NULL) {400idp->free = il->ary[0];401il->ary[0] = NULL;402}403return (il);404}405406static inline struct idr_layer *407idr_get(struct idr *idp)408{409struct idr_layer *il;410411if ((il = idr_free_list_get(idp)) != NULL) {412MPASS(il->bitmap != 0);413} else if ((il = malloc(sizeof(*il), M_IDR, M_ZERO | M_NOWAIT)) != NULL) {414bitmap_fill(&il->bitmap, IDR_SIZE);415} else if ((il = idr_preload_dequeue_locked(&DPCPU_GET(linux_idr_cache))) != NULL) {416bitmap_fill(&il->bitmap, IDR_SIZE);417} else {418return (NULL);419}420return (il);421}422423/*424* Could be implemented as get_new_above(idr, ptr, 0, idp) but written425* first for simplicity sake.426*/427static int428idr_get_new_locked(struct idr *idr, void *ptr, int *idp)429{430struct idr_layer *stack[MAX_LEVEL];431struct idr_layer *il;432int error;433int layer;434int idx;435int id;436437mtx_assert(&idr->lock, MA_OWNED);438439error = -EAGAIN;440/*441* Expand the tree until there is free space.442*/443if (idr->top == NULL || idr->top->bitmap == 0) {444if (idr->layers == MAX_LEVEL + 1) {445error = -ENOSPC;446goto out;447}448il = idr_get(idr);449if (il == NULL)450goto out;451il->ary[0] = idr->top;452if (idr->top)453il->bitmap &= ~1;454idr->top = il;455idr->layers++;456}457il = idr->top;458id = 0;459/*460* Walk the tree following free bitmaps, record our path.461*/462for (layer = idr->layers - 1;; layer--) {463stack[layer] = il;464idx = ffsl(il->bitmap);465if (idx == 0)466panic("idr_get_new: Invalid leaf state (%p, %p)\n",467idr, il);468idx--;469id |= idx << (layer * IDR_BITS);470if (layer == 0)471break;472if (il->ary[idx] == NULL) {473il->ary[idx] = idr_get(idr);474if (il->ary[idx] == NULL)475goto out;476}477il = il->ary[idx];478}479/*480* Allocate the leaf to the consumer.481*/482il->bitmap &= ~(1 << idx);483il->ary[idx] = ptr;484*idp = id;485/*486* Clear bitmaps potentially up to the root.487*/488while (il->bitmap == 0 && ++layer < idr->layers) {489il = stack[layer];490il->bitmap &= ~(1 << idr_pos(id, layer));491}492error = 0;493out:494#ifdef INVARIANTS495if (error == 0 && idr_find_locked(idr, id) != ptr) {496panic("idr_get_new: Failed for idr %p, id %d, ptr %p\n",497idr, id, ptr);498}499#endif500return (error);501}502503int504idr_get_new(struct idr *idr, void *ptr, int *idp)505{506int retval;507508mtx_lock(&idr->lock);509retval = idr_get_new_locked(idr, ptr, idp);510mtx_unlock(&idr->lock);511return (retval);512}513514static int515idr_get_new_above_locked(struct idr *idr, void *ptr, int starting_id, int *idp)516{517struct idr_layer *stack[MAX_LEVEL];518struct idr_layer *il;519int error;520int layer;521int idx, sidx;522int id;523524mtx_assert(&idr->lock, MA_OWNED);525526error = -EAGAIN;527/*528* Compute the layers required to support starting_id and the mask529* at the top layer.530*/531restart:532idx = starting_id;533layer = 0;534while (idx & ~IDR_MASK) {535layer++;536idx >>= IDR_BITS;537}538if (layer == MAX_LEVEL + 1) {539error = -ENOSPC;540goto out;541}542/*543* Expand the tree until there is free space at or beyond starting_id.544*/545while (idr->layers <= layer ||546idr->top->bitmap < (1 << idr_pos(starting_id, idr->layers - 1))) {547if (idr->layers == MAX_LEVEL + 1) {548error = -ENOSPC;549goto out;550}551il = idr_get(idr);552if (il == NULL)553goto out;554il->ary[0] = idr->top;555if (idr->top && idr->top->bitmap == 0)556il->bitmap &= ~1;557idr->top = il;558idr->layers++;559}560il = idr->top;561id = 0;562/*563* Walk the tree following free bitmaps, record our path.564*/565for (layer = idr->layers - 1;; layer--) {566stack[layer] = il;567sidx = idr_pos(starting_id, layer);568/* Returns index numbered from 0 or size if none exists. */569idx = find_next_bit(&il->bitmap, IDR_SIZE, sidx);570if (idx == IDR_SIZE && sidx == 0)571panic("idr_get_new: Invalid leaf state (%p, %p)\n",572idr, il);573/*574* We may have walked a path where there was a free bit but575* it was lower than what we wanted. Restart the search with576* a larger starting id. id contains the progress we made so577* far. Search the leaf one above this level. This may578* restart as many as MAX_LEVEL times but that is expected579* to be rare.580*/581if (idx == IDR_SIZE) {582starting_id = id + (1 << ((layer + 1) * IDR_BITS));583goto restart;584}585if (idx > sidx)586starting_id = 0; /* Search the whole subtree. */587id |= idx << (layer * IDR_BITS);588if (layer == 0)589break;590if (il->ary[idx] == NULL) {591il->ary[idx] = idr_get(idr);592if (il->ary[idx] == NULL)593goto out;594}595il = il->ary[idx];596}597/*598* Allocate the leaf to the consumer.599*/600il->bitmap &= ~(1 << idx);601il->ary[idx] = ptr;602*idp = id;603/*604* Clear bitmaps potentially up to the root.605*/606while (il->bitmap == 0 && ++layer < idr->layers) {607il = stack[layer];608il->bitmap &= ~(1 << idr_pos(id, layer));609}610error = 0;611out:612#ifdef INVARIANTS613if (error == 0 && idr_find_locked(idr, id) != ptr) {614panic("idr_get_new_above: Failed for idr %p, id %d, ptr %p\n",615idr, id, ptr);616}617#endif618return (error);619}620621int622idr_get_new_above(struct idr *idr, void *ptr, int starting_id, int *idp)623{624int retval;625626mtx_lock(&idr->lock);627retval = idr_get_new_above_locked(idr, ptr, starting_id, idp);628mtx_unlock(&idr->lock);629return (retval);630}631632int633ida_get_new_above(struct ida *ida, int starting_id, int *p_id)634{635return (idr_get_new_above(&ida->idr, NULL, starting_id, p_id));636}637638static int639idr_alloc_locked(struct idr *idr, void *ptr, int start, int end)640{641int max = end > 0 ? end - 1 : INT_MAX;642int error;643int id;644645mtx_assert(&idr->lock, MA_OWNED);646647if (unlikely(start < 0))648return (-EINVAL);649if (unlikely(max < start))650return (-ENOSPC);651652if (start == 0)653error = idr_get_new_locked(idr, ptr, &id);654else655error = idr_get_new_above_locked(idr, ptr, start, &id);656657if (unlikely(error < 0))658return (error);659if (unlikely(id > max)) {660idr_remove_locked(idr, id);661return (-ENOSPC);662}663return (id);664}665666int667idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask)668{669int retval;670671mtx_lock(&idr->lock);672retval = idr_alloc_locked(idr, ptr, start, end);673mtx_unlock(&idr->lock);674return (retval);675}676677int678idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask)679{680int retval;681682mtx_lock(&idr->lock);683retval = idr_alloc_locked(idr, ptr, max(start, idr->next_cyclic_id), end);684if (unlikely(retval == -ENOSPC))685retval = idr_alloc_locked(idr, ptr, start, end);686if (likely(retval >= 0))687idr->next_cyclic_id = retval + 1;688mtx_unlock(&idr->lock);689return (retval);690}691692static int693idr_for_each_layer(struct idr_layer *il, int offset, int layer,694int (*f)(int id, void *p, void *data), void *data)695{696int i, err;697698if (il == NULL)699return (0);700if (layer == 0) {701for (i = 0; i < IDR_SIZE; i++) {702if (il->ary[i] == NULL)703continue;704err = f(i + offset, il->ary[i], data);705if (err)706return (err);707}708return (0);709}710for (i = 0; i < IDR_SIZE; i++) {711if (il->ary[i] == NULL)712continue;713err = idr_for_each_layer(il->ary[i],714(i + offset) * IDR_SIZE, layer - 1, f, data);715if (err)716return (err);717}718return (0);719}720721/* NOTE: It is not allowed to modify the IDR tree while it is being iterated */722int723idr_for_each(struct idr *idp, int (*f)(int id, void *p, void *data), void *data)724{725return (idr_for_each_layer(idp->top, 0, idp->layers - 1, f, data));726}727728static int729idr_has_entry(int id, void *p, void *data)730{731732return (1);733}734735bool736idr_is_empty(struct idr *idp)737{738739return (idr_for_each(idp, idr_has_entry, NULL) == 0);740}741742int743ida_pre_get(struct ida *ida, gfp_t flags)744{745if (idr_pre_get(&ida->idr, flags) == 0)746return (0);747748if (ida->free_bitmap == NULL) {749ida->free_bitmap =750malloc(sizeof(struct ida_bitmap), M_IDR, flags);751}752return (ida->free_bitmap != NULL);753}754755int756ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,757gfp_t flags)758{759int ret, id;760unsigned int max;761762MPASS((int)start >= 0);763764if ((int)end <= 0)765max = INT_MAX;766else {767MPASS(end > start);768max = end - 1;769}770again:771if (!ida_pre_get(ida, flags))772return (-ENOMEM);773774if ((ret = ida_get_new_above(ida, start, &id)) == 0) {775if (id > max) {776ida_remove(ida, id);777ret = -ENOSPC;778} else {779ret = id;780}781}782if (__predict_false(ret == -EAGAIN))783goto again;784785return (ret);786}787788void789ida_simple_remove(struct ida *ida, unsigned int id)790{791idr_remove(&ida->idr, id);792}793794void795ida_remove(struct ida *ida, int id)796{797idr_remove(&ida->idr, id);798}799800void801ida_init(struct ida *ida)802{803idr_init(&ida->idr);804}805806void807ida_destroy(struct ida *ida)808{809idr_destroy(&ida->idr);810free(ida->free_bitmap, M_IDR);811ida->free_bitmap = NULL;812}813814815