xref: /freebsd/sys/compat/linuxkpi/common/src/linux_idr.c (revision 69d6653ba4d2f5b26c3a9dee9978d34f1a86aad4)
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.
5427cefdeSHans Petter Selasky  * Copyright (c) 2013-2017 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 
43c9dd0b48SHans Petter Selasky #include <linux/bitmap.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 
49427cefdeSHans Petter Selasky #define	MAX_IDR_LEVEL	((MAX_IDR_SHIFT + IDR_BITS - 1) / IDR_BITS)
50427cefdeSHans Petter Selasky #define	MAX_IDR_FREE	(MAX_IDR_LEVEL * 2)
51427cefdeSHans Petter Selasky 
52427cefdeSHans Petter Selasky struct linux_idr_cache {
53427cefdeSHans Petter Selasky 	spinlock_t lock;
54427cefdeSHans Petter Selasky 	struct idr_layer *head;
55427cefdeSHans Petter Selasky 	unsigned count;
56427cefdeSHans Petter Selasky };
57427cefdeSHans Petter Selasky 
58427cefdeSHans Petter Selasky static DPCPU_DEFINE(struct linux_idr_cache, linux_idr_cache);
59427cefdeSHans Petter Selasky 
608d59ecb2SHans Petter Selasky /*
618d59ecb2SHans Petter Selasky  * IDR Implementation.
628d59ecb2SHans Petter Selasky  *
638d59ecb2SHans Petter Selasky  * This is quick and dirty and not as re-entrant as the linux version
648d59ecb2SHans Petter Selasky  * however it should be fairly fast.  It is basically a radix tree with
658d59ecb2SHans Petter Selasky  * a builtin bitmap for allocation.
668d59ecb2SHans Petter Selasky  */
678d59ecb2SHans Petter Selasky static MALLOC_DEFINE(M_IDR, "idr", "Linux IDR compat");
688d59ecb2SHans Petter Selasky 
69427cefdeSHans Petter Selasky static struct idr_layer *
70427cefdeSHans Petter Selasky idr_preload_dequeue_locked(struct linux_idr_cache *lic)
71427cefdeSHans Petter Selasky {
72427cefdeSHans Petter Selasky 	struct idr_layer *retval;
73427cefdeSHans Petter Selasky 
74427cefdeSHans Petter Selasky 	/* check if wrong thread is trying to dequeue */
75427cefdeSHans Petter Selasky 	if (mtx_owned(&lic->lock.m) == 0)
76427cefdeSHans Petter Selasky 		return (NULL);
77427cefdeSHans Petter Selasky 
78427cefdeSHans Petter Selasky 	retval = lic->head;
79427cefdeSHans Petter Selasky 	if (likely(retval != NULL)) {
80427cefdeSHans Petter Selasky 		lic->head = retval->ary[0];
81427cefdeSHans Petter Selasky 		lic->count--;
82427cefdeSHans Petter Selasky 		retval->ary[0] = NULL;
83427cefdeSHans Petter Selasky 	}
84427cefdeSHans Petter Selasky 	return (retval);
85427cefdeSHans Petter Selasky }
86427cefdeSHans Petter Selasky 
87427cefdeSHans Petter Selasky static void
88427cefdeSHans Petter Selasky idr_preload_init(void *arg)
89427cefdeSHans Petter Selasky {
90427cefdeSHans Petter Selasky 	int cpu;
91427cefdeSHans Petter Selasky 
92427cefdeSHans Petter Selasky 	CPU_FOREACH(cpu) {
93427cefdeSHans Petter Selasky 		struct linux_idr_cache *lic =
94427cefdeSHans Petter Selasky 		    DPCPU_ID_PTR(cpu, linux_idr_cache);
95427cefdeSHans Petter Selasky 
96427cefdeSHans Petter Selasky 		spin_lock_init(&lic->lock);
97427cefdeSHans Petter Selasky 	}
98427cefdeSHans Petter Selasky }
9925b3ef2cSHans Petter Selasky SYSINIT(idr_preload_init, SI_SUB_CPU, SI_ORDER_ANY, idr_preload_init, NULL);
100427cefdeSHans Petter Selasky 
101427cefdeSHans Petter Selasky static void
102427cefdeSHans Petter Selasky idr_preload_uninit(void *arg)
103427cefdeSHans Petter Selasky {
104427cefdeSHans Petter Selasky 	int cpu;
105427cefdeSHans Petter Selasky 
106427cefdeSHans Petter Selasky 	CPU_FOREACH(cpu) {
107427cefdeSHans Petter Selasky 		struct idr_layer *cacheval;
108427cefdeSHans Petter Selasky 		struct linux_idr_cache *lic =
109427cefdeSHans Petter Selasky 		    DPCPU_ID_PTR(cpu, linux_idr_cache);
110427cefdeSHans Petter Selasky 
111427cefdeSHans Petter Selasky 		while (1) {
112427cefdeSHans Petter Selasky 			spin_lock(&lic->lock);
113427cefdeSHans Petter Selasky 			cacheval = idr_preload_dequeue_locked(lic);
114427cefdeSHans Petter Selasky 			spin_unlock(&lic->lock);
115427cefdeSHans Petter Selasky 
116427cefdeSHans Petter Selasky 			if (cacheval == NULL)
117427cefdeSHans Petter Selasky 				break;
118427cefdeSHans Petter Selasky 			free(cacheval, M_IDR);
119427cefdeSHans Petter Selasky 		}
120427cefdeSHans Petter Selasky 		spin_lock_destroy(&lic->lock);
121427cefdeSHans Petter Selasky 	}
122427cefdeSHans Petter Selasky }
123427cefdeSHans Petter Selasky SYSUNINIT(idr_preload_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, idr_preload_uninit, NULL);
124427cefdeSHans Petter Selasky 
125427cefdeSHans Petter Selasky void
126427cefdeSHans Petter Selasky idr_preload(gfp_t gfp_mask)
127427cefdeSHans Petter Selasky {
128427cefdeSHans Petter Selasky 	struct linux_idr_cache *lic;
129427cefdeSHans Petter Selasky 	struct idr_layer *cacheval;
130427cefdeSHans Petter Selasky 
131427cefdeSHans Petter Selasky 	sched_pin();
132427cefdeSHans Petter Selasky 
133427cefdeSHans Petter Selasky 	lic = &DPCPU_GET(linux_idr_cache);
134427cefdeSHans Petter Selasky 
135427cefdeSHans Petter Selasky 	/* fill up cache */
136427cefdeSHans Petter Selasky 	spin_lock(&lic->lock);
137427cefdeSHans Petter Selasky 	while (lic->count < MAX_IDR_FREE) {
138427cefdeSHans Petter Selasky 		spin_unlock(&lic->lock);
139427cefdeSHans Petter Selasky 		cacheval = malloc(sizeof(*cacheval), M_IDR, M_ZERO | gfp_mask);
140427cefdeSHans Petter Selasky 		spin_lock(&lic->lock);
141427cefdeSHans Petter Selasky 		if (cacheval == NULL)
142427cefdeSHans Petter Selasky 			break;
143427cefdeSHans Petter Selasky 		cacheval->ary[0] = lic->head;
144427cefdeSHans Petter Selasky 		lic->head = cacheval;
145427cefdeSHans Petter Selasky 		lic->count++;
146427cefdeSHans Petter Selasky 	}
147427cefdeSHans Petter Selasky }
148427cefdeSHans Petter Selasky 
149427cefdeSHans Petter Selasky void
150427cefdeSHans Petter Selasky idr_preload_end(void)
151427cefdeSHans Petter Selasky {
152427cefdeSHans Petter Selasky 	struct linux_idr_cache *lic;
153427cefdeSHans Petter Selasky 
154427cefdeSHans Petter Selasky 	lic = &DPCPU_GET(linux_idr_cache);
155427cefdeSHans Petter Selasky 	spin_unlock(&lic->lock);
156427cefdeSHans Petter Selasky 	sched_unpin();
157427cefdeSHans Petter Selasky }
158427cefdeSHans Petter Selasky 
1598d59ecb2SHans Petter Selasky static inline int
1608d59ecb2SHans Petter Selasky idr_max(struct idr *idr)
1618d59ecb2SHans Petter Selasky {
1628d59ecb2SHans Petter Selasky 	return (1 << (idr->layers * IDR_BITS)) - 1;
1638d59ecb2SHans Petter Selasky }
1648d59ecb2SHans Petter Selasky 
1658d59ecb2SHans Petter Selasky static inline int
1668d59ecb2SHans Petter Selasky idr_pos(int id, int layer)
1678d59ecb2SHans Petter Selasky {
1688d59ecb2SHans Petter Selasky 	return (id >> (IDR_BITS * layer)) & IDR_MASK;
1698d59ecb2SHans Petter Selasky }
1708d59ecb2SHans Petter Selasky 
1718d59ecb2SHans Petter Selasky void
1728d59ecb2SHans Petter Selasky idr_init(struct idr *idr)
1738d59ecb2SHans Petter Selasky {
1748d59ecb2SHans Petter Selasky 	bzero(idr, sizeof(*idr));
1758d59ecb2SHans Petter Selasky 	mtx_init(&idr->lock, "idr", NULL, MTX_DEF);
1768d59ecb2SHans Petter Selasky }
1778d59ecb2SHans Petter Selasky 
1788d59ecb2SHans Petter Selasky /* Only frees cached pages. */
1798d59ecb2SHans Petter Selasky void
1808d59ecb2SHans Petter Selasky idr_destroy(struct idr *idr)
1818d59ecb2SHans Petter Selasky {
1828d59ecb2SHans Petter Selasky 	struct idr_layer *il, *iln;
1838d59ecb2SHans Petter Selasky 
1848d59ecb2SHans Petter Selasky 	idr_remove_all(idr);
1858d59ecb2SHans Petter Selasky 	mtx_lock(&idr->lock);
1868d59ecb2SHans Petter Selasky 	for (il = idr->free; il != NULL; il = iln) {
1878d59ecb2SHans Petter Selasky 		iln = il->ary[0];
1888d59ecb2SHans Petter Selasky 		free(il, M_IDR);
1898d59ecb2SHans Petter Selasky 	}
1908d59ecb2SHans Petter Selasky 	mtx_unlock(&idr->lock);
1915d35d777SHans Petter Selasky 	mtx_destroy(&idr->lock);
1928d59ecb2SHans Petter Selasky }
1938d59ecb2SHans Petter Selasky 
1948d59ecb2SHans Petter Selasky static void
1958d59ecb2SHans Petter Selasky idr_remove_layer(struct idr_layer *il, int layer)
1968d59ecb2SHans Petter Selasky {
1978d59ecb2SHans Petter Selasky 	int i;
1988d59ecb2SHans Petter Selasky 
1998d59ecb2SHans Petter Selasky 	if (il == NULL)
2008d59ecb2SHans Petter Selasky 		return;
2018d59ecb2SHans Petter Selasky 	if (layer == 0) {
2028d59ecb2SHans Petter Selasky 		free(il, M_IDR);
2038d59ecb2SHans Petter Selasky 		return;
2048d59ecb2SHans Petter Selasky 	}
2058d59ecb2SHans Petter Selasky 	for (i = 0; i < IDR_SIZE; i++)
2068d59ecb2SHans Petter Selasky 		if (il->ary[i])
2078d59ecb2SHans Petter Selasky 			idr_remove_layer(il->ary[i], layer - 1);
2088d59ecb2SHans Petter Selasky }
2098d59ecb2SHans Petter Selasky 
2108d59ecb2SHans Petter Selasky void
2118d59ecb2SHans Petter Selasky idr_remove_all(struct idr *idr)
2128d59ecb2SHans Petter Selasky {
2138d59ecb2SHans Petter Selasky 
2148d59ecb2SHans Petter Selasky 	mtx_lock(&idr->lock);
2158d59ecb2SHans Petter Selasky 	idr_remove_layer(idr->top, idr->layers - 1);
2168d59ecb2SHans Petter Selasky 	idr->top = NULL;
2178d59ecb2SHans Petter Selasky 	idr->layers = 0;
2188d59ecb2SHans Petter Selasky 	mtx_unlock(&idr->lock);
2198d59ecb2SHans Petter Selasky }
2208d59ecb2SHans Petter Selasky 
221*69d6653bSHans Petter Selasky static void *
2222d1bee65SHans Petter Selasky idr_remove_locked(struct idr *idr, int id)
2238d59ecb2SHans Petter Selasky {
2248d59ecb2SHans Petter Selasky 	struct idr_layer *il;
225*69d6653bSHans Petter Selasky 	void *res;
2268d59ecb2SHans Petter Selasky 	int layer;
2278d59ecb2SHans Petter Selasky 	int idx;
2288d59ecb2SHans Petter Selasky 
2298d59ecb2SHans Petter Selasky 	id &= MAX_ID_MASK;
2308d59ecb2SHans Petter Selasky 	il = idr->top;
2318d59ecb2SHans Petter Selasky 	layer = idr->layers - 1;
2322d1bee65SHans Petter Selasky 	if (il == NULL || id > idr_max(idr))
233*69d6653bSHans Petter Selasky 		return (NULL);
2348d59ecb2SHans Petter Selasky 	/*
2358d59ecb2SHans Petter Selasky 	 * Walk down the tree to this item setting bitmaps along the way
2368d59ecb2SHans Petter Selasky 	 * as we know at least one item will be free along this path.
2378d59ecb2SHans Petter Selasky 	 */
2388d59ecb2SHans Petter Selasky 	while (layer && il) {
2398d59ecb2SHans Petter Selasky 		idx = idr_pos(id, layer);
2408d59ecb2SHans Petter Selasky 		il->bitmap |= 1 << idx;
2418d59ecb2SHans Petter Selasky 		il = il->ary[idx];
2428d59ecb2SHans Petter Selasky 		layer--;
2438d59ecb2SHans Petter Selasky 	}
2448d59ecb2SHans Petter Selasky 	idx = id & IDR_MASK;
2458d59ecb2SHans Petter Selasky 	/*
2468d59ecb2SHans Petter Selasky 	 * At this point we've set free space bitmaps up the whole tree.
2478d59ecb2SHans Petter Selasky 	 * We could make this non-fatal and unwind but linux dumps a stack
2488d59ecb2SHans Petter Selasky 	 * and a warning so I don't think it's necessary.
2498d59ecb2SHans Petter Selasky 	 */
2508d59ecb2SHans Petter Selasky 	if (il == NULL || (il->bitmap & (1 << idx)) != 0)
2518d59ecb2SHans Petter Selasky 		panic("idr_remove: Item %d not allocated (%p, %p)\n",
2528d59ecb2SHans Petter Selasky 		    id, idr, il);
253*69d6653bSHans Petter Selasky 	res = il->ary[idx];
2548d59ecb2SHans Petter Selasky 	il->ary[idx] = NULL;
2558d59ecb2SHans Petter Selasky 	il->bitmap |= 1 << idx;
256*69d6653bSHans Petter Selasky 
257*69d6653bSHans Petter Selasky 	return (res);
2582d1bee65SHans Petter Selasky }
2592d1bee65SHans Petter Selasky 
260*69d6653bSHans Petter Selasky void *
2612d1bee65SHans Petter Selasky idr_remove(struct idr *idr, int id)
2622d1bee65SHans Petter Selasky {
263*69d6653bSHans Petter Selasky 	void *res;
264*69d6653bSHans Petter Selasky 
2652d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
266*69d6653bSHans Petter Selasky 	res = idr_remove_locked(idr, id);
2678d59ecb2SHans Petter Selasky 	mtx_unlock(&idr->lock);
268*69d6653bSHans Petter Selasky 
269*69d6653bSHans Petter Selasky 	return (res);
2708d59ecb2SHans Petter Selasky }
2718d59ecb2SHans Petter Selasky 
272f8221002SHans Petter Selasky 
273f8221002SHans Petter Selasky static inline struct idr_layer *
274f8221002SHans Petter Selasky idr_find_layer_locked(struct idr *idr, int id)
275f8221002SHans Petter Selasky {
276f8221002SHans Petter Selasky 	struct idr_layer *il;
277f8221002SHans Petter Selasky 	int layer;
278f8221002SHans Petter Selasky 
279f8221002SHans Petter Selasky 	id &= MAX_ID_MASK;
280f8221002SHans Petter Selasky 	il = idr->top;
281f8221002SHans Petter Selasky 	layer = idr->layers - 1;
282f8221002SHans Petter Selasky 	if (il == NULL || id > idr_max(idr))
283f8221002SHans Petter Selasky 		return (NULL);
284f8221002SHans Petter Selasky 	while (layer && il) {
285f8221002SHans Petter Selasky 		il = il->ary[idr_pos(id, layer)];
286f8221002SHans Petter Selasky 		layer--;
287f8221002SHans Petter Selasky 	}
288f8221002SHans Petter Selasky 	return (il);
289f8221002SHans Petter Selasky }
290f8221002SHans Petter Selasky 
2918d59ecb2SHans Petter Selasky void *
2928d59ecb2SHans Petter Selasky idr_replace(struct idr *idr, void *ptr, int id)
2938d59ecb2SHans Petter Selasky {
2948d59ecb2SHans Petter Selasky 	struct idr_layer *il;
2958d59ecb2SHans Petter Selasky 	void *res;
2968d59ecb2SHans Petter Selasky 	int idx;
2978d59ecb2SHans Petter Selasky 
2988d59ecb2SHans Petter Selasky 	mtx_lock(&idr->lock);
299f8221002SHans Petter Selasky 	il = idr_find_layer_locked(idr, id);
3008d59ecb2SHans Petter Selasky 	idx = id & IDR_MASK;
301f8221002SHans Petter Selasky 
302f8221002SHans Petter Selasky 	/* Replace still returns an error if the item was not allocated. */
303f8221002SHans Petter Selasky 	if (il == NULL || (il->bitmap & (1 << idx))) {
304f8221002SHans Petter Selasky 		res = ERR_PTR(-ENOENT);
305f8221002SHans Petter Selasky 	} else {
3068d59ecb2SHans Petter Selasky 		res = il->ary[idx];
3078d59ecb2SHans Petter Selasky 		il->ary[idx] = ptr;
3088d59ecb2SHans Petter Selasky 	}
3098d59ecb2SHans Petter Selasky 	mtx_unlock(&idr->lock);
3108d59ecb2SHans Petter Selasky 	return (res);
3118d59ecb2SHans Petter Selasky }
3128d59ecb2SHans Petter Selasky 
3138d59ecb2SHans Petter Selasky static inline void *
3148d59ecb2SHans Petter Selasky idr_find_locked(struct idr *idr, int id)
3158d59ecb2SHans Petter Selasky {
3168d59ecb2SHans Petter Selasky 	struct idr_layer *il;
3178d59ecb2SHans Petter Selasky 	void *res;
3188d59ecb2SHans Petter Selasky 
3198d59ecb2SHans Petter Selasky 	mtx_assert(&idr->lock, MA_OWNED);
320f8221002SHans Petter Selasky 	il = idr_find_layer_locked(idr, id);
3218d59ecb2SHans Petter Selasky 	if (il != NULL)
3228d59ecb2SHans Petter Selasky 		res = il->ary[id & IDR_MASK];
323f8221002SHans Petter Selasky 	else
324f8221002SHans Petter Selasky 		res = NULL;
3258d59ecb2SHans Petter Selasky 	return (res);
3268d59ecb2SHans Petter Selasky }
3278d59ecb2SHans Petter Selasky 
3288d59ecb2SHans Petter Selasky void *
3298d59ecb2SHans Petter Selasky idr_find(struct idr *idr, int id)
3308d59ecb2SHans Petter Selasky {
3318d59ecb2SHans Petter Selasky 	void *res;
3328d59ecb2SHans Petter Selasky 
3338d59ecb2SHans Petter Selasky 	mtx_lock(&idr->lock);
3348d59ecb2SHans Petter Selasky 	res = idr_find_locked(idr, id);
3358d59ecb2SHans Petter Selasky 	mtx_unlock(&idr->lock);
3368d59ecb2SHans Petter Selasky 	return (res);
3378d59ecb2SHans Petter Selasky }
3388d59ecb2SHans Petter Selasky 
339fd42d623SHans Petter Selasky void *
340fd42d623SHans Petter Selasky idr_get_next(struct idr *idr, int *nextidp)
341fd42d623SHans Petter Selasky {
342fd42d623SHans Petter Selasky 	void *res = NULL;
343fd42d623SHans Petter Selasky 	int id = *nextidp;
344fd42d623SHans Petter Selasky 
345fd42d623SHans Petter Selasky 	mtx_lock(&idr->lock);
346fd42d623SHans Petter Selasky 	for (; id <= idr_max(idr); id++) {
347fd42d623SHans Petter Selasky 		res = idr_find_locked(idr, id);
348fd42d623SHans Petter Selasky 		if (res == NULL)
349fd42d623SHans Petter Selasky 			continue;
350fd42d623SHans Petter Selasky 		*nextidp = id;
351fd42d623SHans Petter Selasky 		break;
352fd42d623SHans Petter Selasky 	}
353fd42d623SHans Petter Selasky 	mtx_unlock(&idr->lock);
354fd42d623SHans Petter Selasky 	return (res);
355fd42d623SHans Petter Selasky }
356fd42d623SHans Petter Selasky 
3578d59ecb2SHans Petter Selasky int
3588d59ecb2SHans Petter Selasky idr_pre_get(struct idr *idr, gfp_t gfp_mask)
3598d59ecb2SHans Petter Selasky {
3608d59ecb2SHans Petter Selasky 	struct idr_layer *il, *iln;
3618d59ecb2SHans Petter Selasky 	struct idr_layer *head;
3628d59ecb2SHans Petter Selasky 	int need;
3638d59ecb2SHans Petter Selasky 
3648d59ecb2SHans Petter Selasky 	mtx_lock(&idr->lock);
3658d59ecb2SHans Petter Selasky 	for (;;) {
3668d59ecb2SHans Petter Selasky 		need = idr->layers + 1;
3678d59ecb2SHans Petter Selasky 		for (il = idr->free; il != NULL; il = il->ary[0])
3688d59ecb2SHans Petter Selasky 			need--;
3698d59ecb2SHans Petter Selasky 		mtx_unlock(&idr->lock);
3708d59ecb2SHans Petter Selasky 		if (need <= 0)
3718d59ecb2SHans Petter Selasky 			break;
3728d59ecb2SHans Petter Selasky 		for (head = NULL; need; need--) {
3738d59ecb2SHans Petter Selasky 			iln = malloc(sizeof(*il), M_IDR, M_ZERO | gfp_mask);
3748d59ecb2SHans Petter Selasky 			if (iln == NULL)
3758d59ecb2SHans Petter Selasky 				break;
3768d59ecb2SHans Petter Selasky 			bitmap_fill(&iln->bitmap, IDR_SIZE);
3778d59ecb2SHans Petter Selasky 			if (head != NULL) {
3788d59ecb2SHans Petter Selasky 				il->ary[0] = iln;
3798d59ecb2SHans Petter Selasky 				il = iln;
3808d59ecb2SHans Petter Selasky 			} else
3818d59ecb2SHans Petter Selasky 				head = il = iln;
3828d59ecb2SHans Petter Selasky 		}
3838d59ecb2SHans Petter Selasky 		if (head == NULL)
3848d59ecb2SHans Petter Selasky 			return (0);
3858d59ecb2SHans Petter Selasky 		mtx_lock(&idr->lock);
3868d59ecb2SHans Petter Selasky 		il->ary[0] = idr->free;
3878d59ecb2SHans Petter Selasky 		idr->free = head;
3888d59ecb2SHans Petter Selasky 	}
3898d59ecb2SHans Petter Selasky 	return (1);
3908d59ecb2SHans Petter Selasky }
3918d59ecb2SHans Petter Selasky 
392427cefdeSHans Petter Selasky static struct idr_layer *
393427cefdeSHans Petter Selasky idr_free_list_get(struct idr *idp)
3948d59ecb2SHans Petter Selasky {
3958d59ecb2SHans Petter Selasky 	struct idr_layer *il;
3968d59ecb2SHans Petter Selasky 
397427cefdeSHans Petter Selasky 	if ((il = idp->free) != NULL) {
398427cefdeSHans Petter Selasky 		idp->free = il->ary[0];
3998d59ecb2SHans Petter Selasky 		il->ary[0] = NULL;
400427cefdeSHans Petter Selasky 	}
4018d59ecb2SHans Petter Selasky 	return (il);
4028d59ecb2SHans Petter Selasky }
403427cefdeSHans Petter Selasky 
404427cefdeSHans Petter Selasky static inline struct idr_layer *
405427cefdeSHans Petter Selasky idr_get(struct idr *idp)
406427cefdeSHans Petter Selasky {
407427cefdeSHans Petter Selasky 	struct idr_layer *il;
408427cefdeSHans Petter Selasky 
409427cefdeSHans Petter Selasky 	if ((il = idr_free_list_get(idp)) != NULL) {
410427cefdeSHans Petter Selasky 		MPASS(il->bitmap != 0);
411427cefdeSHans Petter Selasky 	} else if ((il = malloc(sizeof(*il), M_IDR, M_ZERO | M_NOWAIT)) != NULL) {
4128d59ecb2SHans Petter Selasky 		bitmap_fill(&il->bitmap, IDR_SIZE);
413427cefdeSHans Petter Selasky 	} else if ((il = idr_preload_dequeue_locked(&DPCPU_GET(linux_idr_cache))) != NULL) {
414427cefdeSHans Petter Selasky 		bitmap_fill(&il->bitmap, IDR_SIZE);
415427cefdeSHans Petter Selasky 	} else {
416427cefdeSHans Petter Selasky 		return (NULL);
417427cefdeSHans Petter Selasky 	}
4188d59ecb2SHans Petter Selasky 	return (il);
4198d59ecb2SHans Petter Selasky }
4208d59ecb2SHans Petter Selasky 
4218d59ecb2SHans Petter Selasky /*
4228d59ecb2SHans Petter Selasky  * Could be implemented as get_new_above(idr, ptr, 0, idp) but written
4238d59ecb2SHans Petter Selasky  * first for simplicity sake.
4248d59ecb2SHans Petter Selasky  */
4252d1bee65SHans Petter Selasky static int
4262d1bee65SHans Petter Selasky idr_get_new_locked(struct idr *idr, void *ptr, int *idp)
4278d59ecb2SHans Petter Selasky {
4288d59ecb2SHans Petter Selasky 	struct idr_layer *stack[MAX_LEVEL];
4298d59ecb2SHans Petter Selasky 	struct idr_layer *il;
4308d59ecb2SHans Petter Selasky 	int error;
4318d59ecb2SHans Petter Selasky 	int layer;
4328d59ecb2SHans Petter Selasky 	int idx;
4338d59ecb2SHans Petter Selasky 	int id;
4348d59ecb2SHans Petter Selasky 
4352d1bee65SHans Petter Selasky 	mtx_assert(&idr->lock, MA_OWNED);
4362d1bee65SHans Petter Selasky 
4378d59ecb2SHans Petter Selasky 	error = -EAGAIN;
4388d59ecb2SHans Petter Selasky 	/*
4398d59ecb2SHans Petter Selasky 	 * Expand the tree until there is free space.
4408d59ecb2SHans Petter Selasky 	 */
4418d59ecb2SHans Petter Selasky 	if (idr->top == NULL || idr->top->bitmap == 0) {
4428d59ecb2SHans Petter Selasky 		if (idr->layers == MAX_LEVEL + 1) {
4438d59ecb2SHans Petter Selasky 			error = -ENOSPC;
4448d59ecb2SHans Petter Selasky 			goto out;
4458d59ecb2SHans Petter Selasky 		}
4468d59ecb2SHans Petter Selasky 		il = idr_get(idr);
4478d59ecb2SHans Petter Selasky 		if (il == NULL)
4488d59ecb2SHans Petter Selasky 			goto out;
4498d59ecb2SHans Petter Selasky 		il->ary[0] = idr->top;
4508d59ecb2SHans Petter Selasky 		if (idr->top)
4518d59ecb2SHans Petter Selasky 			il->bitmap &= ~1;
4528d59ecb2SHans Petter Selasky 		idr->top = il;
4538d59ecb2SHans Petter Selasky 		idr->layers++;
4548d59ecb2SHans Petter Selasky 	}
4558d59ecb2SHans Petter Selasky 	il = idr->top;
4568d59ecb2SHans Petter Selasky 	id = 0;
4578d59ecb2SHans Petter Selasky 	/*
4588d59ecb2SHans Petter Selasky 	 * Walk the tree following free bitmaps, record our path.
4598d59ecb2SHans Petter Selasky 	 */
4608d59ecb2SHans Petter Selasky 	for (layer = idr->layers - 1;; layer--) {
4618d59ecb2SHans Petter Selasky 		stack[layer] = il;
4628d59ecb2SHans Petter Selasky 		idx = ffsl(il->bitmap);
4638d59ecb2SHans Petter Selasky 		if (idx == 0)
4648d59ecb2SHans Petter Selasky 			panic("idr_get_new: Invalid leaf state (%p, %p)\n",
4658d59ecb2SHans Petter Selasky 			    idr, il);
4668d59ecb2SHans Petter Selasky 		idx--;
4678d59ecb2SHans Petter Selasky 		id |= idx << (layer * IDR_BITS);
4688d59ecb2SHans Petter Selasky 		if (layer == 0)
4698d59ecb2SHans Petter Selasky 			break;
4708d59ecb2SHans Petter Selasky 		if (il->ary[idx] == NULL) {
4718d59ecb2SHans Petter Selasky 			il->ary[idx] = idr_get(idr);
4728d59ecb2SHans Petter Selasky 			if (il->ary[idx] == NULL)
4738d59ecb2SHans Petter Selasky 				goto out;
4748d59ecb2SHans Petter Selasky 		}
4758d59ecb2SHans Petter Selasky 		il = il->ary[idx];
4768d59ecb2SHans Petter Selasky 	}
4778d59ecb2SHans Petter Selasky 	/*
4788d59ecb2SHans Petter Selasky 	 * Allocate the leaf to the consumer.
4798d59ecb2SHans Petter Selasky 	 */
4808d59ecb2SHans Petter Selasky 	il->bitmap &= ~(1 << idx);
4818d59ecb2SHans Petter Selasky 	il->ary[idx] = ptr;
4828d59ecb2SHans Petter Selasky 	*idp = id;
4838d59ecb2SHans Petter Selasky 	/*
4848d59ecb2SHans Petter Selasky 	 * Clear bitmaps potentially up to the root.
4858d59ecb2SHans Petter Selasky 	 */
4868d59ecb2SHans Petter Selasky 	while (il->bitmap == 0 && ++layer < idr->layers) {
4878d59ecb2SHans Petter Selasky 		il = stack[layer];
4888d59ecb2SHans Petter Selasky 		il->bitmap &= ~(1 << idr_pos(id, layer));
4898d59ecb2SHans Petter Selasky 	}
4908d59ecb2SHans Petter Selasky 	error = 0;
4918d59ecb2SHans Petter Selasky out:
4928d59ecb2SHans Petter Selasky #ifdef INVARIANTS
4938d59ecb2SHans Petter Selasky 	if (error == 0 && idr_find_locked(idr, id) != ptr) {
4948d59ecb2SHans Petter Selasky 		panic("idr_get_new: Failed for idr %p, id %d, ptr %p\n",
4958d59ecb2SHans Petter Selasky 		    idr, id, ptr);
4968d59ecb2SHans Petter Selasky 	}
4978d59ecb2SHans Petter Selasky #endif
4988d59ecb2SHans Petter Selasky 	return (error);
4998d59ecb2SHans Petter Selasky }
5008d59ecb2SHans Petter Selasky 
5018d59ecb2SHans Petter Selasky int
5022d1bee65SHans Petter Selasky idr_get_new(struct idr *idr, void *ptr, int *idp)
5032d1bee65SHans Petter Selasky {
5042d1bee65SHans Petter Selasky 	int retval;
5052d1bee65SHans Petter Selasky 
5062d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
5072d1bee65SHans Petter Selasky 	retval = idr_get_new_locked(idr, ptr, idp);
5082d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
5092d1bee65SHans Petter Selasky 	return (retval);
5102d1bee65SHans Petter Selasky }
5112d1bee65SHans Petter Selasky 
5122d1bee65SHans Petter Selasky static int
5132d1bee65SHans Petter Selasky idr_get_new_above_locked(struct idr *idr, void *ptr, int starting_id, int *idp)
5148d59ecb2SHans Petter Selasky {
5158d59ecb2SHans Petter Selasky 	struct idr_layer *stack[MAX_LEVEL];
5168d59ecb2SHans Petter Selasky 	struct idr_layer *il;
5178d59ecb2SHans Petter Selasky 	int error;
5188d59ecb2SHans Petter Selasky 	int layer;
5198d59ecb2SHans Petter Selasky 	int idx, sidx;
5208d59ecb2SHans Petter Selasky 	int id;
5218d59ecb2SHans Petter Selasky 
5222d1bee65SHans Petter Selasky 	mtx_assert(&idr->lock, MA_OWNED);
5232d1bee65SHans Petter Selasky 
5248d59ecb2SHans Petter Selasky 	error = -EAGAIN;
5258d59ecb2SHans Petter Selasky 	/*
5268d59ecb2SHans Petter Selasky 	 * Compute the layers required to support starting_id and the mask
5278d59ecb2SHans Petter Selasky 	 * at the top layer.
5288d59ecb2SHans Petter Selasky 	 */
5298d59ecb2SHans Petter Selasky restart:
5308d59ecb2SHans Petter Selasky 	idx = starting_id;
5318d59ecb2SHans Petter Selasky 	layer = 0;
5328d59ecb2SHans Petter Selasky 	while (idx & ~IDR_MASK) {
5338d59ecb2SHans Petter Selasky 		layer++;
5348d59ecb2SHans Petter Selasky 		idx >>= IDR_BITS;
5358d59ecb2SHans Petter Selasky 	}
5368d59ecb2SHans Petter Selasky 	if (layer == MAX_LEVEL + 1) {
5378d59ecb2SHans Petter Selasky 		error = -ENOSPC;
5388d59ecb2SHans Petter Selasky 		goto out;
5398d59ecb2SHans Petter Selasky 	}
5408d59ecb2SHans Petter Selasky 	/*
5418d59ecb2SHans Petter Selasky 	 * Expand the tree until there is free space at or beyond starting_id.
5428d59ecb2SHans Petter Selasky 	 */
5438d59ecb2SHans Petter Selasky 	while (idr->layers <= layer ||
5448d59ecb2SHans Petter Selasky 	    idr->top->bitmap < (1 << idr_pos(starting_id, idr->layers - 1))) {
5458d59ecb2SHans Petter Selasky 		if (idr->layers == MAX_LEVEL + 1) {
5468d59ecb2SHans Petter Selasky 			error = -ENOSPC;
5478d59ecb2SHans Petter Selasky 			goto out;
5488d59ecb2SHans Petter Selasky 		}
5498d59ecb2SHans Petter Selasky 		il = idr_get(idr);
5508d59ecb2SHans Petter Selasky 		if (il == NULL)
5518d59ecb2SHans Petter Selasky 			goto out;
5528d59ecb2SHans Petter Selasky 		il->ary[0] = idr->top;
5538d59ecb2SHans Petter Selasky 		if (idr->top && idr->top->bitmap == 0)
5548d59ecb2SHans Petter Selasky 			il->bitmap &= ~1;
5558d59ecb2SHans Petter Selasky 		idr->top = il;
5568d59ecb2SHans Petter Selasky 		idr->layers++;
5578d59ecb2SHans Petter Selasky 	}
5588d59ecb2SHans Petter Selasky 	il = idr->top;
5598d59ecb2SHans Petter Selasky 	id = 0;
5608d59ecb2SHans Petter Selasky 	/*
5618d59ecb2SHans Petter Selasky 	 * Walk the tree following free bitmaps, record our path.
5628d59ecb2SHans Petter Selasky 	 */
5638d59ecb2SHans Petter Selasky 	for (layer = idr->layers - 1;; layer--) {
5648d59ecb2SHans Petter Selasky 		stack[layer] = il;
5658d59ecb2SHans Petter Selasky 		sidx = idr_pos(starting_id, layer);
5668d59ecb2SHans Petter Selasky 		/* Returns index numbered from 0 or size if none exists. */
5678d59ecb2SHans Petter Selasky 		idx = find_next_bit(&il->bitmap, IDR_SIZE, sidx);
5688d59ecb2SHans Petter Selasky 		if (idx == IDR_SIZE && sidx == 0)
5698d59ecb2SHans Petter Selasky 			panic("idr_get_new: Invalid leaf state (%p, %p)\n",
5708d59ecb2SHans Petter Selasky 			    idr, il);
5718d59ecb2SHans Petter Selasky 		/*
5728d59ecb2SHans Petter Selasky 		 * We may have walked a path where there was a free bit but
5738d59ecb2SHans Petter Selasky 		 * it was lower than what we wanted.  Restart the search with
5748d59ecb2SHans Petter Selasky 		 * a larger starting id.  id contains the progress we made so
5758d59ecb2SHans Petter Selasky 		 * far.  Search the leaf one above this level.  This may
5768d59ecb2SHans Petter Selasky 		 * restart as many as MAX_LEVEL times but that is expected
5778d59ecb2SHans Petter Selasky 		 * to be rare.
5788d59ecb2SHans Petter Selasky 		 */
5798d59ecb2SHans Petter Selasky 		if (idx == IDR_SIZE) {
5808d59ecb2SHans Petter Selasky 			starting_id = id + (1 << ((layer + 1) * IDR_BITS));
5818d59ecb2SHans Petter Selasky 			goto restart;
5828d59ecb2SHans Petter Selasky 		}
5838d59ecb2SHans Petter Selasky 		if (idx > sidx)
5848d59ecb2SHans Petter Selasky 			starting_id = 0;	/* Search the whole subtree. */
5858d59ecb2SHans Petter Selasky 		id |= idx << (layer * IDR_BITS);
5868d59ecb2SHans Petter Selasky 		if (layer == 0)
5878d59ecb2SHans Petter Selasky 			break;
5888d59ecb2SHans Petter Selasky 		if (il->ary[idx] == NULL) {
5898d59ecb2SHans Petter Selasky 			il->ary[idx] = idr_get(idr);
5908d59ecb2SHans Petter Selasky 			if (il->ary[idx] == NULL)
5918d59ecb2SHans Petter Selasky 				goto out;
5928d59ecb2SHans Petter Selasky 		}
5938d59ecb2SHans Petter Selasky 		il = il->ary[idx];
5948d59ecb2SHans Petter Selasky 	}
5958d59ecb2SHans Petter Selasky 	/*
5968d59ecb2SHans Petter Selasky 	 * Allocate the leaf to the consumer.
5978d59ecb2SHans Petter Selasky 	 */
5988d59ecb2SHans Petter Selasky 	il->bitmap &= ~(1 << idx);
5998d59ecb2SHans Petter Selasky 	il->ary[idx] = ptr;
6008d59ecb2SHans Petter Selasky 	*idp = id;
6018d59ecb2SHans Petter Selasky 	/*
6028d59ecb2SHans Petter Selasky 	 * Clear bitmaps potentially up to the root.
6038d59ecb2SHans Petter Selasky 	 */
6048d59ecb2SHans Petter Selasky 	while (il->bitmap == 0 && ++layer < idr->layers) {
6058d59ecb2SHans Petter Selasky 		il = stack[layer];
6068d59ecb2SHans Petter Selasky 		il->bitmap &= ~(1 << idr_pos(id, layer));
6078d59ecb2SHans Petter Selasky 	}
6088d59ecb2SHans Petter Selasky 	error = 0;
6098d59ecb2SHans Petter Selasky out:
6108d59ecb2SHans Petter Selasky #ifdef INVARIANTS
6118d59ecb2SHans Petter Selasky 	if (error == 0 && idr_find_locked(idr, id) != ptr) {
6128d59ecb2SHans Petter Selasky 		panic("idr_get_new_above: Failed for idr %p, id %d, ptr %p\n",
6138d59ecb2SHans Petter Selasky 		    idr, id, ptr);
6148d59ecb2SHans Petter Selasky 	}
6158d59ecb2SHans Petter Selasky #endif
6168d59ecb2SHans Petter Selasky 	return (error);
6178d59ecb2SHans Petter Selasky }
6182d1bee65SHans Petter Selasky 
6192d1bee65SHans Petter Selasky int
6202d1bee65SHans Petter Selasky idr_get_new_above(struct idr *idr, void *ptr, int starting_id, int *idp)
6212d1bee65SHans Petter Selasky {
6222d1bee65SHans Petter Selasky 	int retval;
6232d1bee65SHans Petter Selasky 
6242d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
6252d1bee65SHans Petter Selasky 	retval = idr_get_new_above_locked(idr, ptr, starting_id, idp);
6262d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
6272d1bee65SHans Petter Selasky 	return (retval);
6282d1bee65SHans Petter Selasky }
6292d1bee65SHans Petter Selasky 
630fd42d623SHans Petter Selasky int
631fd42d623SHans Petter Selasky ida_get_new_above(struct ida *ida, int starting_id, int *p_id)
632fd42d623SHans Petter Selasky {
633fd42d623SHans Petter Selasky 	return (idr_get_new_above(&ida->idr, NULL, starting_id, p_id));
634fd42d623SHans Petter Selasky }
635fd42d623SHans Petter Selasky 
6362d1bee65SHans Petter Selasky static int
6372d1bee65SHans Petter Selasky idr_alloc_locked(struct idr *idr, void *ptr, int start, int end)
6382d1bee65SHans Petter Selasky {
6392d1bee65SHans Petter Selasky 	int max = end > 0 ? end - 1 : INT_MAX;
6402d1bee65SHans Petter Selasky 	int error;
6412d1bee65SHans Petter Selasky 	int id;
6422d1bee65SHans Petter Selasky 
6432d1bee65SHans Petter Selasky 	mtx_assert(&idr->lock, MA_OWNED);
6442d1bee65SHans Petter Selasky 
6452d1bee65SHans Petter Selasky 	if (unlikely(start < 0))
6462d1bee65SHans Petter Selasky 		return (-EINVAL);
6472d1bee65SHans Petter Selasky 	if (unlikely(max < start))
6482d1bee65SHans Petter Selasky 		return (-ENOSPC);
6492d1bee65SHans Petter Selasky 
6502d1bee65SHans Petter Selasky 	if (start == 0)
6512d1bee65SHans Petter Selasky 		error = idr_get_new_locked(idr, ptr, &id);
6522d1bee65SHans Petter Selasky 	else
6532d1bee65SHans Petter Selasky 		error = idr_get_new_above_locked(idr, ptr, start, &id);
6542d1bee65SHans Petter Selasky 
6552d1bee65SHans Petter Selasky 	if (unlikely(error < 0))
6562d1bee65SHans Petter Selasky 		return (error);
6572d1bee65SHans Petter Selasky 	if (unlikely(id > max)) {
6582d1bee65SHans Petter Selasky 		idr_remove_locked(idr, id);
6592d1bee65SHans Petter Selasky 		return (-ENOSPC);
6602d1bee65SHans Petter Selasky 	}
6612d1bee65SHans Petter Selasky 	return (id);
6622d1bee65SHans Petter Selasky }
6632d1bee65SHans Petter Selasky 
6642d1bee65SHans Petter Selasky int
6652d1bee65SHans Petter Selasky idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask)
6662d1bee65SHans Petter Selasky {
6672d1bee65SHans Petter Selasky 	int retval;
6682d1bee65SHans Petter Selasky 
6692d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
6702d1bee65SHans Petter Selasky 	retval = idr_alloc_locked(idr, ptr, start, end);
6712d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
6722d1bee65SHans Petter Selasky 	return (retval);
6732d1bee65SHans Petter Selasky }
6742d1bee65SHans Petter Selasky 
6752d1bee65SHans Petter Selasky int
6762d1bee65SHans Petter Selasky idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask)
6772d1bee65SHans Petter Selasky {
6782d1bee65SHans Petter Selasky 	int retval;
6792d1bee65SHans Petter Selasky 
6802d1bee65SHans Petter Selasky 	mtx_lock(&idr->lock);
6812d1bee65SHans Petter Selasky 	retval = idr_alloc_locked(idr, ptr, max(start, idr->next_cyclic_id), end);
6822d1bee65SHans Petter Selasky 	if (unlikely(retval == -ENOSPC))
6832d1bee65SHans Petter Selasky 		retval = idr_alloc_locked(idr, ptr, start, end);
6842d1bee65SHans Petter Selasky 	if (likely(retval >= 0))
6852d1bee65SHans Petter Selasky 		idr->next_cyclic_id = retval + 1;
6862d1bee65SHans Petter Selasky 	mtx_unlock(&idr->lock);
6872d1bee65SHans Petter Selasky 	return (retval);
6882d1bee65SHans Petter Selasky }
689fd42d623SHans Petter Selasky 
690fd42d623SHans Petter Selasky static int
691f885420dSHans Petter Selasky idr_for_each_layer(struct idr_layer *il, int offset, int layer,
692fd42d623SHans Petter Selasky     int (*f)(int id, void *p, void *data), void *data)
693fd42d623SHans Petter Selasky {
694fd42d623SHans Petter Selasky 	int i, err;
695fd42d623SHans Petter Selasky 
696fd42d623SHans Petter Selasky 	if (il == NULL)
697fd42d623SHans Petter Selasky 		return (0);
698fd42d623SHans Petter Selasky 	if (layer == 0) {
699fd42d623SHans Petter Selasky 		for (i = 0; i < IDR_SIZE; i++) {
700fd42d623SHans Petter Selasky 			if (il->ary[i] == NULL)
701fd42d623SHans Petter Selasky 				continue;
702f885420dSHans Petter Selasky 			err = f(i + offset, il->ary[i],  data);
703fd42d623SHans Petter Selasky 			if (err)
704fd42d623SHans Petter Selasky 				return (err);
705fd42d623SHans Petter Selasky 		}
706fd42d623SHans Petter Selasky 		return (0);
707fd42d623SHans Petter Selasky 	}
708fd42d623SHans Petter Selasky 	for (i = 0; i < IDR_SIZE; i++) {
709fd42d623SHans Petter Selasky 		if (il->ary[i] == NULL)
710fd42d623SHans Petter Selasky 			continue;
711f885420dSHans Petter Selasky 		err = idr_for_each_layer(il->ary[i],
712f885420dSHans Petter Selasky 		    (i + offset) * IDR_SIZE, layer - 1, f, data);
713fd42d623SHans Petter Selasky 		if (err)
714fd42d623SHans Petter Selasky 			return (err);
715fd42d623SHans Petter Selasky 	}
716fd42d623SHans Petter Selasky 	return (0);
717fd42d623SHans Petter Selasky }
718fd42d623SHans Petter Selasky 
719dacb734eSHans Petter Selasky /* NOTE: It is not allowed to modify the IDR tree while it is being iterated */
720fd42d623SHans Petter Selasky int
721fd42d623SHans Petter Selasky idr_for_each(struct idr *idp, int (*f)(int id, void *p, void *data), void *data)
722fd42d623SHans Petter Selasky {
723f885420dSHans Petter Selasky 	return (idr_for_each_layer(idp->top, 0, idp->layers - 1, f, data));
724fd42d623SHans Petter Selasky }
725fd42d623SHans Petter Selasky 
726*69d6653bSHans Petter Selasky static int
727*69d6653bSHans Petter Selasky idr_has_entry(int id, void *p, void *data)
728*69d6653bSHans Petter Selasky {
729*69d6653bSHans Petter Selasky 
730*69d6653bSHans Petter Selasky 	return (1);
731*69d6653bSHans Petter Selasky }
732*69d6653bSHans Petter Selasky 
733*69d6653bSHans Petter Selasky bool
734*69d6653bSHans Petter Selasky idr_is_empty(struct idr *idp)
735*69d6653bSHans Petter Selasky {
736*69d6653bSHans Petter Selasky 
737*69d6653bSHans Petter Selasky 	return (idr_for_each(idp, idr_has_entry, NULL) == 0);
738*69d6653bSHans Petter Selasky }
739*69d6653bSHans Petter Selasky 
740fd42d623SHans Petter Selasky int
741fd42d623SHans Petter Selasky ida_pre_get(struct ida *ida, gfp_t flags)
742fd42d623SHans Petter Selasky {
743fd42d623SHans Petter Selasky 	if (idr_pre_get(&ida->idr, flags) == 0)
744fd42d623SHans Petter Selasky 		return (0);
745fd42d623SHans Petter Selasky 
746fd42d623SHans Petter Selasky 	if (ida->free_bitmap == NULL) {
747fd42d623SHans Petter Selasky 		ida->free_bitmap =
748fd42d623SHans Petter Selasky 		    malloc(sizeof(struct ida_bitmap), M_IDR, flags);
749fd42d623SHans Petter Selasky 	}
750fd42d623SHans Petter Selasky 	return (ida->free_bitmap != NULL);
751fd42d623SHans Petter Selasky }
752fd42d623SHans Petter Selasky 
753fd42d623SHans Petter Selasky int
754fd42d623SHans Petter Selasky ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
755fd42d623SHans Petter Selasky     gfp_t flags)
756fd42d623SHans Petter Selasky {
757fd42d623SHans Petter Selasky 	int ret, id;
758fd42d623SHans Petter Selasky 	unsigned int max;
759fd42d623SHans Petter Selasky 
760fd42d623SHans Petter Selasky 	MPASS((int)start >= 0);
761fd42d623SHans Petter Selasky 	MPASS((int)end >= 0);
762fd42d623SHans Petter Selasky 
763fd42d623SHans Petter Selasky 	if (end == 0)
764fd42d623SHans Petter Selasky 		max = 0x80000000;
765fd42d623SHans Petter Selasky 	else {
766fd42d623SHans Petter Selasky 		MPASS(end > start);
767fd42d623SHans Petter Selasky 		max = end - 1;
768fd42d623SHans Petter Selasky 	}
769fd42d623SHans Petter Selasky again:
770fd42d623SHans Petter Selasky 	if (!ida_pre_get(ida, flags))
771fd42d623SHans Petter Selasky 		return (-ENOMEM);
772fd42d623SHans Petter Selasky 
773fd42d623SHans Petter Selasky 	if ((ret = ida_get_new_above(ida, start, &id)) == 0) {
774fd42d623SHans Petter Selasky 		if (id > max) {
775fd42d623SHans Petter Selasky 			ida_remove(ida, id);
776fd42d623SHans Petter Selasky 			ret = -ENOSPC;
777fd42d623SHans Petter Selasky 		} else {
778fd42d623SHans Petter Selasky 			ret = id;
779fd42d623SHans Petter Selasky 		}
780fd42d623SHans Petter Selasky 	}
781fd42d623SHans Petter Selasky 	if (__predict_false(ret == -EAGAIN))
782fd42d623SHans Petter Selasky 		goto again;
783fd42d623SHans Petter Selasky 
784fd42d623SHans Petter Selasky 	return (ret);
785fd42d623SHans Petter Selasky }
786fd42d623SHans Petter Selasky 
787fd42d623SHans Petter Selasky void
788fd42d623SHans Petter Selasky ida_simple_remove(struct ida *ida, unsigned int id)
789fd42d623SHans Petter Selasky {
790fd42d623SHans Petter Selasky 	idr_remove(&ida->idr, id);
791fd42d623SHans Petter Selasky }
792fd42d623SHans Petter Selasky 
793fd42d623SHans Petter Selasky void
794fd42d623SHans Petter Selasky ida_remove(struct ida *ida, int id)
795fd42d623SHans Petter Selasky {
796fd42d623SHans Petter Selasky 	idr_remove(&ida->idr, id);
797fd42d623SHans Petter Selasky }
798fd42d623SHans Petter Selasky 
799fd42d623SHans Petter Selasky void
800fd42d623SHans Petter Selasky ida_init(struct ida *ida)
801fd42d623SHans Petter Selasky {
802fd42d623SHans Petter Selasky 	idr_init(&ida->idr);
803fd42d623SHans Petter Selasky }
804fd42d623SHans Petter Selasky 
805fd42d623SHans Petter Selasky void
806fd42d623SHans Petter Selasky ida_destroy(struct ida *ida)
807fd42d623SHans Petter Selasky {
808fd42d623SHans Petter Selasky 	idr_destroy(&ida->idr);
809fd42d623SHans Petter Selasky 	free(ida->free_bitmap, M_IDR);
810fd42d623SHans Petter Selasky }
811