idr 映射总结

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值