IDR机制

来源:互联网 发布:网络售彩最新消息官方 编辑:程序博客网 时间:2024/05/16 10:50

IDR机制在Linux内核中指的是整数ID管理机制。

实质上来讲,这就是一种将一个整数ID号和一个指针关联在一起的机制。

这个机制最早在03年2月加入内核,当时作为POSIX定时器的一个补丁。现在,内核中很多地方都可以找到它的身影。

IDR机制原理:

IDR机制适用在那些需要把某个整数和特定指针关联在一起的地方。例如,在IIC总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送设备的地址。当适配器要访问总线上的IIC设备时,首先要知道它们的ID号,同时要在内核中建立一个用于描述该设备的结构体,和驱动程序

将ID号和设备结构体结合起来,如果使用数组进行索引,一旦ID 号很大,则用数组索引会占据大量内存空间。这显然不可能。或者用链表,但是,如果总线中实际存在的设备很多,则链表的查询效率会很低。

此时,IDR机制应运而生。该机制内部采用红黑树实现,可以很方便的将整数和指针关联起来,并且具有很高的搜索效率

struct idr {    struct idr_layer *top;    struct idr_layer *id_free;    int          layers; /* only valid without concurrent changes */    int          id_free_cnt;    spinlock_t      lock;};struct idr_layer {    unsigned long         bitmap; /* A zero bit means "space here" */    struct idr_layer    *ary[1<<IDR_BITS];    int             count;     /* When zero, we can release it */    int             layer;     /* distance from leaf */    struct rcu_head         rcu_head;};

宏定义并且初始化一个名为name的IDR:

#define DEFINE_IDR(name)    struct idr name = IDR_INIT(name)#define IDR_INIT(name)                        \{                                \    .top        = NULL,                    \    .id_free    = NULL,                    \    .layers     = 0,                    \    .id_free_cnt    = 0,                    \    .lock        = __SPIN_LOCK_UNLOCKED(name.lock),    \}

动态初始化IDR:

void idr_init(struct idr *idp){    memset(idp, 0, sizeof(struct idr));    spin_lock_init(&idp->lock);}

分配存放ID号的内存:

每次通过IDR获得ID号之前 ,需要为ID号先分配内存。分配内存的函数是idr_pre_get().成功返回1,失败放回0

第一个参数是指向IDR的指针,第二个参数是内存分配标志。

int idr_pre_get(struct idr *idp, gfp_t gfp_mask){    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);        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++;}

分配ID号并将ID号和指针关联:

参数idp是之前,通过idr_init()初始化的idr指针,或者DEFINE_IDR宏定义的指针。参数ptr是和ID号相关联 的 指针。参数id由内核自动分配的ID号。参数start_id是起始ID号。

成功返回0,失败返回负值:

int idr_get_new(struct idr *idp, void *ptr, int *id){    int rv;    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;}int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id){    int rv;    rv = idr_get_new_above_int(idp, ptr, starting_id);    /*     * 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;}

这两个函数唯一的区别是起始ID号不同:

它们都调用了:

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;}

它调用了:

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;build_up:    p = idp->top;    layers = idp->layers;    if (unlikely(!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;    }    rcu_assign_pointer(idp->top, p);    idp->layers = layers;    v = sub_alloc(idp, &id, pa);    if (v == IDR_NEED_TO_GROW)        goto build_up;    return(v);}

static void idr_mark_full(struct idr_layer **pa, int id){    struct idr_layer *p = pa[0];    int l = 0;    __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);    }}

idr_get_new还调用了:

#define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC)                             //这是一个错误处理的宏

通过ID号查找对应的指针:

void *idr_find(struct idr *idp, int id){    int n;    struct idr_layer *p;    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);}

删除ID号:

void idr_remove(struct idr *idp, int id){    struct idr_layer *p;    struct idr_layer *to_free;    /* Mask off upper bits we don't use for the search. */    id &= MAX_ID_MASK;    sub_remove(idp, (idp->layers - 1) * IDR_BITS, id);    if (idp->top && idp->top->count == 1 && (idp->layers > 1) &&        idp->top->ary[0]) {        /*         * Single child at leftmost slot: we can shrink the tree.         * This level is not needed anymore since when layers are         * inserted, they are inserted at the top of the existing         * tree.         */        to_free = idp->top;        p = idp->top->ary[0];        rcu_assign_pointer(idp->top, p);        --idp->layers;        to_free->bitmap = to_free->count = 0;        free_layer(to_free);    }    while (idp->id_free_cnt >= IDR_FREE_MAX) {        p = get_from_free_list(idp);        /*         * Note: we don't call the rcu callback here, since the only         * layers that fall into the freelist are those that have been         * preallocated.         */        kmem_cache_free(idr_layer_cache, p);    }    return;}

它调用了:

static void sub_remove(struct idr *idp, int shift, int id){    struct idr_layer *p = idp->top;    struct idr_layer **pa[MAX_LEVEL];    struct idr_layer ***paa = &pa[0];    struct idr_layer *to_free;    int n;    *paa = NULL;    *++paa = &idp->top;    while ((shift > 0) && p) {        n = (id >> shift) & IDR_MASK;        __clear_bit(n, &p->bitmap);        *++paa = &p->ary[n];        p = p->ary[n];        shift -= IDR_BITS;    }    n = id & IDR_MASK;    if (likely(p != NULL && test_bit(n, &p->bitmap))){        __clear_bit(n, &p->bitmap);        rcu_assign_pointer(p->ary[n], NULL);        to_free = NULL;        while(*paa && ! --((**paa)->count)){            if (to_free)                free_layer(to_free);            to_free = **paa;            **paa-- = NULL;        }        if (!*paa)            idp->layers = 0;        if (to_free)            free_layer(to_free);    } else        idr_remove_warning(id);}

static struct idr_layer *get_from_free_list(struct idr *idp){    struct idr_layer *p;    unsigned long flags;    spin_lock_irqsave(&idp->lock, flags);    if ((p = idp->id_free)) {        idp->id_free = p->ary[0];        idp->id_free_cnt--;        p->ary[0] = NULL;    }    spin_unlock_irqrestore(&idp->lock, flags);    return(p);}
0 0
原创粉丝点击