IDR--Integer ID Management

来源:互联网 发布:qq视频录像软件注册码 编辑:程序博客网 时间:2024/04/30 14:12
IDR是用来管理小整形ID数的库函数集合。这些代码最早是在2003年2月作为POSIX clocks补丁的一部分加入的;现在,在内核的许多地方都可以看到IDR的应用。IDR算法的实现采用了radix tree的思想,从本质上讲,IDR是一个稀疏数组,能将整形ID号映射到任意指针上。

应用IDR机制时要包含头文件<linux/idr.h>(注:本文内核版本为2.6.32.10),IDR结构体如下
struct idr {
    struct idr_layer *top; //IDR顶层
    struct idr_layer *id_free; //预留的可用idr layer指针
    int          layers; /* only valid without concurrent changes */ //IDR树的层数
    int          id_free_cnt; //预留的可用idr layer数目
    spinlock_t      lock; //锁保护
};
而idr layer的结构体如下
struct idr_layer {
    unsigned long         bitmap; /* A zero bit means "space here" */ //节点的位图,0表示slot槽空闲可用
    struct idr_layer    *ary[1<<IDR_BITS]; //以32位系统为例,IDR_BITS为5,即一个节点最多可有32个孩子(slot槽)
    int             count;     /* When zero, we can release it *///与bitmap,ary对应,表示已标注的slot槽数目
    int             layer;     /* distance from leaf */ //与叶子节点的距离,以0开始
    struct rcu_head         rcu_head;//rcu锁
};

首先创建一个IDR对象需要定义一个struct idr,并将指针传给函数void idr_init(struct idr *idp);一般采用快捷方式DEFINE_IDR(name)来创建,定义如下
#define IDR_INIT(name)                        \
{                                \
    .top        = NULL,                    \
    .id_free    = NULL,                    \
    .layers     = 0,                    \
    .id_free_cnt    = 0,                    \
    .lock        = __SPIN_LOCK_UNLOCKED(name.lock),    \
}
#define DEFINE_IDR(name)    struct idr name = IDR_INIT(name)

 

要获得一个ID号要分两个步骤,首先分配内存,其次是获得ID号;因此,先调用函数
int idr_pre_get(struct idr *idp, gfp_t gfp_mask);
注意该函数会导致睡眠,因此不应该用锁保护,函数实现如下
int idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
//通过kmem_cache_zalloc函数申请足够的内存空间,IDR_FREE_MAX定义为2倍的MAX_LEVEL,即14;所以一共预申请了14个idr_layer内存空间
    while (idp->id_free_cnt < IDR_FREE_MAX) {
        struct idr_layer *new;
        new = kmem_cache_zalloc(idr_layer_cache, gfp_mask);
        if (new == NULL)
            return (0);
        //该函数将预申请的idr_layer内存放入可用链表中,具体分析如下
        move_to_free_list(idp, new);
    }
    return 1;
}

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 __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++;
}
在初始化创建IDR对象时,top设为NULL,id_free设为NULL,id_free_cnt设为0,在将第一个idr_layer移入链表时,该idr_layer第一个slot槽(ary[0])指向id_free(NULL),而id_free指向新加入的idr_layer,id_free_cnt加1,如下图所示

 

第二个idr_layer移入链表时,该idr_layer第一个slot槽(ary[0])指向第一个idr_layer,而id_free指向第二个idr_layer,id_free_cnt自加到2;一次移入第三个,第四个,…,直到第十四个idr_layer为止,最终id_free指向最后一个idr_layer,id_free_cnt为14,如下图所示

调用函数idr_pre_get成功后,将返回1,若申请不到内存则返回0;接下来,可通过下面的任一函数来获得ID号:
int idr_get_new(struct idr *idp, void *ptr, int *id);
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);
这两个函数的区别在于idr_get_new函数是从0开始申请ID号,而idr_get_new_above是从starting_id开始申请ID号,这里我们将只分析第一个函数:
int idr_get_new(struct idr *idp, void *ptr, int *id)
{
    int rv;
//具体调用idr_get_new_above_int函数,我们发现idr_get_new和idr_get_new_above函数的区别在于传入的第三个参数是0还是starting_id
    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;
}

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_empty_slot,rcu_assign_pointer和idr_mark_full这三个函数,先来分析idr_get_empty_slot函数:
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; //starting_id为0
build_up:
    //我们以初始化idr后的第一次申请ID号为例
    p = idp->top; //初始化时idp->top设为NULL
    layers = idp->layers; //初始化时idp->layers设为0
    if (unlikely(!p)) {
        //该函数是从idr_pre_get申请的空闲idr_layer内存中获得一块赋给p,
        具体实现函数如下分析
        if (!(p = get_from_free_list(idp)))
            return -1;
        p->layer = 0;
        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) {
            /* 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
             * 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;
        new->layer = layers-1;
        if (p->bitmap == IDR_FULL)
            __set_bit(0, &new->bitmap);
        p = new;
    }
    //将p,即通过get_from_free_list函数获得的内存赋给top层
    rcu_assign_pointer(idp->top, p);
    idp->layers = layers; //此时只有根节点,layers为1
    //该函数真正实现ID号的获得
v = sub_alloc(idp, &id, pa);
    if (v == IDR_NEED_TO_GROW)
        goto build_up;
    return(v);
}

static struct idr_layer *get_from_free_list(struct idr *idp)
{
    struct idr_layer *p;
    unsigned long flags;

    spin_lock_irqsave(&idp->lock, flags);
    //idp->id_free非零,说明有预留的idr_layer内存可用
    if ((p = idp->id_free)) {
//此时将最后申请的可用idr_layer内存从链表中移除,将id_free指向前一个预留内存,如下如图所示(蓝线标出)
        idp->id_free = p->ary[0];//p->ary[0]指向前一个预留内存
        idp->id_free_cnt--; //数目减一
        p->ary[0] = NULL; //slot0槽设为空
    }
    spin_unlock_irqrestore(&idp->lock, flags);
    return(p);
}

函数idr_get_new_above_int中的rcu_assign_pointer实现了指针与ID号的绑定,并将指针保存在ary的slot槽中;
函数idr_get_new_above_int中的idr_mark_full根据ID号来置位bitmap,代码如下
static void idr_mark_full(struct idr_layer **pa, int id)
{
    struct idr_layer *p = pa[0];
    int l = 0;
   //根据id设置bitmap相应位
    __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);
    }
}

总的实现如下图的红色部分表示。

 

若已知ID号,而要得到相应的指针,则可调用函数:
void *idr_find(struct idr *idp, int id);
若删除ID号并释放指针,可调用函数:
void idr_remove(struct idr *idp, int id);
若要销毁idr树,则可调用函数:
void idr_destroy(struct idr *idp);

通过上述这些函数接口,我们能够产生ID号用做设备的次设备号,节点号等,在内核的rtc子系统的设备注册中,就用到了idr机制,代码如下(/drivers/rtc/class.c):
//初始化一个rtc的idr对象
static DEFINE_IDR(rtc_idr);

struct rtc_device *rtc_device_register(const char *name, struct device *dev,
                    const struct rtc_class_ops *ops,
                    struct module *owner)
{
    struct rtc_device *rtc;
    int id, err;
    //为rtc_idr预留内存空间
    if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
        err = -ENOMEM;
        goto exit;
    }


    mutex_lock(&idr_lock);
    err = idr_get_new(&rtc_idr, NULL, &id); //获得ID号
    mutex_unlock(&idr_lock);

    if (err < 0)
        goto exit;

id = id & MAX_ID_MASK; //ID号范围是0~0x7fffffff,而rtc的设备数目RTC_DEV_MAX定义为16

    rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
    if (rtc == NULL) {
        err = -ENOMEM;
        goto exit_idr;
    }

    rtc->id = id;//将获得的ID号作为rtc的次设备号
    rtc->ops = ops;
    rtc->owner = owner;
    rtc->max_user_freq = 64;
    rtc->dev.parent = dev;
    rtc->dev.class = rtc_class;
    rtc->dev.release = rtc_device_release;

    mutex_init(&rtc->ops_lock);
    spin_lock_init(&rtc->irq_lock);
    spin_lock_init(&rtc->irq_task_lock);
    init_waitqueue_head(&rtc->irq_queue);

    strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
    dev_set_name(&rtc->dev, "rtc%d", id);

    rtc_dev_prepare(rtc);

    err = device_register(&rtc->dev);
    if (err)
        goto exit_kfree;

    rtc_dev_add_device(rtc);
    rtc_sysfs_add_device(rtc);
    rtc_proc_add_device(rtc);

    dev_info(dev, "rtc core: registered %s as %s\n",
            rtc->name, dev_name(&rtc->dev));

    return rtc;

exit_kfree:
    kfree(rtc);

exit_idr:
    mutex_lock(&idr_lock);
    idr_remove(&rtc_idr, id);
    mutex_unlock(&idr_lock);

exit:
    dev_err(dev, "rtc core: unable to register %s, err = %d\n",
            name, err);
    return ERR_PTR(err);
}
0 0
原创粉丝点击