xref: /dflybsd-src/sys/libkern/linux_idr.c (revision d5be22e8fd9a74a56c14af59d0e36b9f979b500d)
1b9cc0f59SFrançois Tigeot /*
2b9cc0f59SFrançois Tigeot  * Copyright (c) 2005-2012 The DragonFly Project.
3b9cc0f59SFrançois Tigeot  * Copyright (c) 2013 François Tigeot
448f1ebb3SMatthew Dillon  * Copyright (c) 2013 Matthew Dillon
5b9cc0f59SFrançois Tigeot  * All rights reserved.
6b9cc0f59SFrançois Tigeot  *
7b9cc0f59SFrançois Tigeot  * This code is derived from software contributed to The DragonFly Project
8b9cc0f59SFrançois Tigeot  * by Jeffrey Hsu.
9b9cc0f59SFrançois Tigeot  *
10b9cc0f59SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
11b9cc0f59SFrançois Tigeot  * modification, are permitted provided that the following conditions
12b9cc0f59SFrançois Tigeot  * are met:
13b9cc0f59SFrançois Tigeot  *
14b9cc0f59SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
15b9cc0f59SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer.
16b9cc0f59SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
17b9cc0f59SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in
18b9cc0f59SFrançois Tigeot  *    the documentation and/or other materials provided with the
19b9cc0f59SFrançois Tigeot  *    distribution.
20b9cc0f59SFrançois Tigeot  * 3. Neither the name of The DragonFly Project nor the names of its
21b9cc0f59SFrançois Tigeot  *    contributors may be used to endorse or promote products derived
22b9cc0f59SFrançois Tigeot  *    from this software without specific, prior written permission.
23b9cc0f59SFrançois Tigeot  *
24b9cc0f59SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25b9cc0f59SFrançois Tigeot  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26b9cc0f59SFrançois Tigeot  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27b9cc0f59SFrançois Tigeot  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
28b9cc0f59SFrançois Tigeot  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29b9cc0f59SFrançois Tigeot  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30b9cc0f59SFrançois Tigeot  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31b9cc0f59SFrançois Tigeot  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32b9cc0f59SFrançois Tigeot  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33b9cc0f59SFrançois Tigeot  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34b9cc0f59SFrançois Tigeot  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35b9cc0f59SFrançois Tigeot  * SUCH DAMAGE.
36b9cc0f59SFrançois Tigeot  */
37b9cc0f59SFrançois Tigeot 
3848f1ebb3SMatthew Dillon #ifdef USERLAND_TEST
3948f1ebb3SMatthew Dillon /*
4048f1ebb3SMatthew Dillon  * Testing:
4148f1ebb3SMatthew Dillon  *
4248f1ebb3SMatthew Dillon  * cc -I. -DUSERLAND_TEST libkern/linux_idr.c -o /tmp/idr -g
4348f1ebb3SMatthew Dillon  */
4448f1ebb3SMatthew Dillon 
4548f1ebb3SMatthew Dillon #define _KERNEL
4648f1ebb3SMatthew Dillon #define _KERNEL_STRUCTURES
4748f1ebb3SMatthew Dillon #define KLD_MODULE
4848f1ebb3SMatthew Dillon #include <stdio.h>
4948f1ebb3SMatthew Dillon #include <stdlib.h>
5048f1ebb3SMatthew Dillon #include <unistd.h>
5148f1ebb3SMatthew Dillon #include <string.h>
5248f1ebb3SMatthew Dillon #include <limits.h>
5348f1ebb3SMatthew Dillon #include <assert.h>
5448f1ebb3SMatthew Dillon #include <sys/idr.h>
5548f1ebb3SMatthew Dillon #include <sys/errno.h>
5648f1ebb3SMatthew Dillon 
5748f1ebb3SMatthew Dillon #undef MALLOC_DEFINE
5848f1ebb3SMatthew Dillon #define MALLOC_DEFINE(a, b, c)
5948f1ebb3SMatthew Dillon #define lwkt_gettoken(x)
6048f1ebb3SMatthew Dillon #define lwkt_reltoken(x)
61d0975e24SMatthew Dillon #undef kmalloc
6248f1ebb3SMatthew Dillon #define kmalloc(bytes, zone, flags)	calloc(bytes, 1)
6348f1ebb3SMatthew Dillon #define lwkt_token_init(a, b)
6448f1ebb3SMatthew Dillon #define lwkt_token_uninit(a)
6548f1ebb3SMatthew Dillon #define kfree(ptr, flags)		free(ptr)
6648f1ebb3SMatthew Dillon #define KKASSERT(a)
6748f1ebb3SMatthew Dillon #define panic(str, ...)			assert(0)
6848f1ebb3SMatthew Dillon #define min(a, b)	(((a) < (b)) ? (a) : (b))
6948f1ebb3SMatthew Dillon #define max(a, b)	(((a) > (b)) ? (a) : (b))
7048f1ebb3SMatthew Dillon 
7148f1ebb3SMatthew Dillon int
main(int ac,char ** av)7248f1ebb3SMatthew Dillon main(int ac, char **av)
7348f1ebb3SMatthew Dillon {
7448f1ebb3SMatthew Dillon 	char buf[256];
7548f1ebb3SMatthew Dillon 	struct idr idr;
76d0975e24SMatthew Dillon 	intptr_t generation = 0x0;
7748f1ebb3SMatthew Dillon 	int error;
7848f1ebb3SMatthew Dillon 	int id;
7948f1ebb3SMatthew Dillon 
8048f1ebb3SMatthew Dillon 	idr_init(&idr);
8148f1ebb3SMatthew Dillon 
8248f1ebb3SMatthew Dillon 	printf("cmd> ");
8348f1ebb3SMatthew Dillon 	fflush(stdout);
8448f1ebb3SMatthew Dillon 	while (fgets(buf, sizeof(buf), stdin) != NULL) {
8548f1ebb3SMatthew Dillon 		if (sscanf(buf, "a %d", &id) == 1) {
8648f1ebb3SMatthew Dillon 			for (;;) {
8748f1ebb3SMatthew Dillon 				if (idr_pre_get(&idr, 0) == 0) {
8848f1ebb3SMatthew Dillon 					fprintf(stderr, "pre_get failed\n");
8948f1ebb3SMatthew Dillon 					exit(1);
9048f1ebb3SMatthew Dillon 				}
9148f1ebb3SMatthew Dillon 				error = idr_get_new_above(&idr,
9248f1ebb3SMatthew Dillon 							  (void *)generation,
9348f1ebb3SMatthew Dillon 							  id, &id);
9448f1ebb3SMatthew Dillon 				if (error == -EAGAIN)
9548f1ebb3SMatthew Dillon 					continue;
9648f1ebb3SMatthew Dillon 				if (error) {
9748f1ebb3SMatthew Dillon 					fprintf(stderr, "get_new err %d\n",
9848f1ebb3SMatthew Dillon 						error);
9948f1ebb3SMatthew Dillon 					exit(1);
10048f1ebb3SMatthew Dillon 				}
10148f1ebb3SMatthew Dillon 				printf("allocated %d value %08x\n",
10248f1ebb3SMatthew Dillon 					id, (int)generation);
10348f1ebb3SMatthew Dillon 				++generation;
10448f1ebb3SMatthew Dillon 				break;
10548f1ebb3SMatthew Dillon 			}
10648f1ebb3SMatthew Dillon 		} else if (sscanf(buf, "f %d", &id) == 1) {
10748f1ebb3SMatthew Dillon 			idr_remove(&idr, id);
10848f1ebb3SMatthew Dillon 		} else if (sscanf(buf, "g %d", &id) == 1) {
10948f1ebb3SMatthew Dillon 			void *res = idr_find(&idr, id);
11048f1ebb3SMatthew Dillon 			printf("find %d res %p\n", id, res);
11148f1ebb3SMatthew Dillon 		}
11248f1ebb3SMatthew Dillon 		printf("cmd> ");
11348f1ebb3SMatthew Dillon 		fflush(stdout);
11448f1ebb3SMatthew Dillon 	}
11548f1ebb3SMatthew Dillon 	return 0;
11648f1ebb3SMatthew Dillon }
11748f1ebb3SMatthew Dillon 
11848f1ebb3SMatthew Dillon #else
11948f1ebb3SMatthew Dillon 
120b9cc0f59SFrançois Tigeot #include <sys/idr.h>
121b9cc0f59SFrançois Tigeot #include <sys/kernel.h>
122b9cc0f59SFrançois Tigeot #include <sys/libkern.h>
123b9cc0f59SFrançois Tigeot #include <sys/malloc.h>
124b9cc0f59SFrançois Tigeot #include <sys/param.h>
125b9cc0f59SFrançois Tigeot #include <sys/systm.h>
126b9cc0f59SFrançois Tigeot #include <sys/spinlock2.h>
127b9cc0f59SFrançois Tigeot #include <sys/limits.h>
128b9cc0f59SFrançois Tigeot 
12948f1ebb3SMatthew Dillon #endif
13048f1ebb3SMatthew Dillon 
131a7e8e305SJoris Giovannangeli /* Must be 2^n - 1 */
132a7e8e305SJoris Giovannangeli #define IDR_DEFAULT_SIZE    255
133b9cc0f59SFrançois Tigeot 
134b9cc0f59SFrançois Tigeot MALLOC_DEFINE(M_IDR, "idr", "Integer ID management");
135b9cc0f59SFrançois Tigeot 
136b9cc0f59SFrançois Tigeot static void	idr_grow(struct idr *idp, int want);
137b9cc0f59SFrançois Tigeot static void	idr_reserve(struct idr *idp, int id, int incr);
138b9cc0f59SFrançois Tigeot static int	idr_find_free(struct idr *idp, int want, int lim);
139b9cc0f59SFrançois Tigeot 
140b9cc0f59SFrançois Tigeot /*
141b9cc0f59SFrançois Tigeot  * Number of nodes in right subtree, including the root.
142b9cc0f59SFrançois Tigeot  */
143b9cc0f59SFrançois Tigeot static __inline int
right_subtree_size(int n)144b9cc0f59SFrançois Tigeot right_subtree_size(int n)
145b9cc0f59SFrançois Tigeot {
146b9cc0f59SFrançois Tigeot 	return (n ^ (n | (n + 1)));
147b9cc0f59SFrançois Tigeot }
148b9cc0f59SFrançois Tigeot 
149b9cc0f59SFrançois Tigeot /*
150b9cc0f59SFrançois Tigeot  * Bigger ancestor.
151b9cc0f59SFrançois Tigeot  */
152b9cc0f59SFrançois Tigeot static __inline int
right_ancestor(int n)153b9cc0f59SFrançois Tigeot right_ancestor(int n)
154b9cc0f59SFrançois Tigeot {
155b9cc0f59SFrançois Tigeot 	return (n | (n + 1));
156b9cc0f59SFrançois Tigeot }
157b9cc0f59SFrançois Tigeot 
158b9cc0f59SFrançois Tigeot /*
159b9cc0f59SFrançois Tigeot  * Smaller ancestor.
160b9cc0f59SFrançois Tigeot  */
161b9cc0f59SFrançois Tigeot static __inline int
left_ancestor(int n)162b9cc0f59SFrançois Tigeot left_ancestor(int n)
163b9cc0f59SFrançois Tigeot {
164b9cc0f59SFrançois Tigeot 	return ((n & (n + 1)) - 1);
165b9cc0f59SFrançois Tigeot }
166b9cc0f59SFrançois Tigeot 
167b9cc0f59SFrançois Tigeot static __inline void
idrfixup(struct idr * idp,int id)168b9cc0f59SFrançois Tigeot idrfixup(struct idr *idp, int id)
169b9cc0f59SFrançois Tigeot {
170b9cc0f59SFrançois Tigeot 	if (id < idp->idr_freeindex) {
171b9cc0f59SFrançois Tigeot 		idp->idr_freeindex = id;
172b9cc0f59SFrançois Tigeot 	}
173b9cc0f59SFrançois Tigeot 	while (idp->idr_lastindex >= 0 &&
17448f1ebb3SMatthew Dillon 		idp->idr_nodes[idp->idr_lastindex].data == NULL
175b9cc0f59SFrançois Tigeot 	) {
176b9cc0f59SFrançois Tigeot 		--idp->idr_lastindex;
177b9cc0f59SFrançois Tigeot 	}
178b9cc0f59SFrançois Tigeot }
179b9cc0f59SFrançois Tigeot 
180b9cc0f59SFrançois Tigeot static __inline struct idr_node *
idr_get_node(struct idr * idp,int id)181b9cc0f59SFrançois Tigeot idr_get_node(struct idr *idp, int id)
182b9cc0f59SFrançois Tigeot {
183b9cc0f59SFrançois Tigeot 	struct idr_node *idrnp;
1844b1ff474SJohannes Hofmann 	if (id < 0 || id >= idp->idr_count)
185b9cc0f59SFrançois Tigeot 		return (NULL);
186b9cc0f59SFrançois Tigeot 	idrnp = &idp->idr_nodes[id];
187b9cc0f59SFrançois Tigeot 	if (idrnp->allocated == 0)
188b9cc0f59SFrançois Tigeot 		return (NULL);
189b9cc0f59SFrançois Tigeot 	return (idrnp);
190b9cc0f59SFrançois Tigeot }
191b9cc0f59SFrançois Tigeot 
192b9cc0f59SFrançois Tigeot static void
idr_reserve(struct idr * idp,int id,int incr)193b9cc0f59SFrançois Tigeot idr_reserve(struct idr *idp, int id, int incr)
194b9cc0f59SFrançois Tigeot {
195b9cc0f59SFrançois Tigeot 	while (id >= 0) {
196b9cc0f59SFrançois Tigeot 		idp->idr_nodes[id].allocated += incr;
197b9cc0f59SFrançois Tigeot 		KKASSERT(idp->idr_nodes[id].allocated >= 0);
198b9cc0f59SFrançois Tigeot 		id = left_ancestor(id);
199b9cc0f59SFrançois Tigeot 	}
200b9cc0f59SFrançois Tigeot }
201b9cc0f59SFrançois Tigeot 
202b9cc0f59SFrançois Tigeot static int
idr_find_free(struct idr * idp,int want,int lim)203b9cc0f59SFrançois Tigeot idr_find_free(struct idr *idp, int want, int lim)
204b9cc0f59SFrançois Tigeot {
205b9cc0f59SFrançois Tigeot 	int id, rsum, rsize, node;
206b9cc0f59SFrançois Tigeot 
207b9cc0f59SFrançois Tigeot 	/*
208b9cc0f59SFrançois Tigeot 	 * Search for a free descriptor starting at the higher
209b9cc0f59SFrançois Tigeot 	 * of want or fd_freefile.  If that fails, consider
210b9cc0f59SFrançois Tigeot 	 * expanding the ofile array.
211b9cc0f59SFrançois Tigeot 	 *
212b9cc0f59SFrançois Tigeot 	 * NOTE! the 'allocated' field is a cumulative recursive allocation
213b9cc0f59SFrançois Tigeot 	 * count.  If we happen to see a value of 0 then we can shortcut
214b9cc0f59SFrançois Tigeot 	 * our search.  Otherwise we run through through the tree going
215b9cc0f59SFrançois Tigeot 	 * down branches we know have free descriptor(s) until we hit a
216b9cc0f59SFrançois Tigeot 	 * leaf node.  The leaf node will be free but will not necessarily
217b9cc0f59SFrançois Tigeot 	 * have an allocated field of 0.
218b9cc0f59SFrançois Tigeot 	 */
219b9cc0f59SFrançois Tigeot 	/* move up the tree looking for a subtree with a free node */
220b9cc0f59SFrançois Tigeot 	for (id = max(want, idp->idr_freeindex); id < min(idp->idr_count, lim);
221b9cc0f59SFrançois Tigeot 			id = right_ancestor(id)) {
222b9cc0f59SFrançois Tigeot 		if (idp->idr_nodes[id].allocated == 0)
223b9cc0f59SFrançois Tigeot 			return (id);
224b9cc0f59SFrançois Tigeot 
225b9cc0f59SFrançois Tigeot 		rsize = right_subtree_size(id);
226b9cc0f59SFrançois Tigeot 		if (idp->idr_nodes[id].allocated == rsize)
227b9cc0f59SFrançois Tigeot 			continue;	/* right subtree full */
228b9cc0f59SFrançois Tigeot 
229b9cc0f59SFrançois Tigeot 		/*
230b9cc0f59SFrançois Tigeot 		 * Free fd is in the right subtree of the tree rooted at fd.
231b9cc0f59SFrançois Tigeot 		 * Call that subtree R.  Look for the smallest (leftmost)
232b9cc0f59SFrançois Tigeot 		 * subtree of R with an unallocated fd: continue moving
233b9cc0f59SFrançois Tigeot 		 * down the left branch until encountering a full left
234b9cc0f59SFrançois Tigeot 		 * subtree, then move to the right.
235b9cc0f59SFrançois Tigeot 		 */
236b9cc0f59SFrançois Tigeot 		for (rsum = 0, rsize /= 2; rsize > 0; rsize /= 2) {
237b9cc0f59SFrançois Tigeot 			node = id + rsize;
238b9cc0f59SFrançois Tigeot 			rsum += idp->idr_nodes[node].allocated;
239b9cc0f59SFrançois Tigeot 			if (idp->idr_nodes[id].allocated == rsum + rsize) {
240b9cc0f59SFrançois Tigeot 				id = node;	/* move to the right */
241b9cc0f59SFrançois Tigeot 				if (idp->idr_nodes[node].allocated == 0)
242b9cc0f59SFrançois Tigeot 					return (id);
243b9cc0f59SFrançois Tigeot 				rsum = 0;
244b9cc0f59SFrançois Tigeot 			}
245b9cc0f59SFrançois Tigeot 		}
246b9cc0f59SFrançois Tigeot 		return (id);
247b9cc0f59SFrançois Tigeot 	}
248b9cc0f59SFrançois Tigeot 	return (-1);
249b9cc0f59SFrançois Tigeot }
250b9cc0f59SFrançois Tigeot 
251b9cc0f59SFrançois Tigeot /*
25248f1ebb3SMatthew Dillon  * Blocking pre-get support, allows callers to use idr_pre_get() in
25348f1ebb3SMatthew Dillon  * combination with idr_get_new_above() such that idr_get_new_above()
25448f1ebb3SMatthew Dillon  * can be called safely with a spinlock held.
25548f1ebb3SMatthew Dillon  *
25648f1ebb3SMatthew Dillon  * Returns 0 on failure, 1 on success.
25748f1ebb3SMatthew Dillon  *
25848f1ebb3SMatthew Dillon  * Caller must hold a blockable lock.
259b9cc0f59SFrançois Tigeot  */
260b9cc0f59SFrançois Tigeot int
idr_pre_get(struct idr * idp,__unused unsigned gfp_mask)261b9cc0f59SFrançois Tigeot idr_pre_get(struct idr *idp, __unused unsigned gfp_mask)
262b9cc0f59SFrançois Tigeot {
26348f1ebb3SMatthew Dillon 	int want = idp->idr_maxwant;
26448f1ebb3SMatthew Dillon 	int lim = INT_MAX;
26548f1ebb3SMatthew Dillon 	int result = 1;				/* success */
26648f1ebb3SMatthew Dillon 	int id;
26748f1ebb3SMatthew Dillon 
26848f1ebb3SMatthew Dillon 	KKASSERT(mycpu->gd_spinlocks == 0);
269b9cc0f59SFrançois Tigeot 	lwkt_gettoken(&idp->idr_token);
27048f1ebb3SMatthew Dillon 	for (;;) {
27148f1ebb3SMatthew Dillon 		/*
27248f1ebb3SMatthew Dillon 		 * Grow if necessary (or if forced by the loop)
27348f1ebb3SMatthew Dillon 		 */
27448f1ebb3SMatthew Dillon 		if (want >= idp->idr_count)
27548f1ebb3SMatthew Dillon 			idr_grow(idp, want);
27648f1ebb3SMatthew Dillon 
27748f1ebb3SMatthew Dillon 		/*
27848f1ebb3SMatthew Dillon 		 * Check if a spot is available, break and return 0 if true,
27948f1ebb3SMatthew Dillon 		 * unless the available spot is beyond our limit.  It is
28048f1ebb3SMatthew Dillon 		 * possible to exceed the limit due to the way array growth
28148f1ebb3SMatthew Dillon 		 * works.
28248f1ebb3SMatthew Dillon 		 *
28348f1ebb3SMatthew Dillon 		 * XXX we assume that the caller uses a consistent <sid> such
28448f1ebb3SMatthew Dillon 		 *     that the idr_maxwant field is correct, otherwise we
28548f1ebb3SMatthew Dillon 		 *     may believe that a slot is available but the caller then
28648f1ebb3SMatthew Dillon 		 *     fails in idr_get_new_above() and loops.
28748f1ebb3SMatthew Dillon 		 */
28848f1ebb3SMatthew Dillon 		id = idr_find_free(idp, idp->idr_maxwant, lim);
28948f1ebb3SMatthew Dillon 		if (id != -1) {
29048f1ebb3SMatthew Dillon 			if (id >= lim)
29148f1ebb3SMatthew Dillon 				result = 0;	/* failure */
29248f1ebb3SMatthew Dillon 			break;
293b9cc0f59SFrançois Tigeot 		}
294b9cc0f59SFrançois Tigeot 
29548f1ebb3SMatthew Dillon 		/*
29648f1ebb3SMatthew Dillon 		 * Return ENOSPC if our limit has been reached, otherwise
29748f1ebb3SMatthew Dillon 		 * loop and force growth.
29848f1ebb3SMatthew Dillon 		 */
29948f1ebb3SMatthew Dillon 		if (idp->idr_count >= lim) {
30048f1ebb3SMatthew Dillon 			result = 0;		/* failure */
30148f1ebb3SMatthew Dillon 			break;
30248f1ebb3SMatthew Dillon 		}
30348f1ebb3SMatthew Dillon 		want = idp->idr_count;
30448f1ebb3SMatthew Dillon 	}
30548f1ebb3SMatthew Dillon 	lwkt_reltoken(&idp->idr_token);
30648f1ebb3SMatthew Dillon 	return result;
30748f1ebb3SMatthew Dillon }
30848f1ebb3SMatthew Dillon 
30948f1ebb3SMatthew Dillon /*
31048f1ebb3SMatthew Dillon  * Allocate an integer.  If -EAGAIN is returned the caller should loop
31148f1ebb3SMatthew Dillon  * and call idr_pre_get() with no locks held, and then retry the call
31248f1ebb3SMatthew Dillon  * to idr_get_new_above().
31348f1ebb3SMatthew Dillon  *
31448f1ebb3SMatthew Dillon  * Can be safely called with spinlocks held.
31548f1ebb3SMatthew Dillon  */
316b9cc0f59SFrançois Tigeot int
idr_get_new_above(struct idr * idp,void * ptr,int sid,int * id)317b9cc0f59SFrançois Tigeot idr_get_new_above(struct idr *idp, void *ptr, int sid, int *id)
318b9cc0f59SFrançois Tigeot {
319b9cc0f59SFrançois Tigeot 	int resid;
320b9cc0f59SFrançois Tigeot 
32148f1ebb3SMatthew Dillon 	/*
32248f1ebb3SMatthew Dillon 	 * NOTE! Because the idp is initialized with a non-zero count,
32348f1ebb3SMatthew Dillon 	 *	 sid might be < idp->idr_count but idr_maxwant might not
32448f1ebb3SMatthew Dillon 	 *	 yet be initialized.  So check both cases.
32548f1ebb3SMatthew Dillon 	 */
326b9cc0f59SFrançois Tigeot 	lwkt_gettoken(&idp->idr_token);
32748f1ebb3SMatthew Dillon 	if (sid >= idp->idr_count || idp->idr_maxwant < sid) {
328b9cc0f59SFrançois Tigeot 		idp->idr_maxwant = max(idp->idr_maxwant, sid);
329b9cc0f59SFrançois Tigeot 		lwkt_reltoken(&idp->idr_token);
330b9cc0f59SFrançois Tigeot 		return -EAGAIN;
331b9cc0f59SFrançois Tigeot 	}
332b9cc0f59SFrançois Tigeot 
333b9cc0f59SFrançois Tigeot 	resid = idr_find_free(idp, sid, INT_MAX);
334b9cc0f59SFrançois Tigeot 	if (resid == -1) {
335b9cc0f59SFrançois Tigeot 		lwkt_reltoken(&idp->idr_token);
336b9cc0f59SFrançois Tigeot 		return -EAGAIN;
337b9cc0f59SFrançois Tigeot 	}
338b9cc0f59SFrançois Tigeot 
339b9cc0f59SFrançois Tigeot 	if (resid >= idp->idr_count)
34048f1ebb3SMatthew Dillon 		panic("idr_get_new_above(): illegal resid %d", resid);
341b9cc0f59SFrançois Tigeot 	if (resid > idp->idr_lastindex)
342b9cc0f59SFrançois Tigeot 		idp->idr_lastindex = resid;
343b9cc0f59SFrançois Tigeot 	if (sid <= idp->idr_freeindex)
344b9cc0f59SFrançois Tigeot 		idp->idr_freeindex = resid;
345b9cc0f59SFrançois Tigeot 	*id = resid;
346b9cc0f59SFrançois Tigeot 	idr_reserve(idp, resid, 1);
34748f1ebb3SMatthew Dillon 	idp->idr_nodes[resid].data = ptr;
348b9cc0f59SFrançois Tigeot 
349b9cc0f59SFrançois Tigeot 	lwkt_reltoken(&idp->idr_token);
350b9cc0f59SFrançois Tigeot 	return (0);
351b9cc0f59SFrançois Tigeot }
352b9cc0f59SFrançois Tigeot 
353829d44e6SFrançois Tigeot /*
354829d44e6SFrançois Tigeot  * start: minimum id, inclusive
355829d44e6SFrançois Tigeot  * end:   maximum id, exclusive or INT_MAX if end is negative
356829d44e6SFrançois Tigeot  */
357829d44e6SFrançois Tigeot int
idr_alloc(struct idr * idp,void * ptr,int start,int end,unsigned gfp_mask)358829d44e6SFrançois Tigeot idr_alloc(struct idr *idp, void *ptr, int start, int end, unsigned gfp_mask)
359829d44e6SFrançois Tigeot {
360829d44e6SFrançois Tigeot 	int lim = end > 0 ? end - 1 : INT_MAX;
361637b602cSFrançois Tigeot 	int want = start;
362829d44e6SFrançois Tigeot 	int result, id;
363829d44e6SFrançois Tigeot 
364829d44e6SFrançois Tigeot 	if (start < 0)
365829d44e6SFrançois Tigeot 		return -EINVAL;
366829d44e6SFrançois Tigeot 
367829d44e6SFrançois Tigeot 	if (lim < start)
368829d44e6SFrançois Tigeot 		return -ENOSPC;
369829d44e6SFrançois Tigeot 
370829d44e6SFrançois Tigeot 	lwkt_gettoken(&idp->idr_token);
371829d44e6SFrançois Tigeot 
372637b602cSFrançois Tigeot grow_again:
373637b602cSFrançois Tigeot 	if (want >= idp->idr_count)
374637b602cSFrançois Tigeot 		idr_grow(idp, want);
375829d44e6SFrançois Tigeot 
376829d44e6SFrançois Tigeot 	/*
377829d44e6SFrançois Tigeot 	 * Check if a spot is available, break and return 0 if true,
378829d44e6SFrançois Tigeot 	 * unless the available spot is beyond our limit.  It is
379829d44e6SFrançois Tigeot 	 * possible to exceed the limit due to the way array growth
380829d44e6SFrançois Tigeot 	 * works.
381829d44e6SFrançois Tigeot 	 */
382*d5be22e8SMatthew Dillon 	id = idr_find_free(idp, start, INT_MAX);
383829d44e6SFrançois Tigeot 	if (id == -1) {
384637b602cSFrançois Tigeot 		want = idp->idr_count;
385637b602cSFrançois Tigeot 		goto grow_again;
386829d44e6SFrançois Tigeot 	}
387829d44e6SFrançois Tigeot 
388*d5be22e8SMatthew Dillon 	if (id > lim) {
389829d44e6SFrançois Tigeot 		result = -ENOSPC;
390829d44e6SFrançois Tigeot 		goto done;
391829d44e6SFrançois Tigeot 	}
392829d44e6SFrançois Tigeot 
393829d44e6SFrançois Tigeot 	if (id >= idp->idr_count)
394637b602cSFrançois Tigeot 		panic("idr_alloc(): illegal resid %d", id);
395829d44e6SFrançois Tigeot 	if (id > idp->idr_lastindex)
396829d44e6SFrançois Tigeot 		idp->idr_lastindex = id;
397829d44e6SFrançois Tigeot 	if (start <= idp->idr_freeindex)
398829d44e6SFrançois Tigeot 		idp->idr_freeindex = id;
399829d44e6SFrançois Tigeot 	result = id;
400829d44e6SFrançois Tigeot 	idr_reserve(idp, id, 1);
401829d44e6SFrançois Tigeot 	idp->idr_nodes[id].data = ptr;
402829d44e6SFrançois Tigeot 
403829d44e6SFrançois Tigeot done:
404829d44e6SFrançois Tigeot 	lwkt_reltoken(&idp->idr_token);
405829d44e6SFrançois Tigeot 	return result;
406829d44e6SFrançois Tigeot }
407829d44e6SFrançois Tigeot 
408b9cc0f59SFrançois Tigeot int
idr_get_new(struct idr * idp,void * ptr,int * id)409b9cc0f59SFrançois Tigeot idr_get_new(struct idr *idp, void *ptr, int *id)
410b9cc0f59SFrançois Tigeot {
411b9cc0f59SFrançois Tigeot 	return idr_get_new_above(idp, ptr, 0, id);
412b9cc0f59SFrançois Tigeot }
413b9cc0f59SFrançois Tigeot 
414b9cc0f59SFrançois Tigeot /*
415b9cc0f59SFrançois Tigeot  * Grow the file table so it can hold through descriptor (want).
41648f1ebb3SMatthew Dillon  *
41748f1ebb3SMatthew Dillon  * Caller must hold a blockable lock.
418b9cc0f59SFrançois Tigeot  */
419b9cc0f59SFrançois Tigeot static void
idr_grow(struct idr * idp,int want)420b9cc0f59SFrançois Tigeot idr_grow(struct idr *idp, int want)
421b9cc0f59SFrançois Tigeot {
422b9cc0f59SFrançois Tigeot 	struct idr_node *oldnodes, *newnodes;
423b9cc0f59SFrançois Tigeot 	int nf;
424b9cc0f59SFrançois Tigeot 
425a7e8e305SJoris Giovannangeli 	/* We want 2^n - 1 descriptors */
426b9cc0f59SFrançois Tigeot 	nf = idp->idr_count;
427b9cc0f59SFrançois Tigeot 	do {
428a7e8e305SJoris Giovannangeli 		nf = 2 * nf + 1;
429b9cc0f59SFrançois Tigeot 	} while (nf <= want);
430b9cc0f59SFrançois Tigeot 
43148f1ebb3SMatthew Dillon #ifdef USERLAND_TEST
43248f1ebb3SMatthew Dillon 	printf("idr_grow: %d -> %d\n", idp->idr_count, nf);
43348f1ebb3SMatthew Dillon #endif
43448f1ebb3SMatthew Dillon 
435b9cc0f59SFrançois Tigeot 	/* Allocate a new zero'ed node array */
43648f1ebb3SMatthew Dillon 	newnodes = kmalloc(nf * sizeof(struct idr_node),
43748f1ebb3SMatthew Dillon 			   M_IDR, M_ZERO | M_WAITOK);
438b9cc0f59SFrançois Tigeot 
439a7e8e305SJoris Giovannangeli 	/* We might race another grow */
440a7e8e305SJoris Giovannangeli 	if (nf <= idp->idr_count) {
441a7e8e305SJoris Giovannangeli 		kfree(newnodes, M_IDR);
442a7e8e305SJoris Giovannangeli 		return;
443a7e8e305SJoris Giovannangeli 	}
444a7e8e305SJoris Giovannangeli 
44548f1ebb3SMatthew Dillon 	/*
44648f1ebb3SMatthew Dillon 	 * Copy existing nodes to the beginning of the new array
44748f1ebb3SMatthew Dillon 	 */
448b9cc0f59SFrançois Tigeot 	oldnodes = idp->idr_nodes;
44948f1ebb3SMatthew Dillon 	if (oldnodes) {
45048f1ebb3SMatthew Dillon 		bcopy(oldnodes, newnodes,
45148f1ebb3SMatthew Dillon 		      idp->idr_count * sizeof(struct idr_node));
45248f1ebb3SMatthew Dillon 	}
453b9cc0f59SFrançois Tigeot 	idp->idr_nodes = newnodes;
454b9cc0f59SFrançois Tigeot 	idp->idr_count = nf;
455b9cc0f59SFrançois Tigeot 
45648f1ebb3SMatthew Dillon 	if (oldnodes) {
457b9cc0f59SFrançois Tigeot 		kfree(oldnodes, M_IDR);
458b9cc0f59SFrançois Tigeot 	}
459b9cc0f59SFrançois Tigeot 	idp->idr_nexpands++;
460b9cc0f59SFrançois Tigeot }
461b9cc0f59SFrançois Tigeot 
462d0975e24SMatthew Dillon void *
idr_remove(struct idr * idp,int id)463b9cc0f59SFrançois Tigeot idr_remove(struct idr *idp, int id)
464b9cc0f59SFrançois Tigeot {
465b9cc0f59SFrançois Tigeot 	void *ptr;
466b9cc0f59SFrançois Tigeot 
467b9cc0f59SFrançois Tigeot 	lwkt_gettoken(&idp->idr_token);
46848f1ebb3SMatthew Dillon 	if (id < 0 || id >= idp->idr_count) {
46948f1ebb3SMatthew Dillon 		lwkt_reltoken(&idp->idr_token);
470d0975e24SMatthew Dillon 		return NULL;
47148f1ebb3SMatthew Dillon 	}
472d0975e24SMatthew Dillon         if (idp->idr_nodes[id].allocated == 0) {
47348f1ebb3SMatthew Dillon                 lwkt_reltoken(&idp->idr_token);
474d0975e24SMatthew Dillon                 return NULL;
47548f1ebb3SMatthew Dillon         }
476d0975e24SMatthew Dillon 	ptr = idp->idr_nodes[id].data;
477b9cc0f59SFrançois Tigeot 	idp->idr_nodes[id].data = NULL;
478b9cc0f59SFrançois Tigeot 	idr_reserve(idp, id, -1);
479b9cc0f59SFrançois Tigeot 	idrfixup(idp, id);
480b9cc0f59SFrançois Tigeot 	lwkt_reltoken(&idp->idr_token);
481d0975e24SMatthew Dillon 
482d0975e24SMatthew Dillon 	return ptr;
483b9cc0f59SFrançois Tigeot }
484b9cc0f59SFrançois Tigeot 
48548f1ebb3SMatthew Dillon /*
48648f1ebb3SMatthew Dillon  * Remove all int allocations, leave array intact.
48748f1ebb3SMatthew Dillon  *
48848f1ebb3SMatthew Dillon  * Caller must hold a blockable lock (or be in a context where holding
48948f1ebb3SMatthew Dillon  * the spinlock is not relevant).
49048f1ebb3SMatthew Dillon  */
491b9cc0f59SFrançois Tigeot void
idr_remove_all(struct idr * idp)492b9cc0f59SFrançois Tigeot idr_remove_all(struct idr *idp)
493b9cc0f59SFrançois Tigeot {
494a7e8e305SJoris Giovannangeli 	lwkt_gettoken(&idp->idr_token);
49548f1ebb3SMatthew Dillon 	bzero(idp->idr_nodes, idp->idr_count * sizeof(struct idr_node));
496b9cc0f59SFrançois Tigeot 	idp->idr_lastindex = -1;
497b9cc0f59SFrançois Tigeot 	idp->idr_freeindex = 0;
498b9cc0f59SFrançois Tigeot 	idp->idr_nexpands = 0;
499b9cc0f59SFrançois Tigeot 	idp->idr_maxwant = 0;
500a7e8e305SJoris Giovannangeli 	lwkt_reltoken(&idp->idr_token);
501b9cc0f59SFrançois Tigeot }
502b9cc0f59SFrançois Tigeot 
503b9cc0f59SFrançois Tigeot void
idr_destroy(struct idr * idp)504b9cc0f59SFrançois Tigeot idr_destroy(struct idr *idp)
505b9cc0f59SFrançois Tigeot {
506b9cc0f59SFrançois Tigeot 	lwkt_token_uninit(&idp->idr_token);
50748f1ebb3SMatthew Dillon 	if (idp->idr_nodes) {
508b9cc0f59SFrançois Tigeot 		kfree(idp->idr_nodes, M_IDR);
50948f1ebb3SMatthew Dillon 		idp->idr_nodes = NULL;
51048f1ebb3SMatthew Dillon 	}
51148f1ebb3SMatthew Dillon 	bzero(idp, sizeof(*idp));
512b9cc0f59SFrançois Tigeot }
513b9cc0f59SFrançois Tigeot 
514b9cc0f59SFrançois Tigeot void *
idr_find(struct idr * idp,int id)515b9cc0f59SFrançois Tigeot idr_find(struct idr *idp, int id)
516b9cc0f59SFrançois Tigeot {
51748f1ebb3SMatthew Dillon 	void *ret;
518b9cc0f59SFrançois Tigeot 
5194b1ff474SJohannes Hofmann 	if (id < 0 || id >= idp->idr_count) {
52048f1ebb3SMatthew Dillon 		ret = NULL;
521b9cc0f59SFrançois Tigeot 	} else if (idp->idr_nodes[id].allocated == 0) {
52248f1ebb3SMatthew Dillon 		ret = NULL;
52348f1ebb3SMatthew Dillon 	} else {
524b9cc0f59SFrançois Tigeot 		ret = idp->idr_nodes[id].data;
52548f1ebb3SMatthew Dillon 	}
526b9cc0f59SFrançois Tigeot 	return ret;
527b9cc0f59SFrançois Tigeot }
528b9cc0f59SFrançois Tigeot 
529b9cc0f59SFrançois Tigeot int
idr_for_each(struct idr * idp,int (* fn)(int id,void * p,void * data),void * data)53048f1ebb3SMatthew Dillon idr_for_each(struct idr *idp, int (*fn)(int id, void *p, void *data),
53148f1ebb3SMatthew Dillon 	     void *data)
532b9cc0f59SFrançois Tigeot {
533b9cc0f59SFrançois Tigeot 	int i, error = 0;
5348b8413daSJohannes Hofmann 	struct idr_node *nodes;
535b9cc0f59SFrançois Tigeot 
5368b8413daSJohannes Hofmann 	nodes = idp->idr_nodes;
537b9cc0f59SFrançois Tigeot 	for (i = 0; i < idp->idr_count; i++) {
538b9cc0f59SFrançois Tigeot 		if (nodes[i].data != NULL && nodes[i].allocated > 0) {
539b9cc0f59SFrançois Tigeot 			error = fn(i, nodes[i].data, data);
540b9cc0f59SFrançois Tigeot 			if (error != 0)
54148f1ebb3SMatthew Dillon 				break;
542b9cc0f59SFrançois Tigeot 		}
543b9cc0f59SFrançois Tigeot 	}
544b9cc0f59SFrançois Tigeot 	return error;
545b9cc0f59SFrançois Tigeot }
546b9cc0f59SFrançois Tigeot 
547b9cc0f59SFrançois Tigeot void *
idr_replace(struct idr * idp,void * ptr,int id)548b9cc0f59SFrançois Tigeot idr_replace(struct idr *idp, void *ptr, int id)
549b9cc0f59SFrançois Tigeot {
550b9cc0f59SFrançois Tigeot 	struct idr_node *idrnp;
55148f1ebb3SMatthew Dillon 	void *ret;
552b9cc0f59SFrançois Tigeot 
553b9cc0f59SFrançois Tigeot 	lwkt_gettoken(&idp->idr_token);
554b9cc0f59SFrançois Tigeot 	idrnp = idr_get_node(idp, id);
555d0975e24SMatthew Dillon 	if (idrnp == NULL) {
55648f1ebb3SMatthew Dillon 		ret = NULL;
55748f1ebb3SMatthew Dillon 	} else {
558b9cc0f59SFrançois Tigeot 		ret = idrnp->data;
559b9cc0f59SFrançois Tigeot 		idrnp->data = ptr;
56048f1ebb3SMatthew Dillon 	}
561b9cc0f59SFrançois Tigeot 	lwkt_reltoken(&idp->idr_token);
562b9cc0f59SFrançois Tigeot 	return (ret);
563b9cc0f59SFrançois Tigeot }
564b9cc0f59SFrançois Tigeot 
565b9cc0f59SFrançois Tigeot void
idr_init(struct idr * idp)566b9cc0f59SFrançois Tigeot idr_init(struct idr *idp)
567b9cc0f59SFrançois Tigeot {
568b9cc0f59SFrançois Tigeot 	bzero(idp, sizeof(struct idr));
5695feab6a3SJohannes Hofmann 	idp->idr_nodes = kmalloc(IDR_DEFAULT_SIZE * sizeof(struct idr_node),
570b9cc0f59SFrançois Tigeot 						M_IDR, M_WAITOK | M_ZERO);
571b9cc0f59SFrançois Tigeot 	idp->idr_count = IDR_DEFAULT_SIZE;
572b9cc0f59SFrançois Tigeot 	idp->idr_lastindex = -1;
573b9cc0f59SFrançois Tigeot 	idp->idr_maxwant = 0;
574b9cc0f59SFrançois Tigeot 	lwkt_token_init(&idp->idr_token, "idr token");
575b9cc0f59SFrançois Tigeot }
576