Linux Ida and Ird 源码分析

来源:互联网 发布:wow淘宝刷坐骑会封号吗 编辑:程序博客网 时间:2024/06/07 03:44

我有一个毛病就是不爱读源代码, 总是喜欢从宏观上看逻辑架构, 对于一些比较通用的代码,网上的分析很多,这个方法学起来的确比较快,毕竟源码,尤其是代码量比较多的源码,读起来是很费力的.但是现在越来越多的遇到一些code,网上查也查不太到,我就不得不自己去读源码了.

也好,读源码的能力还是要锻炼的.


Read the Fucking Source Code!


水平有限,不保证内容完全正确,如有错误,烦请指正。


简介: IDR机制在Linux内核中指的是整数ID管理机制。实质上来讲,这就是一种将一个整数ID号和一个指针关联在一起的机制。

IDA是用IDR来实现的ID分配机制,与IDR的区别是IDA仅仅分配与管理ID,并不将ID与指针相关联.


参考源码版本: linux 3.10


先看IDR:

IDR采用树结构+bitmap来管理ID与指针的分配, 不过不是简单的2叉树,而是256叉树! 

每个树节点有256个分支(树枝), 每个分支以指针指向下一层的树节点, 最顶层的节点叫做树叶, 树叶作为特殊的节点其分支不再指向其他节点, 而是直接包含我们要关联的指针, 因此每个树叶拥有256个指针.

那么与指针关联的ID如何记录呢?

每个树节点包含一个256位的bitmap结构, 每位bit代表一个分支, 当这个分支下包含的所有树叶都被分配满指针时,此位置1. 对于最终存放指针的树叶,其也有一个256位的bitmap, 其中每一位表示对应的指针。 置1表示对应的指针被分配,置0表示空闲。 当一个指针被分配时, 这个指针对应的ID由该指针对应的bit在这个树叶的bitmap的位置, 以及该指针所在的树叶在整个树的位置来记录.

为什么不用2叉树? 很简单, 对于大量的ID整型数, 2叉树的叶子太少,需要大量增加树的层次.不便于计算.


再看IDA: 

/** * DOC: IDA description * IDA - IDR based ID allocator * * This is id allocator without id -> pointer translation.  Memory * usage is much lower than full blown idr because each id only * occupies a bit.  ida uses a custom leaf node which contains * IDA_BITMAP_BITS slots. * * 2007-04-25  written by Tejun Heo <htejun@gmail.com> */

IDA只不过是对IDR的一个封装,IDA不需要记录指针,仅仅是分配与管理ID.  因此IDA的树叶的每个分支指向一个Ida bitmap, 这个bitmap中的每一个bit负责记录ID, 置1表示已分配, 置0表示未分配. ID号由对应的bit在IDA bitmap中的位置以及IDA bitmap在整个树中的位置表示.


先看IDA及IDR的定义, 定义在idr.h头文件中(include/linux/idr,h)

IDR定义如下:

#define IDR_BITS 8#define IDR_SIZE (1 << IDR_BITS)#define IDR_MASK ((1 << IDR_BITS)-1)

struct idr_layer {intprefix;/* the ID prefix of this idr_layer */DECLARE_BITMAP(bitmap, IDR_SIZE); /* A zero bit means "space here" */struct idr_layer __rcu*ary[1<<IDR_BITS];intcount;/* When zero, we can release it */intlayer;/* distance from leaf */struct rcu_headrcu_head;};struct idr {struct idr_layer __rcu*hint;/* the last layer allocated from */struct idr_layer __rcu*top;struct idr_layer*id_free;intlayers;/* only valid w/o concurrent changes */intid_free_cnt;intcur;/* current pos for cyclic allocation */spinlock_tlock;};

struct idr_layer 表示一个树节点,layer成员表示该节点位于树的第几层,树叶为第0层,越往树根layer越大。prefix是表示layer的一个ID,同一层的所有节点共享同一个prefix,For layer 0, the prefix mask is all bits except for the lower IDR_BITS (0xFFFFFF00),For layer 1, the prefix mask is all bits except for the lower 2 * IDR_BITS (0xFFFF0000), and so on...

DECLARE_BITMAP(bitmap, IDR_SIZE) 定义了一个包含256位的bitmap数组,对于非树叶的节点每个bit代表一个分支, 当这个分支下包含的所有树叶都被分配满指针时,此位置1。对于树叶,每一位表示对应的指针, 置1表示对应的指针被分配。ary[1<<IDR_BITS]是一个大小为IDR_SIZE的指针数组,每一个指针都指向下一层的树节点,除了树叶例外,树叶的ary数组里的指针不再指向其他的idr_layer, 而是用来存放与ID关联的指针。count表示这个节点已经使用了多少分支,最大为IDR_SIZE,如果为0,那么这个节点可以被释放。

struct idr表示整个树结构,hint表示当前要分配的ID所在的树叶节点,top指向树当前的根节点(离树叶最远),id_free是一个空闲idr_layer链表,用于需要的时候分配新的idr_layer,layers表示这个树当前有多少层,id_free_cnt表示id_free的数量。


IDA定义如下:

#define IDA_CHUNK_SIZE128/* 128 bytes per chunk */#define IDA_BITMAP_LONGS(IDA_CHUNK_SIZE / sizeof(long) - 1)#define IDA_BITMAP_BITS (IDA_BITMAP_LONGS * sizeof(long) * 8)

struct ida_bitmap {longnr_busy;unsigned longbitmap[IDA_BITMAP_LONGS];};

struct ida {struct idridr;struct ida_bitmap*free_bitmap;};


可以明显看到IDA就是IDR+ida_bitmap,ida_bitmap结构free_bitmap表示当前空闲可用的ida_bitmap,nr_busy初始为0,每次分配一个ID, nr_busy + 1,bitmap[IDA_BITMAP_LONGS]是用于IDA记录ID的位表。在IDA中,IDR的树叶节点的ary数组里的指针不再存放与分配的ID相关联的指针,而是存放一个指向ida_bitmap的指针。ida_bitmap指针从free_bitmap成员变量指向的空闲ida_bitmap中取得。然后free_bitmap被置为NULL,所以每次要分配新的ida_bitmap前,必须要事先初始化free_bitmap成员变量。

初始化IDA可以用宏直接完成:

#define IDR_INIT(name)\{\.lock= __SPIN_LOCK_UNLOCKED(name.lock),\}#define DEFINE_IDR(name)struct idr name = IDR_INIT(name)
#define IDA_INIT(name){ .idr = IDR_INIT((name).idr), .free_bitmap = NULL, }#define DEFINE_IDA(name)struct ida name = IDA_INIT(name)

仅仅是初始化了spin_lock。

IDA初始化后free_bitmap = NULL,  idr的id_free也为空,因此在进一步调用其他函数前必须要先预分配bitmap和idr_layer资源,预分配资源通过调用ida_pre_get实现(lib/idr.c):

/** * ida_pre_get - reserve resources for ida allocation * @ida:ida handle * @gfp_mask:memory allocation flag * * This function should be called prior to locking and calling the * following function.  It preallocates enough memory to satisfy the * worst possible allocation. * * If the system is REALLY out of memory this function returns %0, * otherwise %1. */int ida_pre_get(struct ida *ida, gfp_t gfp_mask){/* allocate idr_layers */if (!__idr_pre_get(&ida->idr, gfp_mask))return 0;/* allocate free_bitmap */if (!ida->free_bitmap) {struct ida_bitmap *bitmap;bitmap = kmalloc(sizeof(struct ida_bitmap), gfp_mask);if (!bitmap)return 0;free_bitmap(ida, bitmap);}return 1;}


ida_pre_get首先通过__idr_pre_get分配idr_layer,接着malloc一个ida_bitmap给free_bitmap。

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

int __idr_pre_get(struct idr *idp, gfp_t gfp_mask){while (idp->id_free_cnt < MAX_IDR_FREE) {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 free_bitmap(struct ida *ida, struct ida_bitmap *bitmap){unsigned long flags;if (!ida->free_bitmap) {spin_lock_irqsave(&ida->idr.lock, flags);if (!ida->free_bitmap) {ida->free_bitmap = bitmap;bitmap = NULL;}spin_unlock_irqrestore(&ida->idr.lock, flags);}kfree(bitmap);}


可以看到_idr_pre_get一次分配MAX_IDR_FREE个idr_layer。MAX_IDR_FREE个idr_layer通过layer的ary[0]指针链在一起,idr的id_free指向链表的头部。

ida_pre_get后预分配资源就算完成了,接下来正式分配ID!

以IDA为例(仅分配ID),分配ID的主函数为ida_get_new_above:

/** * ida_get_new_above - allocate new ID above or equal to a start id * @ida:ida handle * @starting_id: id to start search at * @p_id:pointer to the allocated handle * * Allocate new ID above or equal to @starting_id.  It should be called * with any required locks. * * If memory is required, it will return %-EAGAIN, you should unlock * and go back to the ida_pre_get() call.  If the ida is full, it will * return %-ENOSPC. * * @p_id returns a value in the range @starting_id ... %0x7fffffff. */int ida_get_new_above(struct ida *ida, int starting_id, int *p_id){struct idr_layer *pa[MAX_IDR_LEVEL + 1];struct ida_bitmap *bitmap;unsigned long flags;int idr_id = starting_id / IDA_BITMAP_BITS;int offset = starting_id % IDA_BITMAP_BITS;int t, id; restart:/* get vacant slot */t = idr_get_empty_slot(&ida->idr, idr_id, pa, 0, &ida->idr);if (t < 0)return t == -ENOMEM ? -EAGAIN : t;if (t * IDA_BITMAP_BITS >= MAX_IDR_BIT)return -ENOSPC;if (t != idr_id)offset = 0;idr_id = t;/* if bitmap isn't there, create a new one */bitmap = (void *)pa[0]->ary[idr_id & IDR_MASK];if (!bitmap) {spin_lock_irqsave(&ida->idr.lock, flags);bitmap = ida->free_bitmap;ida->free_bitmap = NULL;spin_unlock_irqrestore(&ida->idr.lock, flags);if (!bitmap)return -EAGAIN;memset(bitmap, 0, sizeof(struct ida_bitmap));rcu_assign_pointer(pa[0]->ary[idr_id & IDR_MASK],(void *)bitmap);pa[0]->count++;}/* lookup for empty slot */t = find_next_zero_bit(bitmap->bitmap, IDA_BITMAP_BITS, offset);if (t == IDA_BITMAP_BITS) {/* no empty slot after offset, continue to the next chunk */idr_id++;offset = 0;goto restart;}id = idr_id * IDA_BITMAP_BITS + t;if (id >= MAX_IDR_BIT)return -ENOSPC;__set_bit(t, bitmap->bitmap);if (++bitmap->nr_busy == IDA_BITMAP_BITS)idr_mark_full(pa, idr_id);*p_id = id;/* Each leaf node can handle nearly a thousand slots and the * whole idea of ida is to have small memory foot print. * Throw away extra resources one by one after each successful * allocation. */if (ida->idr.id_free_cnt || ida->free_bitmap) {struct idr_layer *p = get_from_free_list(&ida->idr);if (p)kmem_cache_free(idr_layer_cache, p);}return 0;}


IDA_BITMAP_BITS = (IDA_BITMAP_LONGS * sizeof(long) * 8),这个函数主要功能实现在idr_get_empty_slot函数中,idr_get_empty_slot函数分配一个新的ID大于或等于给定的starting ID,也就是在树叶中寻找一个大于等于starting id的空闲的id slot,如果给定的starting ID大于当前树结构所能支持分配的最大ID号,那么生长这棵树!(分配新的树叶节点,或增长树的层数):

static int idr_get_empty_slot(struct idr *idp, int starting_id,      struct idr_layer **pa, gfp_t gfp_mask,      struct idr *layer_idr){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 = idr_layer_alloc(gfp_mask, layer_idr)))return -ENOMEM;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 (id > idr_max(layers)) {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++;WARN_ON_ONCE(p->prefix);continue;}if (!(new = idr_layer_alloc(gfp_mask, layer_idr))) {/* * 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->count = 0;bitmap_clear(new->bitmap, 0, IDR_SIZE);__move_to_free_list(idp, new);}spin_unlock_irqrestore(&idp->lock, flags);return -ENOMEM;}new->ary[0] = p;new->count = 1;new->layer = layers-1;new->prefix = id & idr_layer_prefix_mask(new->layer);if (bitmap_full(p->bitmap, IDR_SIZE))__set_bit(0, new->bitmap);p = new;}rcu_assign_pointer(idp->top, p);idp->layers = layers;v = sub_alloc(idp, &id, pa, gfp_mask, layer_idr);if (v == -EAGAIN)goto build_up;return(v);}

前面说过idr的top指向树的根,在刚初始化后top等于空,layers等于0,那么会先调用idr_layer_alloc分配一个idr_layer:

static struct idr_layer *idr_layer_alloc(gfp_t gfp_mask, struct idr *layer_idr){struct idr_layer *new;/* this is the old path, bypass to get_from_free_list() */if (layer_idr)return get_from_free_list(layer_idr);/* * Try to allocate directly from kmem_cache.  We want to try this * before preload buffer; otherwise, non-preloading idr_alloc() * users will end up taking advantage of preloading ones.  As the * following is allowed to fail for preloaded cases, suppress * warning this time. */new = kmem_cache_zalloc(idr_layer_cache, gfp_mask | __GFP_NOWARN);if (new)return new;/* * Try to fetch one from the per-cpu preload buffer if in process * context.  See idr_preload() for details. */if (!in_interrupt()) {preempt_disable();new = __this_cpu_read(idr_preload_head);if (new) {__this_cpu_write(idr_preload_head, new->ary[0]);__this_cpu_dec(idr_preload_cnt);new->ary[0] = NULL;}preempt_enable();if (new)return new;}/* * Both failed.  Try kmem_cache again w/o adding __GFP_NOWARN so * that memory allocation failure warning is printed as intended. */return kmem_cache_zalloc(idr_layer_cache, gfp_mask);}

这里传入的第二个参数layer_idr不为空,所以直接从id_free里取一个idr_layer。id_free在前面ida_pre_get里已经分配好了。idr_layer分出来后其成员layer初始化为0(表示此节点为树叶),idr的layers就等于1,表示当前树的层数为1(仅有树叶)。这里要说明一下,idr树的生长是layer从0开始生长的,最多长到4层layer,ID的分配都在树叶,当树叶"不够用"的时候,就会在树叶的上一层节点(如果存在的话)增长新的树叶,如果上层节点的树叶已无法增长(达到256),则创建更上层节点来生长树的层数。如果树叶还没有上一层节点,则创建上层节点,增长树的层数。

while (id > idr_max(layers))循环判断是否需要为分配ID增长树的层次(创建新的非树叶树节点),如果仅增长树叶就可以分配ID的话那这个循环进不去。

接着调用sub_alloc函数为这个ID分配寻找空闲的slot。

/** * sub_alloc - try to allocate an id without growing the tree depth * @idp: idr handle * @starting_id: id to start search at * @pa: idr_layer[MAX_IDR_LEVEL] used as backtrack buffer * @gfp_mask: allocation mask for idr_layer_alloc() * @layer_idr: optional idr passed to idr_layer_alloc() * * Allocate an id in range [@starting_id, INT_MAX] from @idp without * growing its depth.  Returns * *  the allocated id >= 0 if successful, *  -EAGAIN if the tree needs to grow for allocation to succeed, *  -ENOSPC if the id space is exhausted, *  -ENOMEM if more idr_layers need to be allocated. */static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa,     gfp_t gfp_mask, struct idr *layer_idr){int n, m, sh;struct idr_layer *p, *new;int l, id, oid;id = *starting_id; restart:p = idp->top;l = idp->layers;pa[l--] = NULL;while (1) {/* * We run around this while until we reach the leaf node... */n = (id >> (IDR_BITS*l)) & IDR_MASK;m = find_next_zero_bit(p->bitmap, IDR_SIZE, n);if (m == IDR_SIZE) {/* no space available go back to previous layer. */l++;oid = id;id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;/* if already at the top layer, we need to grow */if (id >= 1 << (idp->layers * IDR_BITS)) {*starting_id = id;return -EAGAIN;}p = pa[l];BUG_ON(!p);/* If we need to go up one layer, continue the * loop; otherwise, restart from the top. */sh = IDR_BITS * (l + 1);if (oid >> sh == id >> sh)continue;elsegoto restart;}if (m != n) {sh = IDR_BITS*l;id = ((id >> sh) ^ n ^ m) << sh;}if ((id >= MAX_IDR_BIT) || (id < 0))return -ENOSPC;if (l == 0)break;/* * Create the layer below if it is missing. */if (!p->ary[m]) {new = idr_layer_alloc(gfp_mask, layer_idr);if (!new)return -ENOMEM;new->layer = l-1;new->prefix = id & idr_layer_prefix_mask(new->layer);rcu_assign_pointer(p->ary[m], new);p->count++;}pa[l--] = p;p = p->ary[m];}pa[l] = p;return id;}


这个函数尝试不增长树的层次来分配一个空闲ID slot,从starting_id开始搜索最近的空闲slot返回。首先函数通过while循环利用find_next_zero_bit函数从当前树的根一直索引到大于等于starting ID的第一个空闲slot所在的树叶节点:

id = *starting_id; restart:p = idp->top;l = idp->layers;pa[l--] = NULL;while (1) {/* * We run around this while until we reach the leaf node... */n = (id >> (IDR_BITS*l)) & IDR_MASK;m = find_next_zero_bit(p->bitmap, IDR_SIZE, n);if (m == IDR_SIZE) {/* no space available go back to previous layer. */l++;oid = id;id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;/* if already at the top layer, we need to grow */if (id >= 1 << (idp->layers * IDR_BITS)) {*starting_id = id;return -EAGAIN;}p = pa[l];BUG_ON(!p);/* If we need to go up one layer, continue the * loop; otherwise, restart from the top. */sh = IDR_BITS * (l + 1);if (oid >> sh == id >> sh)continue;elsegoto restart;}if (m != n) {sh = IDR_BITS*l;id = ((id >> sh) ^ n ^ m) << sh;}if (l == 0)break;/* * Create the layer below if it is missing. */if (!p->ary[m]) {new = idr_layer_alloc(gfp_mask, layer_idr);if (!new)return -ENOMEM;new->layer = l-1;new->prefix = id & idr_layer_prefix_mask(new->layer);rcu_assign_pointer(p->ary[m], new);p->count++;}pa[l--] = p;p = p->ary[m];}pa[l] = p;

p = idp->top初始为树的根,l是树的层数,find_next_zero_bit函数返回值m就是需要索引的下层节点在ary数组中的index(这个下层节点就是包含大于或等于starting id的第一个空闲slot所在树叶的那个下层节点,不过有一种情况例外,需要返回上层节点重新执行find_next_zero_bit函数修正下层节点,后面会讨论)。如果下层节点存在(不为空),那么找到下层节点后,赋值给p,进入下层循环。如果下层节点还未创建,那么调用idr_layer_alloc函数创建它,当l=0时表示已经找到树叶节点,退出循环,将树叶节点赋值给pa[0];

find_next_zero_bit函数功能是在一个数组构成的bitmap中找到大于等于offset的下一个0位并返回index。

#define find_next_zero_bit(p,sz,off)_find_next_zero_bit_le(p,sz,off)extern int _find_next_zero_bit_le(const void * p, int size, int offset);

_find_next_zero_bit_le是用ASM汇编来实现的,源文件位于(arch/arm/lib/findbit.S)

/* * Purpose  : Find a 'zero' bit * Prototype: int find_first_zero_bit(void *addr, unsigned int maxbit); */ENTRY(_find_first_zero_bit_le)teqr1, #0beq3fmovr2, #01: ARM(ldrbr3, [r0, r2, lsr #3]) THUMB(lsrr3, r2, #3) THUMB(ldrbr3, [r0, r3])eorsr3, r3, #0xff@ invert bitsbne.L_found@ any now set - found zero bitaddr2, r2, #8@ next bit pointer2:cmpr2, r1@ any more?blo1b3:movr0, r1@ no free bitsmovpc, lrENDPROC(_find_first_zero_bit_le)/* * Purpose  : Find next 'zero' bit * Prototype: int find_next_zero_bit(void *addr, unsigned int maxbit, int offset) */ENTRY(_find_next_zero_bit_le)teqr1, #0beq3bandsip, r2, #7beq1b@ If new byte, goto old routine ARM(ldrbr3, [r0, r2, lsr #3]) THUMB(lsrr3, r2, #3) THUMB(ldrbr3, [r0, r3])eorr3, r3, #0xff@ now looking for a 1 bitmovsr3, r3, lsr ip@ shift off unused bitsbne.L_foundorrr2, r2, #7@ if zero, then no bits hereaddr2, r2, #1@ align bit pointerb2b@ loop for next bitENDPROC(_find_next_zero_bit_le)    .../* * One or more bits in the LSB of r3 are assumed to be set. */.L_found:#if __LINUX_ARM_ARCH__ >= 5rsbr0, r3, #0andr3, r3, r0clzr3, r3rsbr3, r3, #31addr0, r2, r3#elsetstr3, #0x0faddeqr2, r2, #4movner3, r3, lsl #4tstr3, #0x30addeqr2, r2, #2movner3, r3, lsl #2tstr3, #0x40addeqr2, r2, #1movr0, r2#endifcmpr1, r0@ Clamp to maxbitmovlor0, r1movpc, lr


前面说到索引过程中如果下层节点为空(还未分配),那么会根据需要分配下层树节点,或者树叶节点(调用idr_layer_alloc来分配一个idr_layer作为树叶或树节点)。到这里可以看到,sub_alloc主要负责分配slot,创建树叶或树节点(不增长层次),idr_get_empty_slot函数负责调用sub_alloc函数,必要的时候在调用之前创建非树叶节点来生长树的层次。

来看下sub_alloc创建节点并初始化的过程:

/* * Create the layer below if it is missing. */if (!p->ary[m]) {new = idr_layer_alloc(gfp_mask, layer_idr);if (!new)return -ENOMEM;new->layer = l-1;new->prefix = id & idr_layer_prefix_mask(new->layer);rcu_assign_pointer(p->ary[m], new);p->count++;}pa[l--] = p;p = p->ary[m];

可以看到如果节点已经被创建过,则沿着节点继续向下索引。否则创建一个节点,将其成员layer初始化为当前layer-1,设置其prefix成员,最后将当前树节点的ary[]指针数组中对应的指针指向刚刚创建的节点new,当前id_layer的count++。然后将当前layer指向刚刚分出来的idr_layer。

接着while循环会loop到下一层节点,直到最后找到满足条件的(>= starting id)空闲的slot对应的的树叶节点,中途如果遇到还未创建的节点就会调用idr_layer_alloc函数创建它。如果starting id 对应的slot本身就是空闲的,sub_alloc返回这个ID,否则sub_alloc会搜索最近的空闲slot,返回其对应的ID。

m = find_next_zero_bit(p->bitmap, IDR_SIZE, n);if (m == IDR_SIZE) {/* no space available go back to previous layer. */l++;oid = id;id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;/* if already at the top layer, we need to grow */if (id >= 1 << (idp->layers * IDR_BITS)) {*starting_id = id;return -EAGAIN;}p = pa[l];BUG_ON(!p);/* If we need to go up one layer, continue the * loop; otherwise, restart from the top. */sh = IDR_BITS * (l + 1);if (oid >> sh == id >> sh)continue;elsegoto restart;}

这里的 if(m==IDR_SIZE)为了处理一种情况,就是如果在索引过程中,对于某一个layer,find_next_zero_bit找不到空闲位,但这个layer其实是不满的(只不过空闲位小于starting id),那么它在上层layer的bitmap的对应位这时还是0!但此时这个layer其实已经分不出满足大于等于starting id的空闲slot了,此时find_next_zero_bit返回IDR_SIZE,那么sub_alloc就会返回它的上一层layer,调整进入下一个节点来继续索引。

最终sub_alloc返回找到的ID slot,返回到idr_get_empty_slot,最后返回到ida_get_new_above函数。


在接着看ida_get_new_above函数之前,先讨论下idr_get_empty_slot函数中如果给定的starting ID大于当前树结构所能支持分配的最大ID号的情况,那么这时候就会生长这棵树!(分配新的树叶节点,或增长树的层数):

struct idr_layer *p, *new;int layers, v, id;unsigned long flags;id = starting_id;build_up:p = idp->top;layers = idp->layers;     .../* * Add a new layer to the top of the tree if the requested * id is larger than the currently allocated space. */while (id > idr_max(layers)) {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++;WARN_ON_ONCE(p->prefix);continue;}if (!(new = idr_layer_alloc(gfp_mask, layer_idr))) {/* * 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->count = 0;bitmap_clear(new->bitmap, 0, IDR_SIZE);__move_to_free_list(idp, new);}spin_unlock_irqrestore(&idp->lock, flags);return -ENOMEM;}new->ary[0] = p;new->count = 1;new->layer = layers-1;new->prefix = id & idr_layer_prefix_mask(new->layer);if (bitmap_full(p->bitmap, IDR_SIZE))__set_bit(0, new->bitmap);p = new;}rcu_assign_pointer(idp->top, p);idp->layers = layers;v = sub_alloc(idp, &id, pa, gfp_mask, layer_idr);return(v);

idr_max根据layer的数量返回这棵树最大可支持分配的ID减去1(因为ID以0为base):

/* the maximum ID which can be allocated given idr->layers */static int idr_max(int layers){int bits = min_t(int, layers * IDR_BITS, MAX_IDR_SHIFT);return (1 << bits) - 1;}

如果是layer=1的树,一共有1 << IDR_BITS = 256个slot可供分配ID,如果layer=2的树,slot变为256x256个(1 << 2 x IDR_BITS),以此类推。

如果starting id大于idr_max的返回值,首先idr->layers ++,增加这棵树的层数,然后调用idr_layer_alloc函数分配一个新的idr_layer new,分配好以后对new进行初始化,将new->ary[0]指向原先树的根,count =1,new->layers 为layers-1(以0为base),检查之前的根节点p的bitmap是否已满(之前的树是否已无slot可用)如果是就将新的根节点new的bit0置1。最后将idp->top设为新的根new。

idr_get_empty_slot分配好ID以后返回至ida_get_new_above函数。

/* get vacant slot */t = idr_get_empty_slot(&ida->idr, idr_id, pa, 0, &ida->idr);if (t != idr_id)offset = 0;idr_id = t;/* if bitmap isn't there, create a new one */bitmap = (void *)pa[0]->ary[idr_id & IDR_MASK];if (!bitmap) {spin_lock_irqsave(&ida->idr.lock, flags);bitmap = ida->free_bitmap;ida->free_bitmap = NULL;spin_unlock_irqrestore(&ida->idr.lock, flags);if (!bitmap)return -EAGAIN;memset(bitmap, 0, sizeof(struct ida_bitmap));rcu_assign_pointer(pa[0]->ary[idr_id & IDR_MASK],(void *)bitmap);pa[0]->count++;}

对于IDA,树叶的ary[]指针数组里的指针指向ida_bitmap结构,前面分析可知,pa[0]在调用完sub_alloc函数以后指向分配的slot所在的树叶节点。因此pa[0]->ary[idr_id & IDR_MASK]就是与前面idr_get_empty_slot函数找到的空闲slot对应的ary[]数组里的一个指针。这个指针就是指向ida_bitmap的指针,如果为空表示还未分配ida_bitmap,那么就从ida->free_bitmap里分配一个。注意如果 t != idr_id,表示idr分配的空闲的slot大于请求的starting id,那么重置offset为0,表示接下来在ida_bitmap里从头开始搜索空闲slot。

最后在ida_bitmap里分配一个ID slot,并将对应bit置1:

/* lookup for empty slot */t = find_next_zero_bit(bitmap->bitmap, IDA_BITMAP_BITS, offset);if (t == IDA_BITMAP_BITS) {/* no empty slot after offset, continue to the next chunk */idr_id++;offset = 0;goto restart;}id = idr_id * IDA_BITMAP_BITS + t;__set_bit(t, bitmap->bitmap);if (++bitmap->nr_busy == IDA_BITMAP_BITS)idr_mark_full(pa, idr_id);*p_id = id;     ...return 0;

这里 t=IDA_BITMAP_BITS也是为了处理一种情况,表示当前的ida_bitmap无法分配满足大于等于offset的slot,但这个ida_bitmap其实并不满,这时就继续到下一个ida_bitmap中去分配slot。

如果bitmap->nr_busy == IDA_BITMAP_BITS,表示ida_bitmap已经分配满了,那么将这个ida_bitmap对应的树叶节点里的bitmap位置1。最后返回分配好slot对应的id。


原创粉丝点击