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