xref: /freebsd/sys/compat/linuxkpi/common/src/linux_idr.c (revision f8221002a5e2b3562e6272346bb639ac7a59d12a)
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 
163*f8221002SHans Petter Selasky 
164*f8221002SHans Petter Selasky static inline struct idr_layer *
165*f8221002SHans Petter Selasky idr_find_layer_locked(struct idr *idr, int id)
166*f8221002SHans Petter Selasky {
167*f8221002SHans Petter Selasky 	struct idr_layer *il;
168*f8221002SHans Petter Selasky 	int layer;
169*f8221002SHans Petter Selasky 
170*f8221002SHans Petter Selasky 	id &= MAX_ID_MASK;
171*f8221002SHans Petter Selasky 	il = idr->top;
172*f8221002SHans Petter Selasky 	layer = idr->layers - 1;
173*f8221002SHans Petter Selasky 	if (il == NULL || id > idr_max(idr))
174*f8221002SHans Petter Selasky 		return (NULL);
175*f8221002SHans Petter Selasky 	while (layer && il) {
176*f8221002SHans Petter Selasky 		il = il->ary[idr_pos(id, layer)];
177*f8221002SHans Petter Selasky 		layer--;
178*f8221002SHans Petter Selasky 	}
179*f8221002SHans Petter Selasky 	return (il);
180*f8221002SHans Petter Selasky }
181*f8221002SHans 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);
190*f8221002SHans Petter Selasky 	il = idr_find_layer_locked(idr, id);
1918d59ecb2SHans Petter Selasky 	idx = id & IDR_MASK;
192*f8221002SHans Petter Selasky 
193*f8221002SHans Petter Selasky 	/* Replace still returns an error if the item was not allocated. */
194*f8221002SHans Petter Selasky 	if (il == NULL || (il->bitmap & (1 << idx))) {
195*f8221002SHans Petter Selasky 		res = ERR_PTR(-ENOENT);
196*f8221002SHans 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);
211*f8221002SHans Petter Selasky 	il = idr_find_layer_locked(idr, id);
2128d59ecb2SHans Petter Selasky 	if (il != NULL)
2138d59ecb2SHans Petter Selasky 		res = il->ary[id & IDR_MASK];
214*f8221002SHans Petter Selasky 	else
215*f8221002SHans 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 
2308d59ecb2SHans Petter Selasky int
2318d59ecb2SHans Petter Selasky idr_pre_get(struct idr *idr, gfp_t gfp_mask)
2328d59ecb2SHans Petter Selasky {
2338d59ecb2SHans Petter Selasky 	struct idr_layer *il, *iln;
2348d59ecb2SHans Petter Selasky 	struct idr_layer *head;
2358d59ecb2SHans Petter Selasky 	int need;
2368d59ecb2SHans Petter Selasky 
2378d59ecb2SHans Petter Selasky 	mtx_lock(&idr->lock);
2388d59ecb2SHans Petter Selasky 	for (;;) {
2398d59ecb2SHans Petter Selasky 		need = idr->layers + 1;
2408d59ecb2SHans Petter Selasky 		for (il = idr->free; il != NULL; il = il->ary[0])
2418d59ecb2SHans Petter Selasky 			need--;
2428d59ecb2SHans Petter Selasky 		mtx_unlock(&idr->lock);
2438d59ecb2SHans Petter Selasky 		if (need <= 0)
2448d59ecb2SHans Petter Selasky 			break;
2458d59ecb2SHans Petter Selasky 		for (head = NULL; need; need--) {
2468d59ecb2SHans Petter Selasky 			iln = malloc(sizeof(*il), M_IDR, M_ZERO | gfp_mask);
2478d59ecb2SHans Petter Selasky 			if (iln == NULL)
2488d59ecb2SHans Petter Selasky 				break;
2498d59ecb2SHans Petter Selasky 			bitmap_fill(&iln->bitmap, IDR_SIZE);
2508d59ecb2SHans Petter Selasky 			if (head != NULL) {
2518d59ecb2SHans Petter Selasky 				il->ary[0] = iln;
2528d59ecb2SHans Petter Selasky 				il = iln;
2538d59ecb2SHans Petter Selasky 			} else
2548d59ecb2SHans Petter Selasky 				head = il = iln;
2558d59ecb2SHans Petter Selasky 		}
2568d59ecb2SHans Petter Selasky 		if (head == NULL)
2578d59ecb2SHans Petter Selasky 			return (0);
2588d59ecb2SHans Petter Selasky 		mtx_lock(&idr->lock);
2598d59ecb2SHans Petter Selasky 		il->ary[0] = idr->free;
2608d59ecb2SHans Petter Selasky 		idr->free = head;
2618d59ecb2SHans Petter Selasky 	}
2628d59ecb2SHans Petter Selasky 	return (1);
2638d59ecb2SHans Petter Selasky }
2648d59ecb2SHans Petter Selasky 
2658d59ecb2SHans Petter Selasky static inline struct idr_layer *
2668d59ecb2SHans Petter Selasky idr_get(struct idr *idr)
2678d59ecb2SHans Petter Selasky {
2688d59ecb2SHans Petter Selasky 	struct idr_layer *il;
2698d59ecb2SHans Petter Selasky 
2708d59ecb2SHans Petter Selasky 	il = idr->free;
2718d59ecb2SHans Petter Selasky 	if (il) {
2728d59ecb2SHans Petter Selasky 		idr->free = il->ary[0];
2738d59ecb2SHans Petter Selasky 		il->ary[0] = NULL;
2748d59ecb2SHans Petter Selasky 		return (il);
2758d59ecb2SHans Petter Selasky 	}
2768d59ecb2SHans Petter Selasky 	il = malloc(sizeof(*il), M_IDR, M_ZERO | M_NOWAIT);
2778d59ecb2SHans Petter Selasky 	bitmap_fill(&il->bitmap, IDR_SIZE);
2788d59ecb2SHans Petter Selasky 	return (il);
2798d59ecb2SHans Petter Selasky }
2808d59ecb2SHans Petter Selasky 
2818d59ecb2SHans Petter Selasky /*
2828d59ecb2SHans Petter Selasky  * Could be implemented as get_new_above(idr, ptr, 0, idp) but written
2838d59ecb2SHans Petter Selasky  * first for simplicity sake.
2848d59ecb2SHans Petter Selasky  */
2852d1bee65SHans Petter Selasky static int
2862d1bee65SHans Petter Selasky idr_get_new_locked(struct idr *idr, void *ptr, int *idp)
2878d59ecb2SHans Petter Selasky {
2888d59ecb2SHans Petter Selasky 	struct idr_layer *stack[MAX_LEVEL];
2898d59ecb2SHans Petter Selasky 	struct idr_layer *il;
2908d59ecb2SHans Petter Selasky 	int error;
2918d59ecb2SHans Petter Selasky 	int layer;
2928d59ecb2SHans Petter Selasky 	int idx;
2938d59ecb2SHans Petter Selasky 	int id;
2948d59ecb2SHans Petter Selasky 
2952d1bee65SHans Petter Selasky 	mtx_assert(&idr->lock, MA_OWNED);
2962d1bee65SHans Petter Selasky 
2978d59ecb2SHans Petter Selasky 	error = -EAGAIN;
2988d59ecb2SHans Petter Selasky 	/*
2998d59ecb2SHans Petter Selasky 	 * Expand the tree until there is free space.
3008d59ecb2SHans Petter Selasky 	 */
3018d59ecb2SHans Petter Selasky 	if (idr->top == NULL || idr->top->bitmap == 0) {
3028d59ecb2SHans Petter Selasky 		if (idr->layers == MAX_LEVEL + 1) {
3038d59ecb2SHans Petter Selasky 			error = -ENOSPC;
3048d59ecb2SHans Petter Selasky 			goto out;
3058d59ecb2SHans Petter Selasky 		}
3068d59ecb2SHans Petter Selasky 		il = idr_get(idr);
3078d59ecb2SHans Petter Selasky 		if (il == NULL)
3088d59ecb2SHans Petter Selasky 			goto out;
3098d59ecb2SHans Petter Selasky 		il->ary[0] = idr->top;
3108d59ecb2SHans Petter Selasky 		if (idr->top)
3118d59ecb2SHans Petter Selasky 			il->bitmap &= ~1;
3128d59ecb2SHans Petter Selasky 		idr->top = il;
3138d59ecb2SHans Petter Selasky 		idr->layers++;
3148d59ecb2SHans Petter Selasky 	}
3158d59ecb2SHans Petter Selasky 	il = idr->top;
3168d59ecb2SHans Petter Selasky 	id = 0;
3178d59ecb2SHans Petter Selasky 	/*
3188d59ecb2SHans Petter Selasky 	 * Walk the tree following free bitmaps, record our path.
3198d59ecb2SHans Petter Selasky 	 */
3208d59ecb2SHans Petter Selasky 	for (layer = idr->layers - 1;; layer--) {
3218d59ecb2SHans Petter Selasky 		stack[layer] = il;
3228d59ecb2SHans Petter Selasky 		idx = ffsl(il->bitmap);
3238d59ecb2SHans Petter Selasky 		if (idx == 0)
3248d59ecb2SHans Petter Selasky 			panic("idr_get_new: Invalid leaf state (%p, %p)\n",
3258d59ecb2SHans Petter Selasky 			    idr, il);
3268d59ecb2SHans Petter Selasky 		idx--;
3278d59ecb2SHans Petter Selasky 		id |= idx << (layer * IDR_BITS);
3288d59ecb2SHans Petter Selasky 		if (layer == 0)
3298d59ecb2SHans Petter Selasky 			break;
3308d59ecb2SHans Petter Selasky 		if (il->ary[idx] == NULL) {
3318d59ecb2SHans Petter Selasky 			il->ary[idx] = idr_get(idr);
3328d59ecb2SHans Petter Selasky 			if (il->ary[idx] == NULL)
3338d59ecb2SHans Petter Selasky 				goto out;
3348d59ecb2SHans Petter Selasky 		}
3358d59ecb2SHans Petter Selasky 		il = il->ary[idx];
3368d59ecb2SHans Petter Selasky 	}
3378d59ecb2SHans Petter Selasky 	/*
3388d59ecb2SHans Petter Selasky 	 * Allocate the leaf to the consumer.
3398d59ecb2SHans Petter Selasky 	 */
3408d59ecb2SHans Petter Selasky 	il->bitmap &= ~(1 << idx);
3418d59ecb2SHans Petter Selasky 	il->ary[idx] = ptr;
3428d59ecb2SHans Petter Selasky 	*idp = id;
3438d59ecb2SHans Petter Selasky 	/*
3448d59ecb2SHans Petter Selasky 	 * Clear bitmaps potentially up to the root.
3458d59ecb2SHans Petter Selasky 	 */
3468d59ecb2SHans Petter Selasky 	while (il->bitmap == 0 && ++layer < idr->layers) {
3478d59ecb2SHans Petter Selasky 		il = stack[layer];
3488d59ecb2SHans Petter Selasky 		il->bitmap &= ~(1 << idr_pos(id, layer));
3498d59ecb2SHans Petter Selasky 	}
3508d59ecb2SHans Petter Selasky 	error = 0;
3518d59ecb2SHans Petter Selasky out:
3528d59ecb2SHans Petter Selasky #ifdef INVARIANTS
3538d59ecb2SHans Petter Selasky 	if (error == 0 && idr_find_locked(idr, id) != ptr) {
3548d59ecb2SHans Petter Selasky 		panic("idr_get_new: Failed for idr %p, id %d, ptr %p\n",
3558d59ecb2SHans Petter Selasky 		    idr, id, ptr);
3568d59ecb2SHans Petter Selasky 	}
3578d59ecb2SHans Petter Selasky #endif
3588d59ecb2SHans Petter Selasky 	return (error);
3598d59ecb2SHans Petter Selasky }
3608d59ecb2SHans Petter Selasky 
3618d59ecb2SHans Petter Selasky int
3622d1bee65SHans Petter Selasky idr_get_new(struct idr *idr, void *ptr, int *idp)
3632d1bee65SHans Petter Selasky {
3642d1bee65SHans Petter Selasky 	int retval;
3652d1bee65SHans Petter Selasky 
3662d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
3672d1bee65SHans Petter Selasky 	retval = idr_get_new_locked(idr, ptr, idp);
3682d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
3692d1bee65SHans Petter Selasky 	return (retval);
3702d1bee65SHans Petter Selasky }
3712d1bee65SHans Petter Selasky 
3722d1bee65SHans Petter Selasky static int
3732d1bee65SHans Petter Selasky idr_get_new_above_locked(struct idr *idr, void *ptr, int starting_id, int *idp)
3748d59ecb2SHans Petter Selasky {
3758d59ecb2SHans Petter Selasky 	struct idr_layer *stack[MAX_LEVEL];
3768d59ecb2SHans Petter Selasky 	struct idr_layer *il;
3778d59ecb2SHans Petter Selasky 	int error;
3788d59ecb2SHans Petter Selasky 	int layer;
3798d59ecb2SHans Petter Selasky 	int idx, sidx;
3808d59ecb2SHans Petter Selasky 	int id;
3818d59ecb2SHans Petter Selasky 
3822d1bee65SHans Petter Selasky 	mtx_assert(&idr->lock, MA_OWNED);
3832d1bee65SHans Petter Selasky 
3848d59ecb2SHans Petter Selasky 	error = -EAGAIN;
3858d59ecb2SHans Petter Selasky 	/*
3868d59ecb2SHans Petter Selasky 	 * Compute the layers required to support starting_id and the mask
3878d59ecb2SHans Petter Selasky 	 * at the top layer.
3888d59ecb2SHans Petter Selasky 	 */
3898d59ecb2SHans Petter Selasky restart:
3908d59ecb2SHans Petter Selasky 	idx = starting_id;
3918d59ecb2SHans Petter Selasky 	layer = 0;
3928d59ecb2SHans Petter Selasky 	while (idx & ~IDR_MASK) {
3938d59ecb2SHans Petter Selasky 		layer++;
3948d59ecb2SHans Petter Selasky 		idx >>= IDR_BITS;
3958d59ecb2SHans Petter Selasky 	}
3968d59ecb2SHans Petter Selasky 	if (layer == MAX_LEVEL + 1) {
3978d59ecb2SHans Petter Selasky 		error = -ENOSPC;
3988d59ecb2SHans Petter Selasky 		goto out;
3998d59ecb2SHans Petter Selasky 	}
4008d59ecb2SHans Petter Selasky 	/*
4018d59ecb2SHans Petter Selasky 	 * Expand the tree until there is free space at or beyond starting_id.
4028d59ecb2SHans Petter Selasky 	 */
4038d59ecb2SHans Petter Selasky 	while (idr->layers <= layer ||
4048d59ecb2SHans Petter Selasky 	    idr->top->bitmap < (1 << idr_pos(starting_id, idr->layers - 1))) {
4058d59ecb2SHans Petter Selasky 		if (idr->layers == MAX_LEVEL + 1) {
4068d59ecb2SHans Petter Selasky 			error = -ENOSPC;
4078d59ecb2SHans Petter Selasky 			goto out;
4088d59ecb2SHans Petter Selasky 		}
4098d59ecb2SHans Petter Selasky 		il = idr_get(idr);
4108d59ecb2SHans Petter Selasky 		if (il == NULL)
4118d59ecb2SHans Petter Selasky 			goto out;
4128d59ecb2SHans Petter Selasky 		il->ary[0] = idr->top;
4138d59ecb2SHans Petter Selasky 		if (idr->top && idr->top->bitmap == 0)
4148d59ecb2SHans Petter Selasky 			il->bitmap &= ~1;
4158d59ecb2SHans Petter Selasky 		idr->top = il;
4168d59ecb2SHans Petter Selasky 		idr->layers++;
4178d59ecb2SHans Petter Selasky 	}
4188d59ecb2SHans Petter Selasky 	il = idr->top;
4198d59ecb2SHans Petter Selasky 	id = 0;
4208d59ecb2SHans Petter Selasky 	/*
4218d59ecb2SHans Petter Selasky 	 * Walk the tree following free bitmaps, record our path.
4228d59ecb2SHans Petter Selasky 	 */
4238d59ecb2SHans Petter Selasky 	for (layer = idr->layers - 1;; layer--) {
4248d59ecb2SHans Petter Selasky 		stack[layer] = il;
4258d59ecb2SHans Petter Selasky 		sidx = idr_pos(starting_id, layer);
4268d59ecb2SHans Petter Selasky 		/* Returns index numbered from 0 or size if none exists. */
4278d59ecb2SHans Petter Selasky 		idx = find_next_bit(&il->bitmap, IDR_SIZE, sidx);
4288d59ecb2SHans Petter Selasky 		if (idx == IDR_SIZE && sidx == 0)
4298d59ecb2SHans Petter Selasky 			panic("idr_get_new: Invalid leaf state (%p, %p)\n",
4308d59ecb2SHans Petter Selasky 			    idr, il);
4318d59ecb2SHans Petter Selasky 		/*
4328d59ecb2SHans Petter Selasky 		 * We may have walked a path where there was a free bit but
4338d59ecb2SHans Petter Selasky 		 * it was lower than what we wanted.  Restart the search with
4348d59ecb2SHans Petter Selasky 		 * a larger starting id.  id contains the progress we made so
4358d59ecb2SHans Petter Selasky 		 * far.  Search the leaf one above this level.  This may
4368d59ecb2SHans Petter Selasky 		 * restart as many as MAX_LEVEL times but that is expected
4378d59ecb2SHans Petter Selasky 		 * to be rare.
4388d59ecb2SHans Petter Selasky 		 */
4398d59ecb2SHans Petter Selasky 		if (idx == IDR_SIZE) {
4408d59ecb2SHans Petter Selasky 			starting_id = id + (1 << ((layer + 1) * IDR_BITS));
4418d59ecb2SHans Petter Selasky 			goto restart;
4428d59ecb2SHans Petter Selasky 		}
4438d59ecb2SHans Petter Selasky 		if (idx > sidx)
4448d59ecb2SHans Petter Selasky 			starting_id = 0;	/* Search the whole subtree. */
4458d59ecb2SHans Petter Selasky 		id |= idx << (layer * IDR_BITS);
4468d59ecb2SHans Petter Selasky 		if (layer == 0)
4478d59ecb2SHans Petter Selasky 			break;
4488d59ecb2SHans Petter Selasky 		if (il->ary[idx] == NULL) {
4498d59ecb2SHans Petter Selasky 			il->ary[idx] = idr_get(idr);
4508d59ecb2SHans Petter Selasky 			if (il->ary[idx] == NULL)
4518d59ecb2SHans Petter Selasky 				goto out;
4528d59ecb2SHans Petter Selasky 		}
4538d59ecb2SHans Petter Selasky 		il = il->ary[idx];
4548d59ecb2SHans Petter Selasky 	}
4558d59ecb2SHans Petter Selasky 	/*
4568d59ecb2SHans Petter Selasky 	 * Allocate the leaf to the consumer.
4578d59ecb2SHans Petter Selasky 	 */
4588d59ecb2SHans Petter Selasky 	il->bitmap &= ~(1 << idx);
4598d59ecb2SHans Petter Selasky 	il->ary[idx] = ptr;
4608d59ecb2SHans Petter Selasky 	*idp = id;
4618d59ecb2SHans Petter Selasky 	/*
4628d59ecb2SHans Petter Selasky 	 * Clear bitmaps potentially up to the root.
4638d59ecb2SHans Petter Selasky 	 */
4648d59ecb2SHans Petter Selasky 	while (il->bitmap == 0 && ++layer < idr->layers) {
4658d59ecb2SHans Petter Selasky 		il = stack[layer];
4668d59ecb2SHans Petter Selasky 		il->bitmap &= ~(1 << idr_pos(id, layer));
4678d59ecb2SHans Petter Selasky 	}
4688d59ecb2SHans Petter Selasky 	error = 0;
4698d59ecb2SHans Petter Selasky out:
4708d59ecb2SHans Petter Selasky #ifdef INVARIANTS
4718d59ecb2SHans Petter Selasky 	if (error == 0 && idr_find_locked(idr, id) != ptr) {
4728d59ecb2SHans Petter Selasky 		panic("idr_get_new_above: Failed for idr %p, id %d, ptr %p\n",
4738d59ecb2SHans Petter Selasky 		    idr, id, ptr);
4748d59ecb2SHans Petter Selasky 	}
4758d59ecb2SHans Petter Selasky #endif
4768d59ecb2SHans Petter Selasky 	return (error);
4778d59ecb2SHans Petter Selasky }
4782d1bee65SHans Petter Selasky 
4792d1bee65SHans Petter Selasky int
4802d1bee65SHans Petter Selasky idr_get_new_above(struct idr *idr, void *ptr, int starting_id, int *idp)
4812d1bee65SHans Petter Selasky {
4822d1bee65SHans Petter Selasky 	int retval;
4832d1bee65SHans Petter Selasky 
4842d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
4852d1bee65SHans Petter Selasky 	retval = idr_get_new_above_locked(idr, ptr, starting_id, idp);
4862d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
4872d1bee65SHans Petter Selasky 	return (retval);
4882d1bee65SHans Petter Selasky }
4892d1bee65SHans Petter Selasky 
4902d1bee65SHans Petter Selasky static int
4912d1bee65SHans Petter Selasky idr_alloc_locked(struct idr *idr, void *ptr, int start, int end)
4922d1bee65SHans Petter Selasky {
4932d1bee65SHans Petter Selasky 	int max = end > 0 ? end - 1 : INT_MAX;
4942d1bee65SHans Petter Selasky 	int error;
4952d1bee65SHans Petter Selasky 	int id;
4962d1bee65SHans Petter Selasky 
4972d1bee65SHans Petter Selasky 	mtx_assert(&idr->lock, MA_OWNED);
4982d1bee65SHans Petter Selasky 
4992d1bee65SHans Petter Selasky 	if (unlikely(start < 0))
5002d1bee65SHans Petter Selasky 		return (-EINVAL);
5012d1bee65SHans Petter Selasky 	if (unlikely(max < start))
5022d1bee65SHans Petter Selasky 		return (-ENOSPC);
5032d1bee65SHans Petter Selasky 
5042d1bee65SHans Petter Selasky 	if (start == 0)
5052d1bee65SHans Petter Selasky 		error = idr_get_new_locked(idr, ptr, &id);
5062d1bee65SHans Petter Selasky 	else
5072d1bee65SHans Petter Selasky 		error = idr_get_new_above_locked(idr, ptr, start, &id);
5082d1bee65SHans Petter Selasky 
5092d1bee65SHans Petter Selasky 	if (unlikely(error < 0))
5102d1bee65SHans Petter Selasky 		return (error);
5112d1bee65SHans Petter Selasky 	if (unlikely(id > max)) {
5122d1bee65SHans Petter Selasky 		idr_remove_locked(idr, id);
5132d1bee65SHans Petter Selasky 		return (-ENOSPC);
5142d1bee65SHans Petter Selasky 	}
5152d1bee65SHans Petter Selasky 	return (id);
5162d1bee65SHans Petter Selasky }
5172d1bee65SHans Petter Selasky 
5182d1bee65SHans Petter Selasky int
5192d1bee65SHans Petter Selasky idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask)
5202d1bee65SHans Petter Selasky {
5212d1bee65SHans Petter Selasky 	int retval;
5222d1bee65SHans Petter Selasky 
5232d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
5242d1bee65SHans Petter Selasky 	retval = idr_alloc_locked(idr, ptr, start, end);
5252d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
5262d1bee65SHans Petter Selasky 	return (retval);
5272d1bee65SHans Petter Selasky }
5282d1bee65SHans Petter Selasky 
5292d1bee65SHans Petter Selasky int
5302d1bee65SHans Petter Selasky idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask)
5312d1bee65SHans Petter Selasky {
5322d1bee65SHans Petter Selasky 	int retval;
5332d1bee65SHans Petter Selasky 
5342d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
5352d1bee65SHans Petter Selasky 	retval = idr_alloc_locked(idr, ptr, max(start, idr->next_cyclic_id), end);
5362d1bee65SHans Petter Selasky 	if (unlikely(retval == -ENOSPC))
5372d1bee65SHans Petter Selasky 		retval = idr_alloc_locked(idr, ptr, start, end);
5382d1bee65SHans Petter Selasky 	if (likely(retval >= 0))
5392d1bee65SHans Petter Selasky 		idr->next_cyclic_id = retval + 1;
5402d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
5412d1bee65SHans Petter Selasky 	return (retval);
5422d1bee65SHans Petter Selasky }
543