• Linux IDR机制【转】


    转自:https://blog.csdn.net/av_geek/article/details/49640433

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

  • 相关阅读:
    rest-framework之路由
    rest-framework之频率控制
    mysql 模糊查询 concat()
    解决spring使用动态代理
    MySQL巧用sum,case...when...优化统计查询
    解决maven项目中有小红叉的问题
    google ---gson字符串数组用GSON解析然后用逗号隔开拼接,去掉最后一个逗号
    Elicpse使用技巧-打开选中文件文件夹或者包的当前目录
    powdesigner建表
    遍历map
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/9957577.html
Copyright © 2020-2023  润新知