18d59ecb2SHans Petter Selasky /*- 28d59ecb2SHans Petter Selasky * Copyright (c) 2010 Isilon Systems, Inc. 38d59ecb2SHans Petter Selasky * Copyright (c) 2010 iX Systems, Inc. 48d59ecb2SHans Petter Selasky * Copyright (c) 2010 Panasas, Inc. 52d1bee65SHans Petter Selasky * Copyright (c) 2013-2016 Mellanox Technologies, Ltd. 68d59ecb2SHans Petter Selasky * All rights reserved. 78d59ecb2SHans Petter Selasky * 88d59ecb2SHans Petter Selasky * Redistribution and use in source and binary forms, with or without 98d59ecb2SHans Petter Selasky * modification, are permitted provided that the following conditions 108d59ecb2SHans Petter Selasky * are met: 118d59ecb2SHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 128d59ecb2SHans Petter Selasky * notice unmodified, this list of conditions, and the following 138d59ecb2SHans Petter Selasky * disclaimer. 148d59ecb2SHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 158d59ecb2SHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 168d59ecb2SHans Petter Selasky * documentation and/or other materials provided with the distribution. 178d59ecb2SHans Petter Selasky * 188d59ecb2SHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 198d59ecb2SHans Petter Selasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 208d59ecb2SHans Petter Selasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 218d59ecb2SHans Petter Selasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 228d59ecb2SHans Petter Selasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 238d59ecb2SHans Petter Selasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 248d59ecb2SHans Petter Selasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 258d59ecb2SHans Petter Selasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 268d59ecb2SHans Petter Selasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 278d59ecb2SHans Petter Selasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 288d59ecb2SHans Petter Selasky */ 298d59ecb2SHans Petter Selasky 308d59ecb2SHans Petter Selasky #include <sys/cdefs.h> 318d59ecb2SHans Petter Selasky __FBSDID("$FreeBSD$"); 328d59ecb2SHans Petter Selasky 338d59ecb2SHans Petter Selasky #include <sys/param.h> 348d59ecb2SHans Petter Selasky #include <sys/systm.h> 358d59ecb2SHans Petter Selasky #include <sys/malloc.h> 368d59ecb2SHans Petter Selasky #include <sys/kernel.h> 378d59ecb2SHans Petter Selasky #include <sys/sysctl.h> 388d59ecb2SHans Petter Selasky #include <sys/lock.h> 398d59ecb2SHans Petter Selasky #include <sys/mutex.h> 408d59ecb2SHans Petter Selasky 418d59ecb2SHans Petter Selasky #include <machine/stdarg.h> 428d59ecb2SHans Petter Selasky 438d59ecb2SHans Petter Selasky #include <linux/bitops.h> 448d59ecb2SHans Petter Selasky #include <linux/kobject.h> 458d59ecb2SHans Petter Selasky #include <linux/slab.h> 468d59ecb2SHans Petter Selasky #include <linux/idr.h> 478d59ecb2SHans Petter Selasky #include <linux/err.h> 488d59ecb2SHans Petter Selasky 498d59ecb2SHans Petter Selasky /* 508d59ecb2SHans Petter Selasky * IDR Implementation. 518d59ecb2SHans Petter Selasky * 528d59ecb2SHans Petter Selasky * This is quick and dirty and not as re-entrant as the linux version 538d59ecb2SHans Petter Selasky * however it should be fairly fast. It is basically a radix tree with 548d59ecb2SHans Petter Selasky * a builtin bitmap for allocation. 558d59ecb2SHans Petter Selasky */ 568d59ecb2SHans Petter Selasky static MALLOC_DEFINE(M_IDR, "idr", "Linux IDR compat"); 578d59ecb2SHans Petter Selasky 588d59ecb2SHans Petter Selasky static inline int 598d59ecb2SHans Petter Selasky idr_max(struct idr *idr) 608d59ecb2SHans Petter Selasky { 618d59ecb2SHans Petter Selasky return (1 << (idr->layers * IDR_BITS)) - 1; 628d59ecb2SHans Petter Selasky } 638d59ecb2SHans Petter Selasky 648d59ecb2SHans Petter Selasky static inline int 658d59ecb2SHans Petter Selasky idr_pos(int id, int layer) 668d59ecb2SHans Petter Selasky { 678d59ecb2SHans Petter Selasky return (id >> (IDR_BITS * layer)) & IDR_MASK; 688d59ecb2SHans Petter Selasky } 698d59ecb2SHans Petter Selasky 708d59ecb2SHans Petter Selasky void 718d59ecb2SHans Petter Selasky idr_init(struct idr *idr) 728d59ecb2SHans Petter Selasky { 738d59ecb2SHans Petter Selasky bzero(idr, sizeof(*idr)); 748d59ecb2SHans Petter Selasky mtx_init(&idr->lock, "idr", NULL, MTX_DEF); 758d59ecb2SHans Petter Selasky } 768d59ecb2SHans Petter Selasky 778d59ecb2SHans Petter Selasky /* Only frees cached pages. */ 788d59ecb2SHans Petter Selasky void 798d59ecb2SHans Petter Selasky idr_destroy(struct idr *idr) 808d59ecb2SHans Petter Selasky { 818d59ecb2SHans Petter Selasky struct idr_layer *il, *iln; 828d59ecb2SHans Petter Selasky 838d59ecb2SHans Petter Selasky idr_remove_all(idr); 848d59ecb2SHans Petter Selasky mtx_lock(&idr->lock); 858d59ecb2SHans Petter Selasky for (il = idr->free; il != NULL; il = iln) { 868d59ecb2SHans Petter Selasky iln = il->ary[0]; 878d59ecb2SHans Petter Selasky free(il, M_IDR); 888d59ecb2SHans Petter Selasky } 898d59ecb2SHans Petter Selasky mtx_unlock(&idr->lock); 905d35d777SHans Petter Selasky mtx_destroy(&idr->lock); 918d59ecb2SHans Petter Selasky } 928d59ecb2SHans Petter Selasky 938d59ecb2SHans Petter Selasky static void 948d59ecb2SHans Petter Selasky idr_remove_layer(struct idr_layer *il, int layer) 958d59ecb2SHans Petter Selasky { 968d59ecb2SHans Petter Selasky int i; 978d59ecb2SHans Petter Selasky 988d59ecb2SHans Petter Selasky if (il == NULL) 998d59ecb2SHans Petter Selasky return; 1008d59ecb2SHans Petter Selasky if (layer == 0) { 1018d59ecb2SHans Petter Selasky free(il, M_IDR); 1028d59ecb2SHans Petter Selasky return; 1038d59ecb2SHans Petter Selasky } 1048d59ecb2SHans Petter Selasky for (i = 0; i < IDR_SIZE; i++) 1058d59ecb2SHans Petter Selasky if (il->ary[i]) 1068d59ecb2SHans Petter Selasky idr_remove_layer(il->ary[i], layer - 1); 1078d59ecb2SHans Petter Selasky } 1088d59ecb2SHans Petter Selasky 1098d59ecb2SHans Petter Selasky void 1108d59ecb2SHans Petter Selasky idr_remove_all(struct idr *idr) 1118d59ecb2SHans Petter Selasky { 1128d59ecb2SHans Petter Selasky 1138d59ecb2SHans Petter Selasky mtx_lock(&idr->lock); 1148d59ecb2SHans Petter Selasky idr_remove_layer(idr->top, idr->layers - 1); 1158d59ecb2SHans Petter Selasky idr->top = NULL; 1168d59ecb2SHans Petter Selasky idr->layers = 0; 1178d59ecb2SHans Petter Selasky mtx_unlock(&idr->lock); 1188d59ecb2SHans Petter Selasky } 1198d59ecb2SHans Petter Selasky 1202d1bee65SHans Petter Selasky static void 1212d1bee65SHans Petter Selasky idr_remove_locked(struct idr *idr, int id) 1228d59ecb2SHans Petter Selasky { 1238d59ecb2SHans Petter Selasky struct idr_layer *il; 1248d59ecb2SHans Petter Selasky int layer; 1258d59ecb2SHans Petter Selasky int idx; 1268d59ecb2SHans Petter Selasky 1278d59ecb2SHans Petter Selasky id &= MAX_ID_MASK; 1288d59ecb2SHans Petter Selasky il = idr->top; 1298d59ecb2SHans Petter Selasky layer = idr->layers - 1; 1302d1bee65SHans Petter Selasky if (il == NULL || id > idr_max(idr)) 1318d59ecb2SHans Petter Selasky return; 1328d59ecb2SHans Petter Selasky /* 1338d59ecb2SHans Petter Selasky * Walk down the tree to this item setting bitmaps along the way 1348d59ecb2SHans Petter Selasky * as we know at least one item will be free along this path. 1358d59ecb2SHans Petter Selasky */ 1368d59ecb2SHans Petter Selasky while (layer && il) { 1378d59ecb2SHans Petter Selasky idx = idr_pos(id, layer); 1388d59ecb2SHans Petter Selasky il->bitmap |= 1 << idx; 1398d59ecb2SHans Petter Selasky il = il->ary[idx]; 1408d59ecb2SHans Petter Selasky layer--; 1418d59ecb2SHans Petter Selasky } 1428d59ecb2SHans Petter Selasky idx = id & IDR_MASK; 1438d59ecb2SHans Petter Selasky /* 1448d59ecb2SHans Petter Selasky * At this point we've set free space bitmaps up the whole tree. 1458d59ecb2SHans Petter Selasky * We could make this non-fatal and unwind but linux dumps a stack 1468d59ecb2SHans Petter Selasky * and a warning so I don't think it's necessary. 1478d59ecb2SHans Petter Selasky */ 1488d59ecb2SHans Petter Selasky if (il == NULL || (il->bitmap & (1 << idx)) != 0) 1498d59ecb2SHans Petter Selasky panic("idr_remove: Item %d not allocated (%p, %p)\n", 1508d59ecb2SHans Petter Selasky id, idr, il); 1518d59ecb2SHans Petter Selasky il->ary[idx] = NULL; 1528d59ecb2SHans Petter Selasky il->bitmap |= 1 << idx; 1532d1bee65SHans Petter Selasky } 1542d1bee65SHans Petter Selasky 1552d1bee65SHans Petter Selasky void 1562d1bee65SHans Petter Selasky idr_remove(struct idr *idr, int id) 1572d1bee65SHans Petter Selasky { 1582d1bee65SHans Petter Selasky mtx_lock(&idr->lock); 1592d1bee65SHans Petter Selasky idr_remove_locked(idr, id); 1608d59ecb2SHans Petter Selasky mtx_unlock(&idr->lock); 1618d59ecb2SHans Petter Selasky } 1628d59ecb2SHans Petter Selasky 163f8221002SHans Petter Selasky 164f8221002SHans Petter Selasky static inline struct idr_layer * 165f8221002SHans Petter Selasky idr_find_layer_locked(struct idr *idr, int id) 166f8221002SHans Petter Selasky { 167f8221002SHans Petter Selasky struct idr_layer *il; 168f8221002SHans Petter Selasky int layer; 169f8221002SHans Petter Selasky 170f8221002SHans Petter Selasky id &= MAX_ID_MASK; 171f8221002SHans Petter Selasky il = idr->top; 172f8221002SHans Petter Selasky layer = idr->layers - 1; 173f8221002SHans Petter Selasky if (il == NULL || id > idr_max(idr)) 174f8221002SHans Petter Selasky return (NULL); 175f8221002SHans Petter Selasky while (layer && il) { 176f8221002SHans Petter Selasky il = il->ary[idr_pos(id, layer)]; 177f8221002SHans Petter Selasky layer--; 178f8221002SHans Petter Selasky } 179f8221002SHans Petter Selasky return (il); 180f8221002SHans Petter Selasky } 181f8221002SHans Petter Selasky 1828d59ecb2SHans Petter Selasky void * 1838d59ecb2SHans Petter Selasky idr_replace(struct idr *idr, void *ptr, int id) 1848d59ecb2SHans Petter Selasky { 1858d59ecb2SHans Petter Selasky struct idr_layer *il; 1868d59ecb2SHans Petter Selasky void *res; 1878d59ecb2SHans Petter Selasky int idx; 1888d59ecb2SHans Petter Selasky 1898d59ecb2SHans Petter Selasky mtx_lock(&idr->lock); 190f8221002SHans Petter Selasky il = idr_find_layer_locked(idr, id); 1918d59ecb2SHans Petter Selasky idx = id & IDR_MASK; 192f8221002SHans Petter Selasky 193f8221002SHans Petter Selasky /* Replace still returns an error if the item was not allocated. */ 194f8221002SHans Petter Selasky if (il == NULL || (il->bitmap & (1 << idx))) { 195f8221002SHans Petter Selasky res = ERR_PTR(-ENOENT); 196f8221002SHans Petter Selasky } else { 1978d59ecb2SHans Petter Selasky res = il->ary[idx]; 1988d59ecb2SHans Petter Selasky il->ary[idx] = ptr; 1998d59ecb2SHans Petter Selasky } 2008d59ecb2SHans Petter Selasky mtx_unlock(&idr->lock); 2018d59ecb2SHans Petter Selasky return (res); 2028d59ecb2SHans Petter Selasky } 2038d59ecb2SHans Petter Selasky 2048d59ecb2SHans Petter Selasky static inline void * 2058d59ecb2SHans Petter Selasky idr_find_locked(struct idr *idr, int id) 2068d59ecb2SHans Petter Selasky { 2078d59ecb2SHans Petter Selasky struct idr_layer *il; 2088d59ecb2SHans Petter Selasky void *res; 2098d59ecb2SHans Petter Selasky 2108d59ecb2SHans Petter Selasky mtx_assert(&idr->lock, MA_OWNED); 211f8221002SHans Petter Selasky il = idr_find_layer_locked(idr, id); 2128d59ecb2SHans Petter Selasky if (il != NULL) 2138d59ecb2SHans Petter Selasky res = il->ary[id & IDR_MASK]; 214f8221002SHans Petter Selasky else 215f8221002SHans Petter Selasky res = NULL; 2168d59ecb2SHans Petter Selasky return (res); 2178d59ecb2SHans Petter Selasky } 2188d59ecb2SHans Petter Selasky 2198d59ecb2SHans Petter Selasky void * 2208d59ecb2SHans Petter Selasky idr_find(struct idr *idr, int id) 2218d59ecb2SHans Petter Selasky { 2228d59ecb2SHans Petter Selasky void *res; 2238d59ecb2SHans Petter Selasky 2248d59ecb2SHans Petter Selasky mtx_lock(&idr->lock); 2258d59ecb2SHans Petter Selasky res = idr_find_locked(idr, id); 2268d59ecb2SHans Petter Selasky mtx_unlock(&idr->lock); 2278d59ecb2SHans Petter Selasky return (res); 2288d59ecb2SHans Petter Selasky } 2298d59ecb2SHans Petter Selasky 230fd42d623SHans Petter Selasky void * 231fd42d623SHans Petter Selasky idr_get_next(struct idr *idr, int *nextidp) 232fd42d623SHans Petter Selasky { 233fd42d623SHans Petter Selasky void *res = NULL; 234fd42d623SHans Petter Selasky int id = *nextidp; 235fd42d623SHans Petter Selasky 236fd42d623SHans Petter Selasky mtx_lock(&idr->lock); 237fd42d623SHans Petter Selasky for (; id <= idr_max(idr); id++) { 238fd42d623SHans Petter Selasky res = idr_find_locked(idr, id); 239fd42d623SHans Petter Selasky if (res == NULL) 240fd42d623SHans Petter Selasky continue; 241fd42d623SHans Petter Selasky *nextidp = id; 242fd42d623SHans Petter Selasky break; 243fd42d623SHans Petter Selasky } 244fd42d623SHans Petter Selasky mtx_unlock(&idr->lock); 245fd42d623SHans Petter Selasky return (res); 246fd42d623SHans Petter Selasky } 247fd42d623SHans Petter Selasky 2488d59ecb2SHans Petter Selasky int 2498d59ecb2SHans Petter Selasky idr_pre_get(struct idr *idr, gfp_t gfp_mask) 2508d59ecb2SHans Petter Selasky { 2518d59ecb2SHans Petter Selasky struct idr_layer *il, *iln; 2528d59ecb2SHans Petter Selasky struct idr_layer *head; 2538d59ecb2SHans Petter Selasky int need; 2548d59ecb2SHans Petter Selasky 2558d59ecb2SHans Petter Selasky mtx_lock(&idr->lock); 2568d59ecb2SHans Petter Selasky for (;;) { 2578d59ecb2SHans Petter Selasky need = idr->layers + 1; 2588d59ecb2SHans Petter Selasky for (il = idr->free; il != NULL; il = il->ary[0]) 2598d59ecb2SHans Petter Selasky need--; 2608d59ecb2SHans Petter Selasky mtx_unlock(&idr->lock); 2618d59ecb2SHans Petter Selasky if (need <= 0) 2628d59ecb2SHans Petter Selasky break; 2638d59ecb2SHans Petter Selasky for (head = NULL; need; need--) { 2648d59ecb2SHans Petter Selasky iln = malloc(sizeof(*il), M_IDR, M_ZERO | gfp_mask); 2658d59ecb2SHans Petter Selasky if (iln == NULL) 2668d59ecb2SHans Petter Selasky break; 2678d59ecb2SHans Petter Selasky bitmap_fill(&iln->bitmap, IDR_SIZE); 2688d59ecb2SHans Petter Selasky if (head != NULL) { 2698d59ecb2SHans Petter Selasky il->ary[0] = iln; 2708d59ecb2SHans Petter Selasky il = iln; 2718d59ecb2SHans Petter Selasky } else 2728d59ecb2SHans Petter Selasky head = il = iln; 2738d59ecb2SHans Petter Selasky } 2748d59ecb2SHans Petter Selasky if (head == NULL) 2758d59ecb2SHans Petter Selasky return (0); 2768d59ecb2SHans Petter Selasky mtx_lock(&idr->lock); 2778d59ecb2SHans Petter Selasky il->ary[0] = idr->free; 2788d59ecb2SHans Petter Selasky idr->free = head; 2798d59ecb2SHans Petter Selasky } 2808d59ecb2SHans Petter Selasky return (1); 2818d59ecb2SHans Petter Selasky } 2828d59ecb2SHans Petter Selasky 2838d59ecb2SHans Petter Selasky static inline struct idr_layer * 2848d59ecb2SHans Petter Selasky idr_get(struct idr *idr) 2858d59ecb2SHans Petter Selasky { 2868d59ecb2SHans Petter Selasky struct idr_layer *il; 2878d59ecb2SHans Petter Selasky 2888d59ecb2SHans Petter Selasky il = idr->free; 2898d59ecb2SHans Petter Selasky if (il) { 2908d59ecb2SHans Petter Selasky idr->free = il->ary[0]; 2918d59ecb2SHans Petter Selasky il->ary[0] = NULL; 2928d59ecb2SHans Petter Selasky return (il); 2938d59ecb2SHans Petter Selasky } 2948d59ecb2SHans Petter Selasky il = malloc(sizeof(*il), M_IDR, M_ZERO | M_NOWAIT); 2958d59ecb2SHans Petter Selasky bitmap_fill(&il->bitmap, IDR_SIZE); 2968d59ecb2SHans Petter Selasky return (il); 2978d59ecb2SHans Petter Selasky } 2988d59ecb2SHans Petter Selasky 2998d59ecb2SHans Petter Selasky /* 3008d59ecb2SHans Petter Selasky * Could be implemented as get_new_above(idr, ptr, 0, idp) but written 3018d59ecb2SHans Petter Selasky * first for simplicity sake. 3028d59ecb2SHans Petter Selasky */ 3032d1bee65SHans Petter Selasky static int 3042d1bee65SHans Petter Selasky idr_get_new_locked(struct idr *idr, void *ptr, int *idp) 3058d59ecb2SHans Petter Selasky { 3068d59ecb2SHans Petter Selasky struct idr_layer *stack[MAX_LEVEL]; 3078d59ecb2SHans Petter Selasky struct idr_layer *il; 3088d59ecb2SHans Petter Selasky int error; 3098d59ecb2SHans Petter Selasky int layer; 3108d59ecb2SHans Petter Selasky int idx; 3118d59ecb2SHans Petter Selasky int id; 3128d59ecb2SHans Petter Selasky 3132d1bee65SHans Petter Selasky mtx_assert(&idr->lock, MA_OWNED); 3142d1bee65SHans Petter Selasky 3158d59ecb2SHans Petter Selasky error = -EAGAIN; 3168d59ecb2SHans Petter Selasky /* 3178d59ecb2SHans Petter Selasky * Expand the tree until there is free space. 3188d59ecb2SHans Petter Selasky */ 3198d59ecb2SHans Petter Selasky if (idr->top == NULL || idr->top->bitmap == 0) { 3208d59ecb2SHans Petter Selasky if (idr->layers == MAX_LEVEL + 1) { 3218d59ecb2SHans Petter Selasky error = -ENOSPC; 3228d59ecb2SHans Petter Selasky goto out; 3238d59ecb2SHans Petter Selasky } 3248d59ecb2SHans Petter Selasky il = idr_get(idr); 3258d59ecb2SHans Petter Selasky if (il == NULL) 3268d59ecb2SHans Petter Selasky goto out; 3278d59ecb2SHans Petter Selasky il->ary[0] = idr->top; 3288d59ecb2SHans Petter Selasky if (idr->top) 3298d59ecb2SHans Petter Selasky il->bitmap &= ~1; 3308d59ecb2SHans Petter Selasky idr->top = il; 3318d59ecb2SHans Petter Selasky idr->layers++; 3328d59ecb2SHans Petter Selasky } 3338d59ecb2SHans Petter Selasky il = idr->top; 3348d59ecb2SHans Petter Selasky id = 0; 3358d59ecb2SHans Petter Selasky /* 3368d59ecb2SHans Petter Selasky * Walk the tree following free bitmaps, record our path. 3378d59ecb2SHans Petter Selasky */ 3388d59ecb2SHans Petter Selasky for (layer = idr->layers - 1;; layer--) { 3398d59ecb2SHans Petter Selasky stack[layer] = il; 3408d59ecb2SHans Petter Selasky idx = ffsl(il->bitmap); 3418d59ecb2SHans Petter Selasky if (idx == 0) 3428d59ecb2SHans Petter Selasky panic("idr_get_new: Invalid leaf state (%p, %p)\n", 3438d59ecb2SHans Petter Selasky idr, il); 3448d59ecb2SHans Petter Selasky idx--; 3458d59ecb2SHans Petter Selasky id |= idx << (layer * IDR_BITS); 3468d59ecb2SHans Petter Selasky if (layer == 0) 3478d59ecb2SHans Petter Selasky break; 3488d59ecb2SHans Petter Selasky if (il->ary[idx] == NULL) { 3498d59ecb2SHans Petter Selasky il->ary[idx] = idr_get(idr); 3508d59ecb2SHans Petter Selasky if (il->ary[idx] == NULL) 3518d59ecb2SHans Petter Selasky goto out; 3528d59ecb2SHans Petter Selasky } 3538d59ecb2SHans Petter Selasky il = il->ary[idx]; 3548d59ecb2SHans Petter Selasky } 3558d59ecb2SHans Petter Selasky /* 3568d59ecb2SHans Petter Selasky * Allocate the leaf to the consumer. 3578d59ecb2SHans Petter Selasky */ 3588d59ecb2SHans Petter Selasky il->bitmap &= ~(1 << idx); 3598d59ecb2SHans Petter Selasky il->ary[idx] = ptr; 3608d59ecb2SHans Petter Selasky *idp = id; 3618d59ecb2SHans Petter Selasky /* 3628d59ecb2SHans Petter Selasky * Clear bitmaps potentially up to the root. 3638d59ecb2SHans Petter Selasky */ 3648d59ecb2SHans Petter Selasky while (il->bitmap == 0 && ++layer < idr->layers) { 3658d59ecb2SHans Petter Selasky il = stack[layer]; 3668d59ecb2SHans Petter Selasky il->bitmap &= ~(1 << idr_pos(id, layer)); 3678d59ecb2SHans Petter Selasky } 3688d59ecb2SHans Petter Selasky error = 0; 3698d59ecb2SHans Petter Selasky out: 3708d59ecb2SHans Petter Selasky #ifdef INVARIANTS 3718d59ecb2SHans Petter Selasky if (error == 0 && idr_find_locked(idr, id) != ptr) { 3728d59ecb2SHans Petter Selasky panic("idr_get_new: Failed for idr %p, id %d, ptr %p\n", 3738d59ecb2SHans Petter Selasky idr, id, ptr); 3748d59ecb2SHans Petter Selasky } 3758d59ecb2SHans Petter Selasky #endif 3768d59ecb2SHans Petter Selasky return (error); 3778d59ecb2SHans Petter Selasky } 3788d59ecb2SHans Petter Selasky 3798d59ecb2SHans Petter Selasky int 3802d1bee65SHans Petter Selasky idr_get_new(struct idr *idr, void *ptr, int *idp) 3812d1bee65SHans Petter Selasky { 3822d1bee65SHans Petter Selasky int retval; 3832d1bee65SHans Petter Selasky 3842d1bee65SHans Petter Selasky mtx_lock(&idr->lock); 3852d1bee65SHans Petter Selasky retval = idr_get_new_locked(idr, ptr, idp); 3862d1bee65SHans Petter Selasky mtx_unlock(&idr->lock); 3872d1bee65SHans Petter Selasky return (retval); 3882d1bee65SHans Petter Selasky } 3892d1bee65SHans Petter Selasky 3902d1bee65SHans Petter Selasky static int 3912d1bee65SHans Petter Selasky idr_get_new_above_locked(struct idr *idr, void *ptr, int starting_id, int *idp) 3928d59ecb2SHans Petter Selasky { 3938d59ecb2SHans Petter Selasky struct idr_layer *stack[MAX_LEVEL]; 3948d59ecb2SHans Petter Selasky struct idr_layer *il; 3958d59ecb2SHans Petter Selasky int error; 3968d59ecb2SHans Petter Selasky int layer; 3978d59ecb2SHans Petter Selasky int idx, sidx; 3988d59ecb2SHans Petter Selasky int id; 3998d59ecb2SHans Petter Selasky 4002d1bee65SHans Petter Selasky mtx_assert(&idr->lock, MA_OWNED); 4012d1bee65SHans Petter Selasky 4028d59ecb2SHans Petter Selasky error = -EAGAIN; 4038d59ecb2SHans Petter Selasky /* 4048d59ecb2SHans Petter Selasky * Compute the layers required to support starting_id and the mask 4058d59ecb2SHans Petter Selasky * at the top layer. 4068d59ecb2SHans Petter Selasky */ 4078d59ecb2SHans Petter Selasky restart: 4088d59ecb2SHans Petter Selasky idx = starting_id; 4098d59ecb2SHans Petter Selasky layer = 0; 4108d59ecb2SHans Petter Selasky while (idx & ~IDR_MASK) { 4118d59ecb2SHans Petter Selasky layer++; 4128d59ecb2SHans Petter Selasky idx >>= IDR_BITS; 4138d59ecb2SHans Petter Selasky } 4148d59ecb2SHans Petter Selasky if (layer == MAX_LEVEL + 1) { 4158d59ecb2SHans Petter Selasky error = -ENOSPC; 4168d59ecb2SHans Petter Selasky goto out; 4178d59ecb2SHans Petter Selasky } 4188d59ecb2SHans Petter Selasky /* 4198d59ecb2SHans Petter Selasky * Expand the tree until there is free space at or beyond starting_id. 4208d59ecb2SHans Petter Selasky */ 4218d59ecb2SHans Petter Selasky while (idr->layers <= layer || 4228d59ecb2SHans Petter Selasky idr->top->bitmap < (1 << idr_pos(starting_id, idr->layers - 1))) { 4238d59ecb2SHans Petter Selasky if (idr->layers == MAX_LEVEL + 1) { 4248d59ecb2SHans Petter Selasky error = -ENOSPC; 4258d59ecb2SHans Petter Selasky goto out; 4268d59ecb2SHans Petter Selasky } 4278d59ecb2SHans Petter Selasky il = idr_get(idr); 4288d59ecb2SHans Petter Selasky if (il == NULL) 4298d59ecb2SHans Petter Selasky goto out; 4308d59ecb2SHans Petter Selasky il->ary[0] = idr->top; 4318d59ecb2SHans Petter Selasky if (idr->top && idr->top->bitmap == 0) 4328d59ecb2SHans Petter Selasky il->bitmap &= ~1; 4338d59ecb2SHans Petter Selasky idr->top = il; 4348d59ecb2SHans Petter Selasky idr->layers++; 4358d59ecb2SHans Petter Selasky } 4368d59ecb2SHans Petter Selasky il = idr->top; 4378d59ecb2SHans Petter Selasky id = 0; 4388d59ecb2SHans Petter Selasky /* 4398d59ecb2SHans Petter Selasky * Walk the tree following free bitmaps, record our path. 4408d59ecb2SHans Petter Selasky */ 4418d59ecb2SHans Petter Selasky for (layer = idr->layers - 1;; layer--) { 4428d59ecb2SHans Petter Selasky stack[layer] = il; 4438d59ecb2SHans Petter Selasky sidx = idr_pos(starting_id, layer); 4448d59ecb2SHans Petter Selasky /* Returns index numbered from 0 or size if none exists. */ 4458d59ecb2SHans Petter Selasky idx = find_next_bit(&il->bitmap, IDR_SIZE, sidx); 4468d59ecb2SHans Petter Selasky if (idx == IDR_SIZE && sidx == 0) 4478d59ecb2SHans Petter Selasky panic("idr_get_new: Invalid leaf state (%p, %p)\n", 4488d59ecb2SHans Petter Selasky idr, il); 4498d59ecb2SHans Petter Selasky /* 4508d59ecb2SHans Petter Selasky * We may have walked a path where there was a free bit but 4518d59ecb2SHans Petter Selasky * it was lower than what we wanted. Restart the search with 4528d59ecb2SHans Petter Selasky * a larger starting id. id contains the progress we made so 4538d59ecb2SHans Petter Selasky * far. Search the leaf one above this level. This may 4548d59ecb2SHans Petter Selasky * restart as many as MAX_LEVEL times but that is expected 4558d59ecb2SHans Petter Selasky * to be rare. 4568d59ecb2SHans Petter Selasky */ 4578d59ecb2SHans Petter Selasky if (idx == IDR_SIZE) { 4588d59ecb2SHans Petter Selasky starting_id = id + (1 << ((layer + 1) * IDR_BITS)); 4598d59ecb2SHans Petter Selasky goto restart; 4608d59ecb2SHans Petter Selasky } 4618d59ecb2SHans Petter Selasky if (idx > sidx) 4628d59ecb2SHans Petter Selasky starting_id = 0; /* Search the whole subtree. */ 4638d59ecb2SHans Petter Selasky id |= idx << (layer * IDR_BITS); 4648d59ecb2SHans Petter Selasky if (layer == 0) 4658d59ecb2SHans Petter Selasky break; 4668d59ecb2SHans Petter Selasky if (il->ary[idx] == NULL) { 4678d59ecb2SHans Petter Selasky il->ary[idx] = idr_get(idr); 4688d59ecb2SHans Petter Selasky if (il->ary[idx] == NULL) 4698d59ecb2SHans Petter Selasky goto out; 4708d59ecb2SHans Petter Selasky } 4718d59ecb2SHans Petter Selasky il = il->ary[idx]; 4728d59ecb2SHans Petter Selasky } 4738d59ecb2SHans Petter Selasky /* 4748d59ecb2SHans Petter Selasky * Allocate the leaf to the consumer. 4758d59ecb2SHans Petter Selasky */ 4768d59ecb2SHans Petter Selasky il->bitmap &= ~(1 << idx); 4778d59ecb2SHans Petter Selasky il->ary[idx] = ptr; 4788d59ecb2SHans Petter Selasky *idp = id; 4798d59ecb2SHans Petter Selasky /* 4808d59ecb2SHans Petter Selasky * Clear bitmaps potentially up to the root. 4818d59ecb2SHans Petter Selasky */ 4828d59ecb2SHans Petter Selasky while (il->bitmap == 0 && ++layer < idr->layers) { 4838d59ecb2SHans Petter Selasky il = stack[layer]; 4848d59ecb2SHans Petter Selasky il->bitmap &= ~(1 << idr_pos(id, layer)); 4858d59ecb2SHans Petter Selasky } 4868d59ecb2SHans Petter Selasky error = 0; 4878d59ecb2SHans Petter Selasky out: 4888d59ecb2SHans Petter Selasky #ifdef INVARIANTS 4898d59ecb2SHans Petter Selasky if (error == 0 && idr_find_locked(idr, id) != ptr) { 4908d59ecb2SHans Petter Selasky panic("idr_get_new_above: Failed for idr %p, id %d, ptr %p\n", 4918d59ecb2SHans Petter Selasky idr, id, ptr); 4928d59ecb2SHans Petter Selasky } 4938d59ecb2SHans Petter Selasky #endif 4948d59ecb2SHans Petter Selasky return (error); 4958d59ecb2SHans Petter Selasky } 4962d1bee65SHans Petter Selasky 4972d1bee65SHans Petter Selasky int 4982d1bee65SHans Petter Selasky idr_get_new_above(struct idr *idr, void *ptr, int starting_id, int *idp) 4992d1bee65SHans Petter Selasky { 5002d1bee65SHans Petter Selasky int retval; 5012d1bee65SHans Petter Selasky 5022d1bee65SHans Petter Selasky mtx_lock(&idr->lock); 5032d1bee65SHans Petter Selasky retval = idr_get_new_above_locked(idr, ptr, starting_id, idp); 5042d1bee65SHans Petter Selasky mtx_unlock(&idr->lock); 5052d1bee65SHans Petter Selasky return (retval); 5062d1bee65SHans Petter Selasky } 5072d1bee65SHans Petter Selasky 508fd42d623SHans Petter Selasky int 509fd42d623SHans Petter Selasky ida_get_new_above(struct ida *ida, int starting_id, int *p_id) 510fd42d623SHans Petter Selasky { 511fd42d623SHans Petter Selasky return (idr_get_new_above(&ida->idr, NULL, starting_id, p_id)); 512fd42d623SHans Petter Selasky } 513fd42d623SHans Petter Selasky 5142d1bee65SHans Petter Selasky static int 5152d1bee65SHans Petter Selasky idr_alloc_locked(struct idr *idr, void *ptr, int start, int end) 5162d1bee65SHans Petter Selasky { 5172d1bee65SHans Petter Selasky int max = end > 0 ? end - 1 : INT_MAX; 5182d1bee65SHans Petter Selasky int error; 5192d1bee65SHans Petter Selasky int id; 5202d1bee65SHans Petter Selasky 5212d1bee65SHans Petter Selasky mtx_assert(&idr->lock, MA_OWNED); 5222d1bee65SHans Petter Selasky 5232d1bee65SHans Petter Selasky if (unlikely(start < 0)) 5242d1bee65SHans Petter Selasky return (-EINVAL); 5252d1bee65SHans Petter Selasky if (unlikely(max < start)) 5262d1bee65SHans Petter Selasky return (-ENOSPC); 5272d1bee65SHans Petter Selasky 5282d1bee65SHans Petter Selasky if (start == 0) 5292d1bee65SHans Petter Selasky error = idr_get_new_locked(idr, ptr, &id); 5302d1bee65SHans Petter Selasky else 5312d1bee65SHans Petter Selasky error = idr_get_new_above_locked(idr, ptr, start, &id); 5322d1bee65SHans Petter Selasky 5332d1bee65SHans Petter Selasky if (unlikely(error < 0)) 5342d1bee65SHans Petter Selasky return (error); 5352d1bee65SHans Petter Selasky if (unlikely(id > max)) { 5362d1bee65SHans Petter Selasky idr_remove_locked(idr, id); 5372d1bee65SHans Petter Selasky return (-ENOSPC); 5382d1bee65SHans Petter Selasky } 5392d1bee65SHans Petter Selasky return (id); 5402d1bee65SHans Petter Selasky } 5412d1bee65SHans Petter Selasky 5422d1bee65SHans Petter Selasky int 5432d1bee65SHans Petter Selasky idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask) 5442d1bee65SHans Petter Selasky { 5452d1bee65SHans Petter Selasky int retval; 5462d1bee65SHans Petter Selasky 5472d1bee65SHans Petter Selasky mtx_lock(&idr->lock); 5482d1bee65SHans Petter Selasky retval = idr_alloc_locked(idr, ptr, start, end); 5492d1bee65SHans Petter Selasky mtx_unlock(&idr->lock); 5502d1bee65SHans Petter Selasky return (retval); 5512d1bee65SHans Petter Selasky } 5522d1bee65SHans Petter Selasky 5532d1bee65SHans Petter Selasky int 5542d1bee65SHans Petter Selasky idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask) 5552d1bee65SHans Petter Selasky { 5562d1bee65SHans Petter Selasky int retval; 5572d1bee65SHans Petter Selasky 5582d1bee65SHans Petter Selasky mtx_lock(&idr->lock); 5592d1bee65SHans Petter Selasky retval = idr_alloc_locked(idr, ptr, max(start, idr->next_cyclic_id), end); 5602d1bee65SHans Petter Selasky if (unlikely(retval == -ENOSPC)) 5612d1bee65SHans Petter Selasky retval = idr_alloc_locked(idr, ptr, start, end); 5622d1bee65SHans Petter Selasky if (likely(retval >= 0)) 5632d1bee65SHans Petter Selasky idr->next_cyclic_id = retval + 1; 5642d1bee65SHans Petter Selasky mtx_unlock(&idr->lock); 5652d1bee65SHans Petter Selasky return (retval); 5662d1bee65SHans Petter Selasky } 567fd42d623SHans Petter Selasky 568fd42d623SHans Petter Selasky static int 569fd42d623SHans Petter Selasky idr_for_each_layer(struct idr_layer *il, int layer, 570fd42d623SHans Petter Selasky int (*f)(int id, void *p, void *data), void *data) 571fd42d623SHans Petter Selasky { 572fd42d623SHans Petter Selasky int i, err; 573fd42d623SHans Petter Selasky 574fd42d623SHans Petter Selasky if (il == NULL) 575fd42d623SHans Petter Selasky return (0); 576fd42d623SHans Petter Selasky if (layer == 0) { 577fd42d623SHans Petter Selasky for (i = 0; i < IDR_SIZE; i++) { 578fd42d623SHans Petter Selasky if (il->ary[i] == NULL) 579fd42d623SHans Petter Selasky continue; 580fd42d623SHans Petter Selasky err = f(i, il->ary[i], data); 581fd42d623SHans Petter Selasky if (err) 582fd42d623SHans Petter Selasky return (err); 583fd42d623SHans Petter Selasky } 584fd42d623SHans Petter Selasky return (0); 585fd42d623SHans Petter Selasky } 586fd42d623SHans Petter Selasky for (i = 0; i < IDR_SIZE; i++) { 587fd42d623SHans Petter Selasky if (il->ary[i] == NULL) 588fd42d623SHans Petter Selasky continue; 589fd42d623SHans Petter Selasky err = idr_for_each_layer(il->ary[i], layer - 1, f, data); 590fd42d623SHans Petter Selasky if (err) 591fd42d623SHans Petter Selasky return (err); 592fd42d623SHans Petter Selasky } 593fd42d623SHans Petter Selasky return (0); 594fd42d623SHans Petter Selasky } 595fd42d623SHans Petter Selasky 596*dacb734eSHans Petter Selasky /* NOTE: It is not allowed to modify the IDR tree while it is being iterated */ 597fd42d623SHans Petter Selasky int 598fd42d623SHans Petter Selasky idr_for_each(struct idr *idp, int (*f)(int id, void *p, void *data), void *data) 599fd42d623SHans Petter Selasky { 600*dacb734eSHans Petter Selasky return (idr_for_each_layer(idp->top, idp->layers - 1, f, data)); 601fd42d623SHans Petter Selasky } 602fd42d623SHans Petter Selasky 603fd42d623SHans Petter Selasky int 604fd42d623SHans Petter Selasky ida_pre_get(struct ida *ida, gfp_t flags) 605fd42d623SHans Petter Selasky { 606fd42d623SHans Petter Selasky if (idr_pre_get(&ida->idr, flags) == 0) 607fd42d623SHans Petter Selasky return (0); 608fd42d623SHans Petter Selasky 609fd42d623SHans Petter Selasky if (ida->free_bitmap == NULL) { 610fd42d623SHans Petter Selasky ida->free_bitmap = 611fd42d623SHans Petter Selasky malloc(sizeof(struct ida_bitmap), M_IDR, flags); 612fd42d623SHans Petter Selasky } 613fd42d623SHans Petter Selasky return (ida->free_bitmap != NULL); 614fd42d623SHans Petter Selasky } 615fd42d623SHans Petter Selasky 616fd42d623SHans Petter Selasky int 617fd42d623SHans Petter Selasky ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, 618fd42d623SHans Petter Selasky gfp_t flags) 619fd42d623SHans Petter Selasky { 620fd42d623SHans Petter Selasky int ret, id; 621fd42d623SHans Petter Selasky unsigned int max; 622fd42d623SHans Petter Selasky 623fd42d623SHans Petter Selasky MPASS((int)start >= 0); 624fd42d623SHans Petter Selasky MPASS((int)end >= 0); 625fd42d623SHans Petter Selasky 626fd42d623SHans Petter Selasky if (end == 0) 627fd42d623SHans Petter Selasky max = 0x80000000; 628fd42d623SHans Petter Selasky else { 629fd42d623SHans Petter Selasky MPASS(end > start); 630fd42d623SHans Petter Selasky max = end - 1; 631fd42d623SHans Petter Selasky } 632fd42d623SHans Petter Selasky again: 633fd42d623SHans Petter Selasky if (!ida_pre_get(ida, flags)) 634fd42d623SHans Petter Selasky return (-ENOMEM); 635fd42d623SHans Petter Selasky 636fd42d623SHans Petter Selasky if ((ret = ida_get_new_above(ida, start, &id)) == 0) { 637fd42d623SHans Petter Selasky if (id > max) { 638fd42d623SHans Petter Selasky ida_remove(ida, id); 639fd42d623SHans Petter Selasky ret = -ENOSPC; 640fd42d623SHans Petter Selasky } else { 641fd42d623SHans Petter Selasky ret = id; 642fd42d623SHans Petter Selasky } 643fd42d623SHans Petter Selasky } 644fd42d623SHans Petter Selasky if (__predict_false(ret == -EAGAIN)) 645fd42d623SHans Petter Selasky goto again; 646fd42d623SHans Petter Selasky 647fd42d623SHans Petter Selasky return (ret); 648fd42d623SHans Petter Selasky } 649fd42d623SHans Petter Selasky 650fd42d623SHans Petter Selasky void 651fd42d623SHans Petter Selasky ida_simple_remove(struct ida *ida, unsigned int id) 652fd42d623SHans Petter Selasky { 653fd42d623SHans Petter Selasky idr_remove(&ida->idr, id); 654fd42d623SHans Petter Selasky } 655fd42d623SHans Petter Selasky 656fd42d623SHans Petter Selasky void 657fd42d623SHans Petter Selasky ida_remove(struct ida *ida, int id) 658fd42d623SHans Petter Selasky { 659fd42d623SHans Petter Selasky idr_remove(&ida->idr, id); 660fd42d623SHans Petter Selasky } 661fd42d623SHans Petter Selasky 662fd42d623SHans Petter Selasky void 663fd42d623SHans Petter Selasky ida_init(struct ida *ida) 664fd42d623SHans Petter Selasky { 665fd42d623SHans Petter Selasky idr_init(&ida->idr); 666fd42d623SHans Petter Selasky } 667fd42d623SHans Petter Selasky 668fd42d623SHans Petter Selasky void 669fd42d623SHans Petter Selasky ida_destroy(struct ida *ida) 670fd42d623SHans Petter Selasky { 671fd42d623SHans Petter Selasky idr_destroy(&ida->idr); 672fd42d623SHans Petter Selasky free(ida->free_bitmap, M_IDR); 673fd42d623SHans Petter Selasky } 674