xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_idr.c (revision 51e5c539c0e4f24a17f68d3743d9ccc011a8d21d)
1*51e5c539Sriastradh /*	$NetBSD: linux_idr.c,v 1.15 2021/12/19 12:21:02 riastradh Exp $	*/
26cb10275Sriastradh 
36cb10275Sriastradh /*-
46cb10275Sriastradh  * Copyright (c) 2013 The NetBSD Foundation, Inc.
56cb10275Sriastradh  * All rights reserved.
66cb10275Sriastradh  *
76cb10275Sriastradh  * This code is derived from software contributed to The NetBSD Foundation
86cb10275Sriastradh  * by Taylor R. Campbell.
96cb10275Sriastradh  *
106cb10275Sriastradh  * Redistribution and use in source and binary forms, with or without
116cb10275Sriastradh  * modification, are permitted provided that the following conditions
126cb10275Sriastradh  * are met:
136cb10275Sriastradh  * 1. Redistributions of source code must retain the above copyright
146cb10275Sriastradh  *    notice, this list of conditions and the following disclaimer.
156cb10275Sriastradh  * 2. Redistributions in binary form must reproduce the above copyright
166cb10275Sriastradh  *    notice, this list of conditions and the following disclaimer in the
176cb10275Sriastradh  *    documentation and/or other materials provided with the distribution.
186cb10275Sriastradh  *
196cb10275Sriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206cb10275Sriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216cb10275Sriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226cb10275Sriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236cb10275Sriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246cb10275Sriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256cb10275Sriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266cb10275Sriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276cb10275Sriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286cb10275Sriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296cb10275Sriastradh  * POSSIBILITY OF SUCH DAMAGE.
306cb10275Sriastradh  */
316cb10275Sriastradh 
326cb10275Sriastradh #include <sys/cdefs.h>
33*51e5c539Sriastradh __KERNEL_RCSID(0, "$NetBSD: linux_idr.c,v 1.15 2021/12/19 12:21:02 riastradh Exp $");
346cb10275Sriastradh 
356cb10275Sriastradh #include <sys/param.h>
366cb10275Sriastradh #include <sys/atomic.h>
376cb10275Sriastradh #include <sys/rbtree.h>
385248a24cSriastradh #include <sys/sdt.h>
396cb10275Sriastradh 
406cb10275Sriastradh #include <linux/err.h>
416cb10275Sriastradh #include <linux/idr.h>
4277b5597aSriastradh #include <linux/slab.h>
436cb10275Sriastradh 
441d8bbc46Sriastradh #ifdef _KERNEL_OPT
451d8bbc46Sriastradh #include "opt_ddb.h"
461d8bbc46Sriastradh #endif
471d8bbc46Sriastradh 
481d8bbc46Sriastradh #ifdef DDB
491d8bbc46Sriastradh #include <ddb/ddb.h>
501d8bbc46Sriastradh #endif
511d8bbc46Sriastradh 
526cb10275Sriastradh struct idr_node {
536cb10275Sriastradh 	rb_node_t		in_rb_node;
546cb10275Sriastradh 	int			in_index;
556cb10275Sriastradh 	void			*in_data;
566cb10275Sriastradh };
5777b5597aSriastradh 
581d8bbc46Sriastradh struct idr_cache {
591d8bbc46Sriastradh 	struct idr_node		*ic_node;
601d8bbc46Sriastradh 	void			*ic_where;
611d8bbc46Sriastradh };
621d8bbc46Sriastradh 
635248a24cSriastradh SDT_PROBE_DEFINE0(sdt, linux, idr, leak);
645248a24cSriastradh SDT_PROBE_DEFINE1(sdt, linux, idr, init, "struct idr *"/*idr*/);
655248a24cSriastradh SDT_PROBE_DEFINE1(sdt, linux, idr, destroy, "struct idr *"/*idr*/);
665248a24cSriastradh SDT_PROBE_DEFINE4(sdt, linux, idr, replace,
675248a24cSriastradh     "struct idr *"/*idr*/, "int"/*id*/, "void *"/*odata*/, "void *"/*ndata*/);
685248a24cSriastradh SDT_PROBE_DEFINE3(sdt, linux, idr, remove,
695248a24cSriastradh     "struct idr *"/*idr*/, "int"/*id*/, "void *"/*data*/);
705248a24cSriastradh SDT_PROBE_DEFINE0(sdt, linux, idr, preload);
7171b0b921Sriastradh SDT_PROBE_DEFINE0(sdt, linux, idr, preload__end);
725248a24cSriastradh SDT_PROBE_DEFINE3(sdt, linux, idr, alloc,
735248a24cSriastradh     "struct idr *"/*idr*/, "int"/*id*/, "void *"/*data*/);
745248a24cSriastradh 
751d8bbc46Sriastradh static specificdata_key_t idr_cache_key __read_mostly;
761d8bbc46Sriastradh 
771d8bbc46Sriastradh static void
idr_cache_warning(struct idr_cache * cache)781d8bbc46Sriastradh idr_cache_warning(struct idr_cache *cache)
791d8bbc46Sriastradh {
801d8bbc46Sriastradh #ifdef DDB
811d8bbc46Sriastradh 	const char *name;
821d8bbc46Sriastradh 	db_expr_t offset;
831d8bbc46Sriastradh #endif
841d8bbc46Sriastradh 
851d8bbc46Sriastradh 	KASSERT(cache->ic_node != NULL);
861d8bbc46Sriastradh 
871d8bbc46Sriastradh #ifdef DDB
881d8bbc46Sriastradh 	db_find_sym_and_offset((db_addr_t)(uintptr_t)cache->ic_where,
891d8bbc46Sriastradh 	    &name, &offset);
901d8bbc46Sriastradh 	if (name) {
911d8bbc46Sriastradh 		printf("WARNING: idr preload at %s+%#"DDB_EXPR_FMT"x"
921d8bbc46Sriastradh 		    " leaked in lwp %s @ %p\n",
931d8bbc46Sriastradh 		    name, offset, curlwp->l_name, curlwp);
941d8bbc46Sriastradh 	} else
951d8bbc46Sriastradh #endif
961d8bbc46Sriastradh 	{
971d8bbc46Sriastradh 		printf("WARNING: idr preload at %p leaked in lwp %s @ %p\n",
981d8bbc46Sriastradh 		    cache->ic_where, curlwp->l_name, curlwp);
991d8bbc46Sriastradh 	}
1001d8bbc46Sriastradh }
1011d8bbc46Sriastradh 
1021d8bbc46Sriastradh static void
idr_cache_dtor(void * cookie)1031d8bbc46Sriastradh idr_cache_dtor(void *cookie)
1041d8bbc46Sriastradh {
1051d8bbc46Sriastradh 	struct idr_cache *cache = cookie;
1061d8bbc46Sriastradh 
1071d8bbc46Sriastradh 	if (cache->ic_node) {
1085248a24cSriastradh 		SDT_PROBE0(sdt, linux, idr, leak);
1091d8bbc46Sriastradh 		idr_cache_warning(cache);
1101d8bbc46Sriastradh 		kmem_free(cache->ic_node, sizeof(*cache->ic_node));
1111d8bbc46Sriastradh 	}
1121d8bbc46Sriastradh 	kmem_free(cache, sizeof(*cache));
1131d8bbc46Sriastradh }
11477b5597aSriastradh 
11577b5597aSriastradh int
linux_idr_module_init(void)11677b5597aSriastradh linux_idr_module_init(void)
11777b5597aSriastradh {
1181d8bbc46Sriastradh 	int error;
11977b5597aSriastradh 
1201d8bbc46Sriastradh 	error = lwp_specific_key_create(&idr_cache_key, &idr_cache_dtor);
1211d8bbc46Sriastradh 	if (error)
1221d8bbc46Sriastradh 		return error;
1231d8bbc46Sriastradh 
12477b5597aSriastradh 	return 0;
12577b5597aSriastradh }
12677b5597aSriastradh 
12777b5597aSriastradh void
linux_idr_module_fini(void)12877b5597aSriastradh linux_idr_module_fini(void)
12977b5597aSriastradh {
13077b5597aSriastradh 
1311d8bbc46Sriastradh 	lwp_specific_key_delete(idr_cache_key);
13277b5597aSriastradh }
1336cb10275Sriastradh 
1346cb10275Sriastradh static signed int idr_tree_compare_nodes(void *, const void *, const void *);
1356cb10275Sriastradh static signed int idr_tree_compare_key(void *, const void *, const void *);
1366cb10275Sriastradh 
1376cb10275Sriastradh static const rb_tree_ops_t idr_rb_ops = {
1386cb10275Sriastradh 	.rbto_compare_nodes = &idr_tree_compare_nodes,
1396cb10275Sriastradh 	.rbto_compare_key = &idr_tree_compare_key,
1406cb10275Sriastradh 	.rbto_node_offset = offsetof(struct idr_node, in_rb_node),
1416cb10275Sriastradh 	.rbto_context = NULL,
1426cb10275Sriastradh };
1436cb10275Sriastradh 
1446cb10275Sriastradh static signed int
idr_tree_compare_nodes(void * ctx __unused,const void * na,const void * nb)1456cb10275Sriastradh idr_tree_compare_nodes(void *ctx __unused, const void *na, const void *nb)
1466cb10275Sriastradh {
1476cb10275Sriastradh 	const int a = ((const struct idr_node *)na)->in_index;
1486cb10275Sriastradh 	const int b = ((const struct idr_node *)nb)->in_index;
1496cb10275Sriastradh 
1506cb10275Sriastradh 	if (a < b)
1516cb10275Sriastradh 		return -1;
1526cb10275Sriastradh 	else if (b < a)
1536cb10275Sriastradh 		 return +1;
1546cb10275Sriastradh 	else
1556cb10275Sriastradh 		return 0;
1566cb10275Sriastradh }
1576cb10275Sriastradh 
1586cb10275Sriastradh static signed int
idr_tree_compare_key(void * ctx __unused,const void * n,const void * key)1596cb10275Sriastradh idr_tree_compare_key(void *ctx __unused, const void *n, const void *key)
1606cb10275Sriastradh {
1616cb10275Sriastradh 	const int a = ((const struct idr_node *)n)->in_index;
1626cb10275Sriastradh 	const int b = *(const int *)key;
1636cb10275Sriastradh 
1646cb10275Sriastradh 	if (a < b)
1656cb10275Sriastradh 		return -1;
1666cb10275Sriastradh 	else if (b < a)
1676cb10275Sriastradh 		return +1;
1686cb10275Sriastradh 	else
1696cb10275Sriastradh 		return 0;
1706cb10275Sriastradh }
1716cb10275Sriastradh 
1726cb10275Sriastradh void
idr_init(struct idr * idr)1736cb10275Sriastradh idr_init(struct idr *idr)
1746cb10275Sriastradh {
1756cb10275Sriastradh 
17692c79c76Sriastradh 	idr_init_base(idr, 0);
17792c79c76Sriastradh }
17892c79c76Sriastradh 
17992c79c76Sriastradh void
idr_init_base(struct idr * idr,int base)18092c79c76Sriastradh idr_init_base(struct idr *idr, int base)
18192c79c76Sriastradh {
18292c79c76Sriastradh 
18332b977d2Smrg 	mutex_init(&idr->idr_lock, MUTEX_DEFAULT, IPL_VM);
1846cb10275Sriastradh 	rb_tree_init(&idr->idr_tree, &idr_rb_ops);
18592c79c76Sriastradh 	idr->idr_base = base;
18692c79c76Sriastradh 
1875248a24cSriastradh 	SDT_PROBE1(sdt, linux, idr, init,  idr);
1886cb10275Sriastradh }
1896cb10275Sriastradh 
1906cb10275Sriastradh void
idr_destroy(struct idr * idr)1916cb10275Sriastradh idr_destroy(struct idr *idr)
1926cb10275Sriastradh {
1936cb10275Sriastradh 
1945248a24cSriastradh 	SDT_PROBE1(sdt, linux, idr, destroy,  idr);
1956cb10275Sriastradh #if 0				/* XXX No rb_tree_destroy?  */
1966cb10275Sriastradh 	rb_tree_destroy(&idr->idr_tree);
1976cb10275Sriastradh #endif
1986cb10275Sriastradh 	mutex_destroy(&idr->idr_lock);
1996cb10275Sriastradh }
2006cb10275Sriastradh 
20177b5597aSriastradh bool
idr_is_empty(struct idr * idr)20277b5597aSriastradh idr_is_empty(struct idr *idr)
20377b5597aSriastradh {
20477b5597aSriastradh 
20577b5597aSriastradh 	return (RB_TREE_MIN(&idr->idr_tree) == NULL);
20677b5597aSriastradh }
20777b5597aSriastradh 
2086cb10275Sriastradh void *
idr_find(struct idr * idr,int id)2096cb10275Sriastradh idr_find(struct idr *idr, int id)
2106cb10275Sriastradh {
2116cb10275Sriastradh 	const struct idr_node *node;
2126cb10275Sriastradh 	void *data;
2136cb10275Sriastradh 
2146cb10275Sriastradh 	mutex_spin_enter(&idr->idr_lock);
2156cb10275Sriastradh 	node = rb_tree_find_node(&idr->idr_tree, &id);
2166cb10275Sriastradh 	data = (node == NULL? NULL : node->in_data);
2176cb10275Sriastradh 	mutex_spin_exit(&idr->idr_lock);
2186cb10275Sriastradh 
2196cb10275Sriastradh 	return data;
2206cb10275Sriastradh }
2216cb10275Sriastradh 
2226cb10275Sriastradh void *
idr_get_next(struct idr * idr,int * idp)22377a04540Sriastradh idr_get_next(struct idr *idr, int *idp)
22477a04540Sriastradh {
22577a04540Sriastradh 	const struct idr_node *node;
22677a04540Sriastradh 	void *data;
22777a04540Sriastradh 
22877a04540Sriastradh 	mutex_spin_enter(&idr->idr_lock);
22977a04540Sriastradh 	node = rb_tree_find_node_geq(&idr->idr_tree, idp);
23077a04540Sriastradh 	if (node == NULL) {
23177a04540Sriastradh 		data = NULL;
23277a04540Sriastradh 	} else {
23377a04540Sriastradh 		data = node->in_data;
23477a04540Sriastradh 		*idp = node->in_index;
23577a04540Sriastradh 	}
23677a04540Sriastradh 	mutex_spin_exit(&idr->idr_lock);
23777a04540Sriastradh 
23877a04540Sriastradh 	return data;
23977a04540Sriastradh }
24077a04540Sriastradh 
24177a04540Sriastradh void *
idr_replace(struct idr * idr,void * replacement,int id)2426cb10275Sriastradh idr_replace(struct idr *idr, void *replacement, int id)
2436cb10275Sriastradh {
2446cb10275Sriastradh 	struct idr_node *node;
2456cb10275Sriastradh 	void *result;
2466cb10275Sriastradh 
2476cb10275Sriastradh 	mutex_spin_enter(&idr->idr_lock);
2486cb10275Sriastradh 	node = rb_tree_find_node(&idr->idr_tree, &id);
2496cb10275Sriastradh 	if (node == NULL) {
2506cb10275Sriastradh 		result = ERR_PTR(-ENOENT);
2516cb10275Sriastradh 	} else {
2526cb10275Sriastradh 		result = node->in_data;
2536cb10275Sriastradh 		node->in_data = replacement;
2545248a24cSriastradh 		SDT_PROBE4(sdt, linux, idr, replace,
2555248a24cSriastradh 		    idr, id, result, replacement);
2566cb10275Sriastradh 	}
2576cb10275Sriastradh 	mutex_spin_exit(&idr->idr_lock);
2586cb10275Sriastradh 
2596cb10275Sriastradh 	return result;
2606cb10275Sriastradh }
2616cb10275Sriastradh 
262f14e25d8Sriastradh void *
idr_remove(struct idr * idr,int id)2636cb10275Sriastradh idr_remove(struct idr *idr, int id)
2646cb10275Sriastradh {
2656cb10275Sriastradh 	struct idr_node *node;
266f14e25d8Sriastradh 	void *data;
2676cb10275Sriastradh 
2686cb10275Sriastradh 	mutex_spin_enter(&idr->idr_lock);
2696cb10275Sriastradh 	node = rb_tree_find_node(&idr->idr_tree, &id);
270f14e25d8Sriastradh 	if (node == NULL) {
271f14e25d8Sriastradh 		data = NULL;
272f14e25d8Sriastradh 	} else {
273f14e25d8Sriastradh 		data = node->in_data;
274f14e25d8Sriastradh 		SDT_PROBE3(sdt, linux, idr, remove,  idr, id, data);
2756cb10275Sriastradh 		rb_tree_remove_node(&idr->idr_tree, node);
276f14e25d8Sriastradh 	}
2776cb10275Sriastradh 	mutex_spin_exit(&idr->idr_lock);
2781d8bbc46Sriastradh 
2791d8bbc46Sriastradh 	kmem_free(node, sizeof(*node));
280f14e25d8Sriastradh 
281f14e25d8Sriastradh 	return data;
2826cb10275Sriastradh }
2836cb10275Sriastradh 
2846cb10275Sriastradh void
idr_preload(gfp_t gfp)28577b5597aSriastradh idr_preload(gfp_t gfp)
2866cb10275Sriastradh {
2871d8bbc46Sriastradh 	struct idr_cache *cache;
2886cb10275Sriastradh 	struct idr_node *node;
2891d8bbc46Sriastradh 	km_flag_t kmflag = ISSET(gfp, __GFP_WAIT) ? KM_SLEEP : KM_NOSLEEP;
2906cb10275Sriastradh 
2915248a24cSriastradh 	SDT_PROBE0(sdt, linux, idr, preload);
2925248a24cSriastradh 
2931d8bbc46Sriastradh 	/* If caller asked to wait, we had better be sleepable.  */
29477b5597aSriastradh 	if (ISSET(gfp, __GFP_WAIT))
29577b5597aSriastradh 		ASSERT_SLEEPABLE();
29677b5597aSriastradh 
2971d8bbc46Sriastradh 	/*
2981d8bbc46Sriastradh 	 * Get the current lwp's private idr cache.
2991d8bbc46Sriastradh 	 */
3001d8bbc46Sriastradh 	cache = lwp_getspecific(idr_cache_key);
3011d8bbc46Sriastradh 	if (cache == NULL) {
3021d8bbc46Sriastradh 		/* lwp_setspecific must be sleepable.  */
3031d8bbc46Sriastradh 		if (!ISSET(gfp, __GFP_WAIT))
3041d8bbc46Sriastradh 			return;
3054f49d672Sriastradh 		cache = kmem_zalloc(sizeof(*cache), kmflag);
3061d8bbc46Sriastradh 		if (cache == NULL)
3071d8bbc46Sriastradh 			return;
3081d8bbc46Sriastradh 		lwp_setspecific(idr_cache_key, cache);
3091d8bbc46Sriastradh 	}
3101d8bbc46Sriastradh 
3111d8bbc46Sriastradh 	/*
3121d8bbc46Sriastradh 	 * If there already is a node, a prior call to idr_preload must
3131d8bbc46Sriastradh 	 * not have been matched by idr_preload_end.  Print a warning,
3141d8bbc46Sriastradh 	 * claim the node, and record our return address for where this
3151d8bbc46Sriastradh 	 * node came from so the next leak is attributed to us.
3161d8bbc46Sriastradh 	 */
3171d8bbc46Sriastradh 	if (cache->ic_node) {
3181d8bbc46Sriastradh 		idr_cache_warning(cache);
3191d8bbc46Sriastradh 		goto out;
3201d8bbc46Sriastradh 	}
3211d8bbc46Sriastradh 
3221d8bbc46Sriastradh 	/*
3231d8bbc46Sriastradh 	 * No cached node.  Allocate a new one, store it in the cache,
3241d8bbc46Sriastradh 	 * and record our return address for where this node came from
3251d8bbc46Sriastradh 	 * so the next leak is attributed to us.
3261d8bbc46Sriastradh 	 */
3271d8bbc46Sriastradh 	node = kmem_alloc(sizeof(*node), kmflag);
32806a27a01Sriastradh 	KASSERT(node != NULL || !ISSET(gfp, __GFP_WAIT));
32977b5597aSriastradh 	if (node == NULL)
33077b5597aSriastradh 		return;
33177b5597aSriastradh 
3321d8bbc46Sriastradh 	cache->ic_node = node;
3331d8bbc46Sriastradh out:	cache->ic_where = __builtin_return_address(0);
3346cb10275Sriastradh }
3356cb10275Sriastradh 
3366cb10275Sriastradh int
idr_alloc(struct idr * idr,void * data,int start,int end,gfp_t gfp)33777b5597aSriastradh idr_alloc(struct idr *idr, void *data, int start, int end, gfp_t gfp)
3386cb10275Sriastradh {
33977b5597aSriastradh 	int maximum = (end <= 0? INT_MAX : (end - 1));
3401d8bbc46Sriastradh 	struct idr_cache *cache;
34177b5597aSriastradh 	struct idr_node *node, *search, *collision __diagused;
34277b5597aSriastradh 	int id = start;
3436cb10275Sriastradh 
34477b5597aSriastradh 	/* Sanity-check inputs.  */
34577b5597aSriastradh 	if (ISSET(gfp, __GFP_WAIT))
34677b5597aSriastradh 		ASSERT_SLEEPABLE();
34777b5597aSriastradh 	if (__predict_false(start < 0))
34877b5597aSriastradh 		return -EINVAL;
34977b5597aSriastradh 	if (__predict_false(maximum < start))
35077b5597aSriastradh 		return -ENOSPC;
35177b5597aSriastradh 
3521d8bbc46Sriastradh 	/*
3531d8bbc46Sriastradh 	 * Grab a node allocated by idr_preload, if we have a cache and
3541d8bbc46Sriastradh 	 * it is populated.
3551d8bbc46Sriastradh 	 */
3561d8bbc46Sriastradh 	cache = lwp_getspecific(idr_cache_key);
3571d8bbc46Sriastradh 	if (cache == NULL || cache->ic_node == NULL)
35806a27a01Sriastradh 		return -ENOMEM;
3591d8bbc46Sriastradh 	node = cache->ic_node;
3601d8bbc46Sriastradh 	cache->ic_node = NULL;
36177b5597aSriastradh 
36277b5597aSriastradh 	/* Find an id.  */
3636cb10275Sriastradh 	mutex_spin_enter(&idr->idr_lock);
36477b5597aSriastradh 	search = rb_tree_find_node_geq(&idr->idr_tree, &start);
36577b5597aSriastradh 	while ((search != NULL) && (search->in_index == id)) {
36677b5597aSriastradh 		if (maximum <= id) {
36777b5597aSriastradh 			id = -ENOSPC;
3686cb10275Sriastradh 			goto out;
3696cb10275Sriastradh 		}
3706cb10275Sriastradh 		search = rb_tree_iterate(&idr->idr_tree, search, RB_DIR_RIGHT);
37177b5597aSriastradh 		id++;
3726cb10275Sriastradh 	}
37377b5597aSriastradh 	node->in_index = id;
3746cb10275Sriastradh 	node->in_data = data;
3756cb10275Sriastradh 	collision = rb_tree_insert_node(&idr->idr_tree, node);
3766cb10275Sriastradh 	KASSERT(collision == node);
3776cb10275Sriastradh out:	mutex_spin_exit(&idr->idr_lock);
37877b5597aSriastradh 
37977b5597aSriastradh 	/* Discard the node on failure.  */
380077afc9aSriastradh 	if (id < 0) {
3811d8bbc46Sriastradh 		cache->ic_node = node;
382077afc9aSriastradh 	} else {
3835248a24cSriastradh 		SDT_PROBE3(sdt, linux, idr, alloc,  idr, id, data);
384077afc9aSriastradh 	}
38577b5597aSriastradh 	return id;
38677b5597aSriastradh }
38777b5597aSriastradh 
38877b5597aSriastradh void
idr_preload_end(void)38977b5597aSriastradh idr_preload_end(void)
39077b5597aSriastradh {
3911d8bbc46Sriastradh 	struct idr_cache *cache;
39277b5597aSriastradh 
39371b0b921Sriastradh 	SDT_PROBE0(sdt, linux, idr, preload__end);
3945248a24cSriastradh 
3951d8bbc46Sriastradh 	/* Get the cache, or bail if it's not there.  */
3961d8bbc46Sriastradh 	cache = lwp_getspecific(idr_cache_key);
3971d8bbc46Sriastradh 	if (cache == NULL)
3981d8bbc46Sriastradh 		return;
39977b5597aSriastradh 
4001d8bbc46Sriastradh 	/*
4011d8bbc46Sriastradh 	 * If there is a node, either because we didn't idr_alloc or
4021d8bbc46Sriastradh 	 * because idr_alloc failed, chuck it.
4031d8bbc46Sriastradh 	 *
4041d8bbc46Sriastradh 	 * XXX If we are not sleepable, then while the caller may have
4051d8bbc46Sriastradh 	 * used idr_preload(GFP_ATOMIC), kmem_free may still sleep.
4061d8bbc46Sriastradh 	 * What to do?
4071d8bbc46Sriastradh 	 */
4081d8bbc46Sriastradh 	if (cache->ic_node) {
4091d8bbc46Sriastradh 		struct idr_node *node;
4101d8bbc46Sriastradh 
4111d8bbc46Sriastradh 		node = cache->ic_node;
4121d8bbc46Sriastradh 		cache->ic_node = NULL;
4131d8bbc46Sriastradh 		cache->ic_where = NULL;
4141d8bbc46Sriastradh 
4151d8bbc46Sriastradh 		kmem_free(node, sizeof(*node));
41677b5597aSriastradh 	}
4176cb10275Sriastradh }
4186cb10275Sriastradh 
4196cb10275Sriastradh int
idr_for_each(struct idr * idr,int (* proc)(int,void *,void *),void * arg)4206cb10275Sriastradh idr_for_each(struct idr *idr, int (*proc)(int, void *, void *), void *arg)
4216cb10275Sriastradh {
4226cb10275Sriastradh 	struct idr_node *node;
4236cb10275Sriastradh 	int error = 0;
4246cb10275Sriastradh 
4256cb10275Sriastradh 	/* XXX Caller must exclude modifications.  */
4266cb10275Sriastradh 	RB_TREE_FOREACH(node, &idr->idr_tree) {
4276cb10275Sriastradh 		error = (*proc)(node->in_index, node->in_data, arg);
4286cb10275Sriastradh 		if (error)
4296cb10275Sriastradh 			break;
4306cb10275Sriastradh 	}
4316cb10275Sriastradh 
4326cb10275Sriastradh 	return error;
4336cb10275Sriastradh }
434