⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 idr.c

📁 Lib files of linux kernel
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 2002-10-18  written by Jim Houston jim.houston@ccur.com *	Copyright (C) 2002 by Concurrent Computer Corporation *	Distributed under the GNU GPL license version 2. * * Modified by George Anzinger to reuse immediately and to use * find bit instructions.  Also removed _irq on spinlocks. * * Modified by Nadia Derbey to make it RCU safe. * * Small id to pointer translation service. * * It uses a radix tree like structure as a sparse array indexed * by the id to obtain the pointer.  The bitmap makes allocating * a new id quick. * * You call it to allocate an id (an int) an associate with that id a * pointer or what ever, we treat it as a (void *).  You can pass this * id to a user for him to pass back at a later time.  You then pass * that id to this code and it returns your pointer. * You can release ids at any time. When all ids are released, most of * the memory is returned (we keep IDR_FREE_MAX) in a local pool so we * don't need to go to the memory "store" during an id allocate, just * so you don't need to be too concerned about locking and conflicts * with the slab allocator. */#ifndef TEST                        // to test in user space...#include <linux/slab.h>#include <linux/init.h>#include <linux/module.h>#endif#include <linux/err.h>#include <linux/string.h>#include <linux/idr.h>static struct kmem_cache *idr_layer_cache;static struct idr_layer *get_from_free_list(struct idr *idp){	struct idr_layer *p;	unsigned long flags;	spin_lock_irqsave(&idp->lock, flags);	if ((p = idp->id_free)) {		idp->id_free = p->ary[0];		idp->id_free_cnt--;		p->ary[0] = NULL;	}	spin_unlock_irqrestore(&idp->lock, flags);	return(p);}static void idr_layer_rcu_free(struct rcu_head *head){	struct idr_layer *layer;	layer = container_of(head, struct idr_layer, rcu_head);	kmem_cache_free(idr_layer_cache, layer);}static inline void free_layer(struct idr_layer *p){	call_rcu(&p->rcu_head, idr_layer_rcu_free);}/* only called when idp->lock is held */static void __move_to_free_list(struct idr *idp, struct idr_layer *p){	p->ary[0] = idp->id_free;	idp->id_free = p;	idp->id_free_cnt++;}static void move_to_free_list(struct idr *idp, struct idr_layer *p){	unsigned long flags;	/*	 * Depends on the return element being zeroed.	 */	spin_lock_irqsave(&idp->lock, flags);	__move_to_free_list(idp, p);	spin_unlock_irqrestore(&idp->lock, flags);}static void idr_mark_full(struct idr_layer **pa, int id){	struct idr_layer *p = pa[0];	int l = 0;	__set_bit(id & IDR_MASK, &p->bitmap);	/*	 * If this layer is full mark the bit in the layer above to	 * show that this part of the radix tree is full.  This may	 * complete the layer above and require walking up the radix	 * tree.	 */	while (p->bitmap == IDR_FULL) {		if (!(p = pa[++l]))			break;		id = id >> IDR_BITS;		__set_bit((id & IDR_MASK), &p->bitmap);	}}/** * idr_pre_get - reserver resources for idr allocation * @idp:	idr handle * @gfp_mask:	memory allocation flags * * This function should be called prior to locking and calling the * idr_get_new* functions. It preallocates enough memory to satisfy * the worst possible allocation. * * If the system is REALLY out of memory this function returns 0, * otherwise 1. */int idr_pre_get(struct idr *idp, gfp_t gfp_mask){	while (idp->id_free_cnt < IDR_FREE_MAX) {		struct idr_layer *new;		new = kmem_cache_alloc(idr_layer_cache, gfp_mask);		if (new == NULL)			return (0);		move_to_free_list(idp, new);	}	return 1;}EXPORT_SYMBOL(idr_pre_get);static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa){	int n, m, sh;	struct idr_layer *p, *new;	int l, id, oid;	unsigned long bm;	id = *starting_id; restart:	p = idp->top;	l = idp->layers;	pa[l--] = NULL;	while (1) {		/*		 * We run around this while until we reach the leaf node...		 */		n = (id >> (IDR_BITS*l)) & IDR_MASK;		bm = ~p->bitmap;		m = find_next_bit(&bm, IDR_SIZE, n);		if (m == IDR_SIZE) {			/* no space available go back to previous layer. */			l++;			oid = id;			id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;			/* if already at the top layer, we need to grow */			if (!(p = pa[l])) {				*starting_id = id;				return IDR_NEED_TO_GROW;			}			/* If we need to go up one layer, continue the			 * loop; otherwise, restart from the top.			 */			sh = IDR_BITS * (l + 1);			if (oid >> sh == id >> sh)				continue;			else				goto restart;		}		if (m != n) {			sh = IDR_BITS*l;			id = ((id >> sh) ^ n ^ m) << sh;		}		if ((id >= MAX_ID_BIT) || (id < 0))			return IDR_NOMORE_SPACE;		if (l == 0)			break;		/*		 * Create the layer below if it is missing.		 */		if (!p->ary[m]) {			new = get_from_free_list(idp);			if (!new)				return -1;			rcu_assign_pointer(p->ary[m], new);			p->count++;		}		pa[l--] = p;		p = p->ary[m];	}	pa[l] = p;	return id;}static int idr_get_empty_slot(struct idr *idp, int starting_id,			      struct idr_layer **pa){	struct idr_layer *p, *new;	int layers, v, id;	unsigned long flags;	id = starting_id;build_up:	p = idp->top;	layers = idp->layers;	if (unlikely(!p)) {		if (!(p = get_from_free_list(idp)))			return -1;		layers = 1;	}	/*	 * Add a new layer to the top of the tree if the requested	 * id is larger than the currently allocated space.	 */	while ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) {		layers++;		if (!p->count)			continue;		if (!(new = get_from_free_list(idp))) {			/*			 * The allocation failed.  If we built part of			 * the structure tear it down.			 */			spin_lock_irqsave(&idp->lock, flags);			for (new = p; p && p != idp->top; new = p) {				p = p->ary[0];				new->ary[0] = NULL;				new->bitmap = new->count = 0;				__move_to_free_list(idp, new);			}			spin_unlock_irqrestore(&idp->lock, flags);			return -1;		}		new->ary[0] = p;		new->count = 1;		if (p->bitmap == IDR_FULL)			__set_bit(0, &new->bitmap);		p = new;	}	rcu_assign_pointer(idp->top, p);	idp->layers = layers;	v = sub_alloc(idp, &id, pa);	if (v == IDR_NEED_TO_GROW)		goto build_up;	return(v);}static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id){	struct idr_layer *pa[MAX_LEVEL];	int id;	id = idr_get_empty_slot(idp, starting_id, pa);	if (id >= 0) {		/*		 * Successfully found an empty slot.  Install the user		 * pointer and mark the slot full.		 */		rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],				(struct idr_layer *)ptr);		pa[0]->count++;		idr_mark_full(pa, id);	}	return id;}/** * idr_get_new_above - allocate new idr entry above or equal to a start id * @idp: idr handle * @ptr: pointer you want associated with the ide * @start_id: id to start search at * @id: pointer to the allocated handle * * This is the allocate id function.  It should be called with any * required locks. * * If memory is required, it will return -EAGAIN, you should unlock * and go back to the idr_pre_get() call.  If the idr is full, it will * return -ENOSPC. * * @id returns a value in the range 0 ... 0x7fffffff */int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id){	int rv;	rv = idr_get_new_above_int(idp, ptr, starting_id);	/*	 * This is a cheap hack until the IDR code can be fixed to	 * return proper error values.	 */	if (rv < 0)		return _idr_rc_to_errno(rv);	*id = rv;	return 0;}EXPORT_SYMBOL(idr_get_new_above);/** * idr_get_new - allocate new idr entry * @idp: idr handle * @ptr: pointer you want associated with the ide * @id: pointer to the allocated handle * * This is the allocate id function.  It should be called with any * required locks. * * If memory is required, it will return -EAGAIN, you should unlock * and go back to the idr_pre_get() call.  If the idr is full, it will * return -ENOSPC. * * @id returns a value in the range 0 ... 0x7fffffff */int idr_get_new(struct idr *idp, void *ptr, int *id){	int rv;	rv = idr_get_new_above_int(idp, ptr, 0);	/*	 * This is a cheap hack until the IDR code can be fixed to	 * return proper error values.	 */	if (rv < 0)		return _idr_rc_to_errno(rv);	*id = rv;	return 0;}EXPORT_SYMBOL(idr_get_new);static void idr_remove_warning(int id){	printk(KERN_WARNING		"idr_remove called for id=%d which is not allocated.\n", id);	dump_stack();}static void sub_remove(struct idr *idp, int shift, int id){	struct idr_layer *p = idp->top;	struct idr_layer **pa[MAX_LEVEL];	struct idr_layer ***paa = &pa[0];	struct idr_layer *to_free;	int n;	*paa = NULL;	*++paa = &idp->top;	while ((shift > 0) && p) {		n = (id >> shift) & IDR_MASK;		__clear_bit(n, &p->bitmap);		*++paa = &p->ary[n];		p = p->ary[n];		shift -= IDR_BITS;	}	n = id & IDR_MASK;	if (likely(p != NULL && test_bit(n, &p->bitmap))){		__clear_bit(n, &p->bitmap);		rcu_assign_pointer(p->ary[n], NULL);		to_free = NULL;		while(*paa && ! --((**paa)->count)){			if (to_free)				free_layer(to_free);			to_free = **paa;			**paa-- = NULL;		}		if (!*paa)			idp->layers = 0;		if (to_free)			free_layer(to_free);	} else		idr_remove_warning(id);}/** * idr_remove - remove the given id and free it's slot * @idp: idr handle * @id: unique key */void idr_remove(struct idr *idp, int id){	struct idr_layer *p;	struct idr_layer *to_free;	/* Mask off upper bits we don't use for the search. */	id &= MAX_ID_MASK;	sub_remove(idp, (idp->layers - 1) * IDR_BITS, id);	if (idp->top && idp->top->count == 1 && (idp->layers > 1) &&	    idp->top->ary[0]) {		/*		 * Single child at leftmost slot: we can shrink the tree.		 * This level is not needed anymore since when layers are		 * inserted, they are inserted at the top of the existing		 * tree.		 */		to_free = idp->top;		p = idp->top->ary[0];		rcu_assign_pointer(idp->top, p);		--idp->layers;		to_free->bitmap = to_free->count = 0;		free_layer(to_free);	}	while (idp->id_free_cnt >= IDR_FREE_MAX) {		p = get_from_free_list(idp);		/*		 * Note: we don't call the rcu callback here, since the only		 * layers that fall into the freelist are those that have been		 * preallocated.		 */		kmem_cache_free(idr_layer_cache, p);	}	return;}EXPORT_SYMBOL(idr_remove);/** * idr_remove_all - remove all ids from the given idr tree * @idp: idr handle * * idr_destroy() only frees up unused, cached idp_layers, but this * function will remove all id mappings and leave all idp_layers * unused. * * A typical clean-up sequence for objects stored in an idr tree, will * use idr_for_each() to free all objects, if necessay, then * idr_remove_all() to remove all ids, and idr_destroy() to free * up the cached idr_layers. */void idr_remove_all(struct idr *idp){	int n, id, max;	struct idr_layer *p;	struct idr_layer *pa[MAX_LEVEL];	struct idr_layer **paa = &pa[0];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -