diff options
Diffstat (limited to 'lib/idr.c')
| -rw-r--r-- | lib/idr.c | 22 | 
1 files changed, 19 insertions, 3 deletions
diff --git a/lib/idr.c b/lib/idr.c index e728c7fccc4..1c4f9281f41 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -185,6 +185,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa)  			new = get_from_free_list(idp);  			if (!new)  				return -1; +			new->layer = l-1;  			rcu_assign_pointer(p->ary[m], new);  			p->count++;  		} @@ -210,6 +211,7 @@ build_up:  	if (unlikely(!p)) {  		if (!(p = get_from_free_list(idp)))  			return -1; +		p->layer = 0;  		layers = 1;  	}  	/* @@ -218,8 +220,14 @@ build_up:  	 */  	while ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) {  		layers++; -		if (!p->count) +		if (!p->count) { +			/* special case: if the tree is currently empty, +			 * then we grow the tree by moving the top node +			 * upwards. +			 */ +			p->layer++;  			continue; +		}  		if (!(new = get_from_free_list(idp))) {  			/*  			 * The allocation failed.  If we built part of @@ -237,6 +245,7 @@ build_up:  		}  		new->ary[0] = p;  		new->count = 1; +		new->layer = layers-1;  		if (p->bitmap == IDR_FULL)  			__set_bit(0, &new->bitmap);  		p = new; @@ -493,17 +502,21 @@ void *idr_find(struct idr *idp, int id)  	int n;  	struct idr_layer *p; -	n = idp->layers * IDR_BITS;  	p = rcu_dereference(idp->top); +	if (!p) +		return NULL; +	n = (p->layer+1) * IDR_BITS;  	/* Mask off upper bits we don't use for the search. */  	id &= MAX_ID_MASK;  	if (id >= (1 << n))  		return NULL; +	BUG_ON(n == 0);  	while (n > 0 && p) {  		n -= IDR_BITS; +		BUG_ON(n != p->layer*IDR_BITS);  		p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);  	}  	return((void *)p); @@ -582,8 +595,11 @@ void *idr_replace(struct idr *idp, void *ptr, int id)  	int n;  	struct idr_layer *p, *old_p; -	n = idp->layers * IDR_BITS;  	p = idp->top; +	if (!p) +		return ERR_PTR(-EINVAL); + +	n = (p->layer+1) * IDR_BITS;  	id &= MAX_ID_MASK;  |