xref: /freebsd/sys/compat/linuxkpi/common/src/linux_idr.c (revision dacb734ea83634e86d299475ca1a651ea9d688d6)
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