idr的源码包含lib/idr.c 和include/linux/idr.h
idr 是一种关联数组,采用基数让没一个键关联一个特定的值
可以通过下面的方式初始化一个idr,采取先定义然后调用idr_init 来初始化.
动态创建
static struct idr hfi1_unit_table;
idr_init(&hfi1_unit_table);
静态创建
static DEFINE_IDA(net_generic_ids);
一般可以参考下面的code得到一个新的id明显调用ida_pre_get 获得一个新的UID,在这个例子中就是dma_ida,然后再调用ida_get_new 返回和device->dev_id 关联的rc,也就是所rc和device->dev_id 组成一个关联数组
do {
if (!ida_pre_get(&dma_ida, GFP_KERNEL))
return -ENOMEM;
mutex_lock(&dma_list_mutex);
rc = ida_get_new(&dma_ida, &device->dev_id);
mutex_unlock(&dma_list_mutex);
} while (rc == -EAGAIN);
或者采用下面的方式获得一个比mnt_group_start 更大的id和mnt->mnt_group_id 关联
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
res = ida_get_new_above(&mnt_group_ida,
mnt_group_start,
&mnt->mnt_group_id);
if (!res)
mnt_group_start = mnt->mnt_group_id + 1;
如果下给你删除一个ida 可以调用ida_remove ,下面这段code 先调用ida_get_new_above,如果failed的话,调用ida_pre_get
static DEFINE_IDA(net_generic_ids);
static int register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
{
int error;
if (ops->id) {
again:
error = ida_get_new_above(&net_generic_ids, MIN_PERNET_OPS_ID, ops->id);
if (error < 0) {
if (error == -EAGAIN) {
ida_pre_get(&net_generic_ids, GFP_KERNEL);
goto again;
}
return error;
}
max_gen_ptrs = max(max_gen_ptrs, *ops->id + 1);
}
error = __register_pernet_operations(list, ops);
if (error) {
rcu_barrier();
if (ops->id)
ida_remove(&net_generic_ids, *ops->id);
}
return error;
}
如果failed就调用ida_remove。这种写法的好处适用于静态创建ida.可以不用调用idr_init
可以调用 ida_destroy(&server->openowner_id); 彻底销毁ida
除了前面申请ida的方式外推荐使用下面这种方式
int clid;
clid = ida_simple_get(&rpc_clids, 0, 0, GFP_KERNEL);
if (clid < 0)
return clid;
其中ida_simple_get 就是ida_pre_get 和 ida_get_new_above的再包装,并且自带锁,推荐只用这种方式
int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
gfp_t gfp_mask)
{
int ret, id;
unsigned int max;
unsigned long flags;
BUG_ON((int)start < 0);
BUG_ON((int)end < 0);
if (end == 0)
max = 0x80000000;
else {
BUG_ON(end < start);
max = end - 1;
}
again:
if (!ida_pre_get(ida, gfp_mask))
return -ENOMEM;
spin_lock_irqsave(&simple_ida_lock, flags);
ret = ida_get_new_above(ida, start, &id);
if (!ret) {
if (id > max) {
ida_remove(ida, id);
ret = -ENOSPC;
} else {
ret = id;
}
}
spin_unlock_irqrestore(&simple_ida_lock, flags);
if (unlikely(ret == -EAGAIN))
goto again;
return ret;
}
与之对应的
void ida_simple_remove(struct ida *ida, unsigned int id)
{
unsigned long flags;
BUG_ON((int)id < 0);
spin_lock_irqsave(&simple_ida_lock, flags);
ida_remove(ida, id);
spin_unlock_irqrestore(&simple_ida_lock, flags);
}
ida_simple_remove删除ida
在建立ida和一个指针相关联后,可以通过idr_find找到和ida也就是一个int整数相关联的指针
struct c4iw_ep *ep;
spin_lock_irqsave(&dev->lock, flags);
ep = idr_find(&dev->hwtid_idr, tid);
spin_unlock_irqrestore(&dev->lock, flags);
static inline void idr_init(struct idr *idr)
{
INIT_RADIX_TREE(&idr->idr_rt, IDR_RT_MARKER);
idr->idr_next = 0;
}
可见idr_init 确实采用了基数来管理关联数组
#define IDA_INIT { \
.ida_rt = RADIX_TREE_INIT(IDR_RT_MARKER | GFP_NOWAIT), \
}
#define DEFINE_IDA(name) struct ida name = IDA_INIT
静态定义ida 也容易看出是基于基数管理的
int ida_pre_get(struct ida *ida, gfp_t gfp)
{
__radix_tree_preload(gfp, IDA_PRELOAD_SIZE);
/*
* The IDA API has no preload_end() equivalent. Instead,
* ida_get_new() can return -EAGAIN, prompting the caller
* to return to the ida_pre_get() step.
*/
preempt_enable();
if (!this_cpu_read(ida_bitmap)) {
struct ida_bitmap *bitmap = kmalloc(sizeof(*bitmap), gfp);
if (!bitmap)
return 0;
if (this_cpu_cmpxchg(ida_bitmap, NULL, bitmap))
kfree(bitmap);
}
return 1;
}
可以看到ida_pre_get 中并没有处理struct ida *ida,相反是会为每个cpu申请一个ida_bitmap
static inline int ida_get_new(struct ida *ida, int *p_id)
{
return ida_get_new_above(ida, 0, p_id);
}
可以看到ida_get_new 就是ida_get_new_above 的再包装,也就是ida_get_new 返回的id值只要比0大就行
ida_get_new_above的实现比较复杂,其 作用就是申请一个大于或者等于start id的值。其函数原型如下:
int ida_get_new_above(struct ida *ida, int start, int *id)
idr 是一种关联数组,采用基数让没一个键关联一个特定的值
可以通过下面的方式初始化一个idr,采取先定义然后调用idr_init 来初始化.
动态创建
static struct idr hfi1_unit_table;
idr_init(&hfi1_unit_table);
静态创建
static DEFINE_IDA(net_generic_ids);
一般可以参考下面的code得到一个新的id明显调用ida_pre_get 获得一个新的UID,在这个例子中就是dma_ida,然后再调用ida_get_new 返回和device->dev_id 关联的rc,也就是所rc和device->dev_id 组成一个关联数组
do {
if (!ida_pre_get(&dma_ida, GFP_KERNEL))
return -ENOMEM;
mutex_lock(&dma_list_mutex);
rc = ida_get_new(&dma_ida, &device->dev_id);
mutex_unlock(&dma_list_mutex);
} while (rc == -EAGAIN);
或者采用下面的方式获得一个比mnt_group_start 更大的id和mnt->mnt_group_id 关联
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
res = ida_get_new_above(&mnt_group_ida,
mnt_group_start,
&mnt->mnt_group_id);
if (!res)
mnt_group_start = mnt->mnt_group_id + 1;
如果下给你删除一个ida 可以调用ida_remove ,下面这段code 先调用ida_get_new_above,如果failed的话,调用ida_pre_get
static DEFINE_IDA(net_generic_ids);
static int register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
{
int error;
if (ops->id) {
again:
error = ida_get_new_above(&net_generic_ids, MIN_PERNET_OPS_ID, ops->id);
if (error < 0) {
if (error == -EAGAIN) {
ida_pre_get(&net_generic_ids, GFP_KERNEL);
goto again;
}
return error;
}
max_gen_ptrs = max(max_gen_ptrs, *ops->id + 1);
}
error = __register_pernet_operations(list, ops);
if (error) {
rcu_barrier();
if (ops->id)
ida_remove(&net_generic_ids, *ops->id);
}
return error;
}
如果failed就调用ida_remove。这种写法的好处适用于静态创建ida.可以不用调用idr_init
可以调用 ida_destroy(&server->openowner_id); 彻底销毁ida
除了前面申请ida的方式外推荐使用下面这种方式
int clid;
clid = ida_simple_get(&rpc_clids, 0, 0, GFP_KERNEL);
if (clid < 0)
return clid;
其中ida_simple_get 就是ida_pre_get 和 ida_get_new_above的再包装,并且自带锁,推荐只用这种方式
int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
gfp_t gfp_mask)
{
int ret, id;
unsigned int max;
unsigned long flags;
BUG_ON((int)start < 0);
BUG_ON((int)end < 0);
if (end == 0)
max = 0x80000000;
else {
BUG_ON(end < start);
max = end - 1;
}
again:
if (!ida_pre_get(ida, gfp_mask))
return -ENOMEM;
spin_lock_irqsave(&simple_ida_lock, flags);
ret = ida_get_new_above(ida, start, &id);
if (!ret) {
if (id > max) {
ida_remove(ida, id);
ret = -ENOSPC;
} else {
ret = id;
}
}
spin_unlock_irqrestore(&simple_ida_lock, flags);
if (unlikely(ret == -EAGAIN))
goto again;
return ret;
}
与之对应的
void ida_simple_remove(struct ida *ida, unsigned int id)
{
unsigned long flags;
BUG_ON((int)id < 0);
spin_lock_irqsave(&simple_ida_lock, flags);
ida_remove(ida, id);
spin_unlock_irqrestore(&simple_ida_lock, flags);
}
ida_simple_remove删除ida
在建立ida和一个指针相关联后,可以通过idr_find找到和ida也就是一个int整数相关联的指针
struct c4iw_ep *ep;
spin_lock_irqsave(&dev->lock, flags);
ep = idr_find(&dev->hwtid_idr, tid);
spin_unlock_irqrestore(&dev->lock, flags);
static inline void idr_init(struct idr *idr)
{
INIT_RADIX_TREE(&idr->idr_rt, IDR_RT_MARKER);
idr->idr_next = 0;
}
可见idr_init 确实采用了基数来管理关联数组
#define IDA_INIT { \
.ida_rt = RADIX_TREE_INIT(IDR_RT_MARKER | GFP_NOWAIT), \
}
#define DEFINE_IDA(name) struct ida name = IDA_INIT
静态定义ida 也容易看出是基于基数管理的
int ida_pre_get(struct ida *ida, gfp_t gfp)
{
__radix_tree_preload(gfp, IDA_PRELOAD_SIZE);
/*
* The IDA API has no preload_end() equivalent. Instead,
* ida_get_new() can return -EAGAIN, prompting the caller
* to return to the ida_pre_get() step.
*/
preempt_enable();
if (!this_cpu_read(ida_bitmap)) {
struct ida_bitmap *bitmap = kmalloc(sizeof(*bitmap), gfp);
if (!bitmap)
return 0;
if (this_cpu_cmpxchg(ida_bitmap, NULL, bitmap))
kfree(bitmap);
}
return 1;
}
可以看到ida_pre_get 中并没有处理struct ida *ida,相反是会为每个cpu申请一个ida_bitmap
static inline int ida_get_new(struct ida *ida, int *p_id)
{
return ida_get_new_above(ida, 0, p_id);
}
可以看到ida_get_new 就是ida_get_new_above 的再包装,也就是ida_get_new 返回的id值只要比0大就行
ida_get_new_above的实现比较复杂,其 作用就是申请一个大于或者等于start id的值。其函数原型如下:
int ida_get_new_above(struct ida *ida, int start, int *id)