mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 11:16:11 +00:00
ida: implement idr based id allocator
Implement idr based id allocator. ida is used the same way idr is used but lacks id -> ptr translation and thus consumes much less memory. struct ida_bitmap is attached as leaf nodes to idr tree which is managed by the idr code. Each ida_bitmap is 128bytes long and contains slightly less than a thousand slots. ida is more aggressive with releasing extra resources acquired using ida_pre_get(). After every successful id allocation, ida frees one reserved idr_layer if possible. Reserved ida_bitmap is not freed automatically but only one ida_bitmap is reserved and it's almost always used right away. Under most circumstances, ida won't hold on to memory for too long which isn't actively used. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
e33ac8bdb0
commit
72dba584b6
2 changed files with 274 additions and 0 deletions
|
@ -83,4 +83,33 @@ void idr_remove(struct idr *idp, int id);
|
|||
void idr_destroy(struct idr *idp);
|
||||
void idr_init(struct idr *idp);
|
||||
|
||||
|
||||
/*
|
||||
* IDA - IDR based id allocator, use when translation from id to
|
||||
* pointer isn't necessary.
|
||||
*/
|
||||
#define IDA_CHUNK_SIZE 128 /* 128 bytes per chunk */
|
||||
#define IDA_BITMAP_LONGS (128 / sizeof(long) - 1)
|
||||
#define IDA_BITMAP_BITS (IDA_BITMAP_LONGS * sizeof(long) * 8)
|
||||
|
||||
struct ida_bitmap {
|
||||
long nr_busy;
|
||||
unsigned long bitmap[IDA_BITMAP_LONGS];
|
||||
};
|
||||
|
||||
struct ida {
|
||||
struct idr idr;
|
||||
struct ida_bitmap *free_bitmap;
|
||||
};
|
||||
|
||||
#define IDA_INIT(name) { .idr = IDR_INIT(name), .free_bitmap = NULL, }
|
||||
#define DEFINE_IDA(name) struct ida name = IDA_INIT(name)
|
||||
|
||||
int ida_pre_get(struct ida *ida, gfp_t gfp_mask);
|
||||
int ida_get_new_above(struct ida *ida, int starting_id, int *p_id);
|
||||
int ida_get_new(struct ida *ida, int *p_id);
|
||||
void ida_remove(struct ida *ida, int id);
|
||||
void ida_destroy(struct ida *ida);
|
||||
void ida_init(struct ida *ida);
|
||||
|
||||
#endif /* __IDR_H__ */
|
||||
|
|
245
lib/idr.c
245
lib/idr.c
|
@ -506,3 +506,248 @@ void idr_init(struct idr *idp)
|
|||
spin_lock_init(&idp->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(idr_init);
|
||||
|
||||
|
||||
/*
|
||||
* IDA - IDR based ID allocator
|
||||
*
|
||||
* this is id allocator without id -> pointer translation. Memory
|
||||
* usage is much lower than full blown idr because each id only
|
||||
* occupies a bit. ida uses a custom leaf node which contains
|
||||
* IDA_BITMAP_BITS slots.
|
||||
*
|
||||
* 2007-04-25 written by Tejun Heo <htejun@gmail.com>
|
||||
*/
|
||||
|
||||
static void free_bitmap(struct ida *ida, struct ida_bitmap *bitmap)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!ida->free_bitmap) {
|
||||
spin_lock_irqsave(&ida->idr.lock, flags);
|
||||
if (!ida->free_bitmap) {
|
||||
ida->free_bitmap = bitmap;
|
||||
bitmap = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&ida->idr.lock, flags);
|
||||
}
|
||||
|
||||
kfree(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* ida_pre_get - reserve resources for ida allocation
|
||||
* @ida: ida handle
|
||||
* @gfp_mask: memory allocation flag
|
||||
*
|
||||
* This function should be called prior to locking and calling the
|
||||
* following function. 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 ida_pre_get(struct ida *ida, gfp_t gfp_mask)
|
||||
{
|
||||
/* allocate idr_layers */
|
||||
if (!idr_pre_get(&ida->idr, gfp_mask))
|
||||
return 0;
|
||||
|
||||
/* allocate free_bitmap */
|
||||
if (!ida->free_bitmap) {
|
||||
struct ida_bitmap *bitmap;
|
||||
|
||||
bitmap = kmalloc(sizeof(struct ida_bitmap), gfp_mask);
|
||||
if (!bitmap)
|
||||
return 0;
|
||||
|
||||
free_bitmap(ida, bitmap);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(ida_pre_get);
|
||||
|
||||
/**
|
||||
* ida_get_new_above - allocate new ID above or equal to a start id
|
||||
* @ida: ida handle
|
||||
* @staring_id: id to start search at
|
||||
* @p_id: pointer to the allocated handle
|
||||
*
|
||||
* Allocate new ID above or equal to @ida. It should be called with
|
||||
* any required locks.
|
||||
*
|
||||
* If memory is required, it will return -EAGAIN, you should unlock
|
||||
* and go back to the ida_pre_get() call. If the ida is full, it will
|
||||
* return -ENOSPC.
|
||||
*
|
||||
* @p_id returns a value in the range 0 ... 0x7fffffff.
|
||||
*/
|
||||
int ida_get_new_above(struct ida *ida, int starting_id, int *p_id)
|
||||
{
|
||||
struct idr_layer *pa[MAX_LEVEL];
|
||||
struct ida_bitmap *bitmap;
|
||||
unsigned long flags;
|
||||
int idr_id = starting_id / IDA_BITMAP_BITS;
|
||||
int offset = starting_id % IDA_BITMAP_BITS;
|
||||
int t, id;
|
||||
|
||||
restart:
|
||||
/* get vacant slot */
|
||||
t = idr_get_empty_slot(&ida->idr, idr_id, pa);
|
||||
if (t < 0) {
|
||||
if (t == -1)
|
||||
return -EAGAIN;
|
||||
else /* will be -3 */
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (t * IDA_BITMAP_BITS >= MAX_ID_BIT)
|
||||
return -ENOSPC;
|
||||
|
||||
if (t != idr_id)
|
||||
offset = 0;
|
||||
idr_id = t;
|
||||
|
||||
/* if bitmap isn't there, create a new one */
|
||||
bitmap = (void *)pa[0]->ary[idr_id & IDR_MASK];
|
||||
if (!bitmap) {
|
||||
spin_lock_irqsave(&ida->idr.lock, flags);
|
||||
bitmap = ida->free_bitmap;
|
||||
ida->free_bitmap = NULL;
|
||||
spin_unlock_irqrestore(&ida->idr.lock, flags);
|
||||
|
||||
if (!bitmap)
|
||||
return -EAGAIN;
|
||||
|
||||
memset(bitmap, 0, sizeof(struct ida_bitmap));
|
||||
pa[0]->ary[idr_id & IDR_MASK] = (void *)bitmap;
|
||||
pa[0]->count++;
|
||||
}
|
||||
|
||||
/* lookup for empty slot */
|
||||
t = find_next_zero_bit(bitmap->bitmap, IDA_BITMAP_BITS, offset);
|
||||
if (t == IDA_BITMAP_BITS) {
|
||||
/* no empty slot after offset, continue to the next chunk */
|
||||
idr_id++;
|
||||
offset = 0;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
id = idr_id * IDA_BITMAP_BITS + t;
|
||||
if (id >= MAX_ID_BIT)
|
||||
return -ENOSPC;
|
||||
|
||||
__set_bit(t, bitmap->bitmap);
|
||||
if (++bitmap->nr_busy == IDA_BITMAP_BITS)
|
||||
idr_mark_full(pa, idr_id);
|
||||
|
||||
*p_id = id;
|
||||
|
||||
/* Each leaf node can handle nearly a thousand slots and the
|
||||
* whole idea of ida is to have small memory foot print.
|
||||
* Throw away extra resources one by one after each successful
|
||||
* allocation.
|
||||
*/
|
||||
if (ida->idr.id_free_cnt || ida->free_bitmap) {
|
||||
struct idr_layer *p = alloc_layer(&ida->idr);
|
||||
if (p)
|
||||
kmem_cache_free(idr_layer_cache, p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ida_get_new_above);
|
||||
|
||||
/**
|
||||
* ida_get_new - allocate new ID
|
||||
* @ida: idr handle
|
||||
* @p_id: pointer to the allocated handle
|
||||
*
|
||||
* Allocate new ID. 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 ida_get_new(struct ida *ida, int *p_id)
|
||||
{
|
||||
return ida_get_new_above(ida, 0, p_id);
|
||||
}
|
||||
EXPORT_SYMBOL(ida_get_new);
|
||||
|
||||
/**
|
||||
* ida_remove - remove the given ID
|
||||
* @ida: ida handle
|
||||
* @id: ID to free
|
||||
*/
|
||||
void ida_remove(struct ida *ida, int id)
|
||||
{
|
||||
struct idr_layer *p = ida->idr.top;
|
||||
int shift = (ida->idr.layers - 1) * IDR_BITS;
|
||||
int idr_id = id / IDA_BITMAP_BITS;
|
||||
int offset = id % IDA_BITMAP_BITS;
|
||||
int n;
|
||||
struct ida_bitmap *bitmap;
|
||||
|
||||
/* clear full bits while looking up the leaf idr_layer */
|
||||
while ((shift > 0) && p) {
|
||||
n = (idr_id >> shift) & IDR_MASK;
|
||||
__clear_bit(n, &p->bitmap);
|
||||
p = p->ary[n];
|
||||
shift -= IDR_BITS;
|
||||
}
|
||||
|
||||
if (p == NULL)
|
||||
goto err;
|
||||
|
||||
n = idr_id & IDR_MASK;
|
||||
__clear_bit(n, &p->bitmap);
|
||||
|
||||
bitmap = (void *)p->ary[n];
|
||||
if (!test_bit(offset, bitmap->bitmap))
|
||||
goto err;
|
||||
|
||||
/* update bitmap and remove it if empty */
|
||||
__clear_bit(offset, bitmap->bitmap);
|
||||
if (--bitmap->nr_busy == 0) {
|
||||
__set_bit(n, &p->bitmap); /* to please idr_remove() */
|
||||
idr_remove(&ida->idr, idr_id);
|
||||
free_bitmap(ida, bitmap);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
printk(KERN_WARNING
|
||||
"ida_remove called for id=%d which is not allocated.\n", id);
|
||||
}
|
||||
EXPORT_SYMBOL(ida_remove);
|
||||
|
||||
/**
|
||||
* ida_destroy - release all cached layers within an ida tree
|
||||
* ida: ida handle
|
||||
*/
|
||||
void ida_destroy(struct ida *ida)
|
||||
{
|
||||
idr_destroy(&ida->idr);
|
||||
kfree(ida->free_bitmap);
|
||||
}
|
||||
EXPORT_SYMBOL(ida_destroy);
|
||||
|
||||
/**
|
||||
* ida_init - initialize ida handle
|
||||
* @ida: ida handle
|
||||
*
|
||||
* This function is use to set up the handle (@ida) that you will pass
|
||||
* to the rest of the functions.
|
||||
*/
|
||||
void ida_init(struct ida *ida)
|
||||
{
|
||||
memset(ida, 0, sizeof(struct ida));
|
||||
idr_init(&ida->idr);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(ida_init);
|
||||
|
|
Loading…
Reference in a new issue