Home | History | Annotate | Line # | Download | only in linux
      1  1.15  riastrad /*	$NetBSD: linux_idr.c,v 1.15 2021/12/19 12:21:02 riastradh Exp $	*/
      2   1.2  riastrad 
      3   1.2  riastrad /*-
      4   1.2  riastrad  * Copyright (c) 2013 The NetBSD Foundation, Inc.
      5   1.2  riastrad  * All rights reserved.
      6   1.2  riastrad  *
      7   1.2  riastrad  * This code is derived from software contributed to The NetBSD Foundation
      8   1.2  riastrad  * by Taylor R. Campbell.
      9   1.2  riastrad  *
     10   1.2  riastrad  * Redistribution and use in source and binary forms, with or without
     11   1.2  riastrad  * modification, are permitted provided that the following conditions
     12   1.2  riastrad  * are met:
     13   1.2  riastrad  * 1. Redistributions of source code must retain the above copyright
     14   1.2  riastrad  *    notice, this list of conditions and the following disclaimer.
     15   1.2  riastrad  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.2  riastrad  *    notice, this list of conditions and the following disclaimer in the
     17   1.2  riastrad  *    documentation and/or other materials provided with the distribution.
     18   1.2  riastrad  *
     19   1.2  riastrad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20   1.2  riastrad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.2  riastrad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.2  riastrad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23   1.2  riastrad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24   1.2  riastrad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25   1.2  riastrad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26   1.2  riastrad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.2  riastrad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.2  riastrad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.2  riastrad  * POSSIBILITY OF SUCH DAMAGE.
     30   1.2  riastrad  */
     31   1.2  riastrad 
     32   1.2  riastrad #include <sys/cdefs.h>
     33  1.15  riastrad __KERNEL_RCSID(0, "$NetBSD: linux_idr.c,v 1.15 2021/12/19 12:21:02 riastradh Exp $");
     34   1.2  riastrad 
     35   1.2  riastrad #include <sys/param.h>
     36   1.2  riastrad #include <sys/atomic.h>
     37   1.2  riastrad #include <sys/rbtree.h>
     38  1.10  riastrad #include <sys/sdt.h>
     39   1.2  riastrad 
     40   1.2  riastrad #include <linux/err.h>
     41   1.2  riastrad #include <linux/idr.h>
     42   1.3  riastrad #include <linux/slab.h>
     43   1.2  riastrad 
     44   1.8  riastrad #ifdef _KERNEL_OPT
     45   1.8  riastrad #include "opt_ddb.h"
     46   1.8  riastrad #endif
     47   1.8  riastrad 
     48   1.8  riastrad #ifdef DDB
     49   1.8  riastrad #include <ddb/ddb.h>
     50   1.8  riastrad #endif
     51   1.8  riastrad 
     52   1.2  riastrad struct idr_node {
     53   1.3  riastrad 	rb_node_t		in_rb_node;
     54   1.3  riastrad 	int			in_index;
     55   1.3  riastrad 	void			*in_data;
     56   1.2  riastrad };
     57   1.3  riastrad 
     58   1.8  riastrad struct idr_cache {
     59   1.8  riastrad 	struct idr_node		*ic_node;
     60   1.8  riastrad 	void			*ic_where;
     61   1.8  riastrad };
     62   1.8  riastrad 
     63  1.10  riastrad SDT_PROBE_DEFINE0(sdt, linux, idr, leak);
     64  1.10  riastrad SDT_PROBE_DEFINE1(sdt, linux, idr, init, "struct idr *"/*idr*/);
     65  1.10  riastrad SDT_PROBE_DEFINE1(sdt, linux, idr, destroy, "struct idr *"/*idr*/);
     66  1.10  riastrad SDT_PROBE_DEFINE4(sdt, linux, idr, replace,
     67  1.10  riastrad     "struct idr *"/*idr*/, "int"/*id*/, "void *"/*odata*/, "void *"/*ndata*/);
     68  1.10  riastrad SDT_PROBE_DEFINE3(sdt, linux, idr, remove,
     69  1.10  riastrad     "struct idr *"/*idr*/, "int"/*id*/, "void *"/*data*/);
     70  1.10  riastrad SDT_PROBE_DEFINE0(sdt, linux, idr, preload);
     71  1.12  riastrad SDT_PROBE_DEFINE0(sdt, linux, idr, preload__end);
     72  1.10  riastrad SDT_PROBE_DEFINE3(sdt, linux, idr, alloc,
     73  1.10  riastrad     "struct idr *"/*idr*/, "int"/*id*/, "void *"/*data*/);
     74  1.10  riastrad 
     75   1.8  riastrad static specificdata_key_t idr_cache_key __read_mostly;
     76   1.8  riastrad 
     77   1.8  riastrad static void
     78   1.8  riastrad idr_cache_warning(struct idr_cache *cache)
     79   1.8  riastrad {
     80   1.8  riastrad #ifdef DDB
     81   1.8  riastrad 	const char *name;
     82   1.8  riastrad 	db_expr_t offset;
     83   1.8  riastrad #endif
     84   1.8  riastrad 
     85   1.8  riastrad 	KASSERT(cache->ic_node != NULL);
     86   1.8  riastrad 
     87   1.8  riastrad #ifdef DDB
     88   1.8  riastrad 	db_find_sym_and_offset((db_addr_t)(uintptr_t)cache->ic_where,
     89   1.8  riastrad 	    &name, &offset);
     90   1.8  riastrad 	if (name) {
     91   1.8  riastrad 		printf("WARNING: idr preload at %s+%#"DDB_EXPR_FMT"x"
     92   1.8  riastrad 		    " leaked in lwp %s @ %p\n",
     93   1.8  riastrad 		    name, offset, curlwp->l_name, curlwp);
     94   1.8  riastrad 	} else
     95   1.8  riastrad #endif
     96   1.8  riastrad 	{
     97   1.8  riastrad 		printf("WARNING: idr preload at %p leaked in lwp %s @ %p\n",
     98   1.8  riastrad 		    cache->ic_where, curlwp->l_name, curlwp);
     99   1.8  riastrad 	}
    100   1.8  riastrad }
    101   1.8  riastrad 
    102   1.8  riastrad static void
    103   1.8  riastrad idr_cache_dtor(void *cookie)
    104   1.8  riastrad {
    105   1.8  riastrad 	struct idr_cache *cache = cookie;
    106   1.8  riastrad 
    107   1.8  riastrad 	if (cache->ic_node) {
    108  1.10  riastrad 		SDT_PROBE0(sdt, linux, idr, leak);
    109   1.8  riastrad 		idr_cache_warning(cache);
    110   1.8  riastrad 		kmem_free(cache->ic_node, sizeof(*cache->ic_node));
    111   1.8  riastrad 	}
    112   1.8  riastrad 	kmem_free(cache, sizeof(*cache));
    113   1.8  riastrad }
    114   1.3  riastrad 
    115   1.3  riastrad int
    116   1.3  riastrad linux_idr_module_init(void)
    117   1.3  riastrad {
    118   1.8  riastrad 	int error;
    119   1.8  riastrad 
    120   1.8  riastrad 	error = lwp_specific_key_create(&idr_cache_key, &idr_cache_dtor);
    121   1.8  riastrad 	if (error)
    122   1.8  riastrad 		return error;
    123   1.3  riastrad 
    124   1.3  riastrad 	return 0;
    125   1.3  riastrad }
    126   1.3  riastrad 
    127   1.3  riastrad void
    128   1.3  riastrad linux_idr_module_fini(void)
    129   1.3  riastrad {
    130   1.3  riastrad 
    131   1.8  riastrad 	lwp_specific_key_delete(idr_cache_key);
    132   1.3  riastrad }
    133   1.2  riastrad 
    134   1.2  riastrad static signed int idr_tree_compare_nodes(void *, const void *, const void *);
    135   1.2  riastrad static signed int idr_tree_compare_key(void *, const void *, const void *);
    136   1.2  riastrad 
    137   1.2  riastrad static const rb_tree_ops_t idr_rb_ops = {
    138   1.2  riastrad 	.rbto_compare_nodes = &idr_tree_compare_nodes,
    139   1.2  riastrad 	.rbto_compare_key = &idr_tree_compare_key,
    140   1.2  riastrad 	.rbto_node_offset = offsetof(struct idr_node, in_rb_node),
    141   1.2  riastrad 	.rbto_context = NULL,
    142   1.2  riastrad };
    143   1.2  riastrad 
    144   1.2  riastrad static signed int
    145   1.2  riastrad idr_tree_compare_nodes(void *ctx __unused, const void *na, const void *nb)
    146   1.2  riastrad {
    147   1.2  riastrad 	const int a = ((const struct idr_node *)na)->in_index;
    148   1.2  riastrad 	const int b = ((const struct idr_node *)nb)->in_index;
    149   1.2  riastrad 
    150   1.2  riastrad 	if (a < b)
    151   1.2  riastrad 		return -1;
    152   1.2  riastrad 	else if (b < a)
    153   1.2  riastrad 		 return +1;
    154   1.2  riastrad 	else
    155   1.2  riastrad 		return 0;
    156   1.2  riastrad }
    157   1.2  riastrad 
    158   1.2  riastrad static signed int
    159   1.2  riastrad idr_tree_compare_key(void *ctx __unused, const void *n, const void *key)
    160   1.2  riastrad {
    161   1.2  riastrad 	const int a = ((const struct idr_node *)n)->in_index;
    162   1.2  riastrad 	const int b = *(const int *)key;
    163   1.2  riastrad 
    164   1.2  riastrad 	if (a < b)
    165   1.2  riastrad 		return -1;
    166   1.2  riastrad 	else if (b < a)
    167   1.2  riastrad 		return +1;
    168   1.2  riastrad 	else
    169   1.2  riastrad 		return 0;
    170   1.2  riastrad }
    171   1.2  riastrad 
    172   1.2  riastrad void
    173   1.2  riastrad idr_init(struct idr *idr)
    174   1.2  riastrad {
    175   1.2  riastrad 
    176  1.13  riastrad 	idr_init_base(idr, 0);
    177  1.13  riastrad }
    178  1.13  riastrad 
    179  1.13  riastrad void
    180  1.13  riastrad idr_init_base(struct idr *idr, int base)
    181  1.13  riastrad {
    182  1.13  riastrad 
    183   1.5       mrg 	mutex_init(&idr->idr_lock, MUTEX_DEFAULT, IPL_VM);
    184   1.2  riastrad 	rb_tree_init(&idr->idr_tree, &idr_rb_ops);
    185  1.13  riastrad 	idr->idr_base = base;
    186  1.13  riastrad 
    187  1.10  riastrad 	SDT_PROBE1(sdt, linux, idr, init,  idr);
    188   1.2  riastrad }
    189   1.2  riastrad 
    190   1.2  riastrad void
    191   1.2  riastrad idr_destroy(struct idr *idr)
    192   1.2  riastrad {
    193   1.2  riastrad 
    194  1.10  riastrad 	SDT_PROBE1(sdt, linux, idr, destroy,  idr);
    195   1.2  riastrad #if 0				/* XXX No rb_tree_destroy?  */
    196   1.2  riastrad 	rb_tree_destroy(&idr->idr_tree);
    197   1.2  riastrad #endif
    198   1.2  riastrad 	mutex_destroy(&idr->idr_lock);
    199   1.2  riastrad }
    200   1.2  riastrad 
    201   1.3  riastrad bool
    202   1.3  riastrad idr_is_empty(struct idr *idr)
    203   1.3  riastrad {
    204   1.3  riastrad 
    205   1.3  riastrad 	return (RB_TREE_MIN(&idr->idr_tree) == NULL);
    206   1.3  riastrad }
    207   1.3  riastrad 
    208   1.2  riastrad void *
    209   1.2  riastrad idr_find(struct idr *idr, int id)
    210   1.2  riastrad {
    211   1.2  riastrad 	const struct idr_node *node;
    212   1.2  riastrad 	void *data;
    213   1.2  riastrad 
    214   1.2  riastrad 	mutex_spin_enter(&idr->idr_lock);
    215   1.2  riastrad 	node = rb_tree_find_node(&idr->idr_tree, &id);
    216   1.2  riastrad 	data = (node == NULL? NULL : node->in_data);
    217   1.2  riastrad 	mutex_spin_exit(&idr->idr_lock);
    218   1.2  riastrad 
    219   1.2  riastrad 	return data;
    220   1.2  riastrad }
    221   1.2  riastrad 
    222   1.2  riastrad void *
    223   1.7  riastrad idr_get_next(struct idr *idr, int *idp)
    224   1.7  riastrad {
    225   1.7  riastrad 	const struct idr_node *node;
    226   1.7  riastrad 	void *data;
    227   1.7  riastrad 
    228   1.7  riastrad 	mutex_spin_enter(&idr->idr_lock);
    229   1.7  riastrad 	node = rb_tree_find_node_geq(&idr->idr_tree, idp);
    230   1.7  riastrad 	if (node == NULL) {
    231   1.7  riastrad 		data = NULL;
    232   1.7  riastrad 	} else {
    233   1.7  riastrad 		data = node->in_data;
    234   1.7  riastrad 		*idp = node->in_index;
    235   1.7  riastrad 	}
    236   1.7  riastrad 	mutex_spin_exit(&idr->idr_lock);
    237   1.7  riastrad 
    238   1.7  riastrad 	return data;
    239   1.7  riastrad }
    240   1.7  riastrad 
    241   1.7  riastrad void *
    242   1.2  riastrad idr_replace(struct idr *idr, void *replacement, int id)
    243   1.2  riastrad {
    244   1.2  riastrad 	struct idr_node *node;
    245   1.2  riastrad 	void *result;
    246   1.2  riastrad 
    247   1.2  riastrad 	mutex_spin_enter(&idr->idr_lock);
    248   1.2  riastrad 	node = rb_tree_find_node(&idr->idr_tree, &id);
    249   1.2  riastrad 	if (node == NULL) {
    250   1.2  riastrad 		result = ERR_PTR(-ENOENT);
    251   1.2  riastrad 	} else {
    252   1.2  riastrad 		result = node->in_data;
    253   1.2  riastrad 		node->in_data = replacement;
    254  1.10  riastrad 		SDT_PROBE4(sdt, linux, idr, replace,
    255  1.10  riastrad 		    idr, id, result, replacement);
    256   1.2  riastrad 	}
    257   1.2  riastrad 	mutex_spin_exit(&idr->idr_lock);
    258   1.2  riastrad 
    259   1.2  riastrad 	return result;
    260   1.2  riastrad }
    261   1.2  riastrad 
    262  1.14  riastrad void *
    263   1.2  riastrad idr_remove(struct idr *idr, int id)
    264   1.2  riastrad {
    265   1.2  riastrad 	struct idr_node *node;
    266  1.14  riastrad 	void *data;
    267   1.2  riastrad 
    268   1.2  riastrad 	mutex_spin_enter(&idr->idr_lock);
    269   1.2  riastrad 	node = rb_tree_find_node(&idr->idr_tree, &id);
    270  1.14  riastrad 	if (node == NULL) {
    271  1.14  riastrad 		data = NULL;
    272  1.14  riastrad 	} else {
    273  1.14  riastrad 		data = node->in_data;
    274  1.14  riastrad 		SDT_PROBE3(sdt, linux, idr, remove,  idr, id, data);
    275  1.14  riastrad 		rb_tree_remove_node(&idr->idr_tree, node);
    276  1.14  riastrad 	}
    277   1.2  riastrad 	mutex_spin_exit(&idr->idr_lock);
    278   1.8  riastrad 
    279   1.8  riastrad 	kmem_free(node, sizeof(*node));
    280  1.14  riastrad 
    281  1.14  riastrad 	return data;
    282   1.2  riastrad }
    283   1.2  riastrad 
    284   1.2  riastrad void
    285   1.3  riastrad idr_preload(gfp_t gfp)
    286   1.2  riastrad {
    287   1.8  riastrad 	struct idr_cache *cache;
    288   1.2  riastrad 	struct idr_node *node;
    289   1.8  riastrad 	km_flag_t kmflag = ISSET(gfp, __GFP_WAIT) ? KM_SLEEP : KM_NOSLEEP;
    290   1.2  riastrad 
    291  1.10  riastrad 	SDT_PROBE0(sdt, linux, idr, preload);
    292  1.10  riastrad 
    293   1.8  riastrad 	/* If caller asked to wait, we had better be sleepable.  */
    294   1.3  riastrad 	if (ISSET(gfp, __GFP_WAIT))
    295   1.3  riastrad 		ASSERT_SLEEPABLE();
    296   1.2  riastrad 
    297   1.8  riastrad 	/*
    298   1.8  riastrad 	 * Get the current lwp's private idr cache.
    299   1.8  riastrad 	 */
    300   1.8  riastrad 	cache = lwp_getspecific(idr_cache_key);
    301   1.8  riastrad 	if (cache == NULL) {
    302   1.8  riastrad 		/* lwp_setspecific must be sleepable.  */
    303   1.8  riastrad 		if (!ISSET(gfp, __GFP_WAIT))
    304   1.8  riastrad 			return;
    305   1.9  riastrad 		cache = kmem_zalloc(sizeof(*cache), kmflag);
    306   1.8  riastrad 		if (cache == NULL)
    307   1.8  riastrad 			return;
    308   1.8  riastrad 		lwp_setspecific(idr_cache_key, cache);
    309   1.8  riastrad 	}
    310   1.8  riastrad 
    311   1.8  riastrad 	/*
    312   1.8  riastrad 	 * If there already is a node, a prior call to idr_preload must
    313   1.8  riastrad 	 * not have been matched by idr_preload_end.  Print a warning,
    314   1.8  riastrad 	 * claim the node, and record our return address for where this
    315   1.8  riastrad 	 * node came from so the next leak is attributed to us.
    316   1.8  riastrad 	 */
    317   1.8  riastrad 	if (cache->ic_node) {
    318   1.8  riastrad 		idr_cache_warning(cache);
    319   1.8  riastrad 		goto out;
    320   1.8  riastrad 	}
    321   1.8  riastrad 
    322   1.8  riastrad 	/*
    323   1.8  riastrad 	 * No cached node.  Allocate a new one, store it in the cache,
    324   1.8  riastrad 	 * and record our return address for where this node came from
    325   1.8  riastrad 	 * so the next leak is attributed to us.
    326   1.8  riastrad 	 */
    327   1.8  riastrad 	node = kmem_alloc(sizeof(*node), kmflag);
    328   1.6  riastrad 	KASSERT(node != NULL || !ISSET(gfp, __GFP_WAIT));
    329   1.3  riastrad 	if (node == NULL)
    330   1.3  riastrad 		return;
    331   1.3  riastrad 
    332   1.8  riastrad 	cache->ic_node = node;
    333   1.8  riastrad out:	cache->ic_where = __builtin_return_address(0);
    334   1.2  riastrad }
    335   1.2  riastrad 
    336   1.2  riastrad int
    337   1.3  riastrad idr_alloc(struct idr *idr, void *data, int start, int end, gfp_t gfp)
    338   1.2  riastrad {
    339   1.3  riastrad 	int maximum = (end <= 0? INT_MAX : (end - 1));
    340   1.8  riastrad 	struct idr_cache *cache;
    341   1.3  riastrad 	struct idr_node *node, *search, *collision __diagused;
    342   1.3  riastrad 	int id = start;
    343   1.3  riastrad 
    344   1.3  riastrad 	/* Sanity-check inputs.  */
    345   1.3  riastrad 	if (ISSET(gfp, __GFP_WAIT))
    346   1.3  riastrad 		ASSERT_SLEEPABLE();
    347   1.3  riastrad 	if (__predict_false(start < 0))
    348   1.3  riastrad 		return -EINVAL;
    349   1.3  riastrad 	if (__predict_false(maximum < start))
    350   1.3  riastrad 		return -ENOSPC;
    351   1.3  riastrad 
    352   1.8  riastrad 	/*
    353   1.8  riastrad 	 * Grab a node allocated by idr_preload, if we have a cache and
    354   1.8  riastrad 	 * it is populated.
    355   1.8  riastrad 	 */
    356   1.8  riastrad 	cache = lwp_getspecific(idr_cache_key);
    357   1.8  riastrad 	if (cache == NULL || cache->ic_node == NULL)
    358   1.6  riastrad 		return -ENOMEM;
    359   1.8  riastrad 	node = cache->ic_node;
    360   1.8  riastrad 	cache->ic_node = NULL;
    361   1.2  riastrad 
    362   1.3  riastrad 	/* Find an id.  */
    363   1.2  riastrad 	mutex_spin_enter(&idr->idr_lock);
    364   1.3  riastrad 	search = rb_tree_find_node_geq(&idr->idr_tree, &start);
    365   1.3  riastrad 	while ((search != NULL) && (search->in_index == id)) {
    366   1.3  riastrad 		if (maximum <= id) {
    367   1.3  riastrad 			id = -ENOSPC;
    368   1.2  riastrad 			goto out;
    369   1.2  riastrad 		}
    370   1.2  riastrad 		search = rb_tree_iterate(&idr->idr_tree, search, RB_DIR_RIGHT);
    371   1.3  riastrad 		id++;
    372   1.2  riastrad 	}
    373   1.3  riastrad 	node->in_index = id;
    374   1.2  riastrad 	node->in_data = data;
    375   1.2  riastrad 	collision = rb_tree_insert_node(&idr->idr_tree, node);
    376   1.2  riastrad 	KASSERT(collision == node);
    377   1.3  riastrad out:	mutex_spin_exit(&idr->idr_lock);
    378   1.3  riastrad 
    379   1.8  riastrad 	/* Discard the node on failure.  */
    380  1.11  riastrad 	if (id < 0) {
    381   1.8  riastrad 		cache->ic_node = node;
    382  1.11  riastrad 	} else {
    383  1.10  riastrad 		SDT_PROBE3(sdt, linux, idr, alloc,  idr, id, data);
    384  1.11  riastrad 	}
    385   1.3  riastrad 	return id;
    386   1.3  riastrad }
    387   1.2  riastrad 
    388   1.3  riastrad void
    389   1.3  riastrad idr_preload_end(void)
    390   1.3  riastrad {
    391   1.8  riastrad 	struct idr_cache *cache;
    392   1.2  riastrad 
    393  1.12  riastrad 	SDT_PROBE0(sdt, linux, idr, preload__end);
    394  1.10  riastrad 
    395   1.8  riastrad 	/* Get the cache, or bail if it's not there.  */
    396   1.8  riastrad 	cache = lwp_getspecific(idr_cache_key);
    397   1.8  riastrad 	if (cache == NULL)
    398   1.8  riastrad 		return;
    399   1.8  riastrad 
    400   1.8  riastrad 	/*
    401   1.8  riastrad 	 * If there is a node, either because we didn't idr_alloc or
    402   1.8  riastrad 	 * because idr_alloc failed, chuck it.
    403   1.8  riastrad 	 *
    404   1.8  riastrad 	 * XXX If we are not sleepable, then while the caller may have
    405   1.8  riastrad 	 * used idr_preload(GFP_ATOMIC), kmem_free may still sleep.
    406   1.8  riastrad 	 * What to do?
    407   1.8  riastrad 	 */
    408   1.8  riastrad 	if (cache->ic_node) {
    409   1.8  riastrad 		struct idr_node *node;
    410   1.8  riastrad 
    411   1.8  riastrad 		node = cache->ic_node;
    412   1.8  riastrad 		cache->ic_node = NULL;
    413   1.8  riastrad 		cache->ic_where = NULL;
    414   1.3  riastrad 
    415   1.8  riastrad 		kmem_free(node, sizeof(*node));
    416   1.3  riastrad 	}
    417   1.2  riastrad }
    418   1.2  riastrad 
    419   1.2  riastrad int
    420   1.2  riastrad idr_for_each(struct idr *idr, int (*proc)(int, void *, void *), void *arg)
    421   1.2  riastrad {
    422   1.2  riastrad 	struct idr_node *node;
    423   1.2  riastrad 	int error = 0;
    424   1.2  riastrad 
    425   1.2  riastrad 	/* XXX Caller must exclude modifications.  */
    426   1.2  riastrad 	RB_TREE_FOREACH(node, &idr->idr_tree) {
    427   1.2  riastrad 		error = (*proc)(node->in_index, node->in_data, arg);
    428   1.2  riastrad 		if (error)
    429   1.2  riastrad 			break;
    430   1.2  riastrad 	}
    431   1.2  riastrad 
    432   1.2  riastrad 	return error;
    433   1.2  riastrad }
    434