一.結構體
1.idr結構體
[cpp]
struct idr {
struct idr_layer __rcu *top; //idr_layer頂層,32叉樹的根
struct idr_layer *id_free; //指向idr_layer的空閒鏈表
int layers; //idr_layer的層數量
int id_free_cnt; //idr_layer空閒鏈表中剩余的idr_layer個數
spinlock_t lock;
};
2.idr_layer結構體
[cpp
struct idr_layer {
unsigned long bitmap; //標記位圖,標記使用情況
struct idr_layer __rcu *ary[1<<IDR_BITS]; //子idr_layer數組ary[32]
int count; //ary數組使用情況
int layer; //層號
struct rcu_head rcu_head;
};
在32位系統中IDR_BITS的取值為5
[cpp]
#if BITS_PER_LONG == 32
# define IDR_BITS 5
# define IDR_FULL 0xfffffffful
# define TOP_LEVEL_FULL (IDR_FULL >> 30)
#elif BITS_PER_LONG == 64
# define IDR_BITS 6
# define IDR_FULL 0xfffffffffffffffful
# define TOP_LEVEL_FULL (IDR_FULL >> 62)
#else
# error "BITS_PER_LONG is not 32 or 64"
#endif
二.idr的初始化
[cpp]
#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)
定義一個idr結構體並賦值
三.分配id
1.idr_pre_get
[cpp]
int idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
while (idp->id_free_cnt < IDR_FREE_MAX) { //IDR_FREE_MAX=14
struct idr_layer *new; //定義新的idr_layer結構體指針
new = kmem_cache_zalloc(idr_layer_cache, gfp_mask); //分配*new內存空間
if (new == NULL)
return (0);
move_to_free_list(idp, new); //-->move_to_free_list
}
return 1;
}
EXPORT_SYMBOL(idr_pre_get);
move_to_free_list
[cpp]
static void move_to_free_list(struct idr *idp, struct idr_layer *p)
{
unsigned long flags;
spin_lock_irqsave(&idp->lock, flags);
__move_to_free_list(idp, p); //-->__move_to_free_list
spin_unlock_irqrestore(&idp->lock, flags);
}
__move_to_free_list
[cpp]
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++;
}
第一次循環結果
接著循環
再接著
一直這樣下去直到循環結束(14次)
2.idr_get_new和idr_get_new_above
idr_get_new
[cpp]
int idr_get_new(struct idr *idp, void *ptr, int *id)
{
int rv;
rv = idr_get_new_above_int(idp, ptr, 0);
if (rv < 0)
return _idr_rc_to_errno(rv);
*id = rv;
return 0;
}
EXPORT_SYMBOL(idr_get_new);
idr_get_new_above
[cpp]
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);
if (rv < 0)
return _idr_rc_to_errno(rv);
*id = rv;
return 0;
}
EXPORT_SYMBOL(idr_get_new_above);
兩個函數都會調用idr_get_new_above_int函數,差別在於starting_id不同
下面分情況討論,先以id為0走個過場
idr的top簡稱為根top,free簡稱為根free均為idr_layer指針類型,分別指向使用中和空閒idr_layer鏈表頭
[cpp]
static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)
{
struct idr_layer *pa[MAX_LEVEL]; //MAX_LEVEL=7
int id;
id = idr_get_empty_slot(idp, starting_id, pa); //-->idr_get_empty_slot
if (id >= 0) {
rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],(struct idr_layer *)ptr
//pa[0]->ary[0]=ptr 也就是idr_layer14->ary[0]=ptr
pa[0]->count++; //idr_layer14->count++
idr_mark_full(pa, id); //設置其位圖-->走完0過場的效果見圖c
}
return id;
}
idr_get_empty_slot
[cpp]
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; //按常規出牌吧,假設這個為0
build_up:
p = idp->top; //根top指向的idr_layer NULL
layers = idp->layers; //獲取layers層數量(0)
if (unlikely(!p)) { //第一次運行idp->top=NULL,所以if條件為真,執行if分支的結果參考 圖A
if (!(p = get_from_free_list(idp))) //>>>1-->get_from_free_list 從根free中獲取一個idr_layer14
return -1;
p->layer = 0; //指定idr_layer14的層號為0
layers = 1; //layers層數量設為1
}
//layers<6 && id>=2^(layers*5) 看需不需要增加層數 見圖B
while ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) {
layers++;
if (!p->count) {
p->layer++;
continue;
}
if (!(new = get_from_free_list(idp))) {
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); //根top指向idr_layer14
idp->layers = layers; //設置更新idr->layers層數量
//----------------------------------------------分割線----------------------------------------------
//以上部分主要處理layer相關,以下部分主要處理id相關
v = sub_alloc(idp, &id, pa); //>>>2-->sub_alloc
if (v == IDR_NEED_TO_GROW) //IDR_NEED_TO_GROW=-2需要擴大
goto build_up;
return(v);
}
圖A:
圖B
>>>get_from_free_list 從idr空閒idr_layer鏈表中獲取第一個idr_layer
[cpp]
static struct idr_layer *get_from_free_list(struct idr *idp)
{
struct idr_layer *p; //定義一個idr_layer指針
unsigned long flags;
spin_lock_irqsave(&idp->lock, flags);
if ((p = idp->id_free)) { //根free獲取一個空閒idr_layer
idp->id_free = p->ary[0]; //idr空閒鏈表指針指向第二個idr_layer
idp->id_free_cnt--; //idr的空閒idr_layer個數減1(14-1)
p->ary[0] = NULL; //斷開第一個idr_layer和第二個idr_layer的聯系
}
spin_unlock_irqrestore(&idp->lock, flags);
return(p);
}
這裡先穿插一下32進制的計算,上面圖B中2^0,2^5,2^10,2^15,2^20,2^25可以(32=2^5)理解成32^0,32^1,32^2,32^3,32^3,32^4,32^5
那麼用32進制表達一個十進制數id可以套用一下公式
a的值屬於[0,31]
an的值如何獲得id/(32^n)即可,等同於id/(2^5^n)等同於id/((1<<5)^n)
an-1的值如何獲得id>>(5*(n-1))即可
>>>sub_alloc
[cpp]
static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa)
{
int n, m, sh;
struct idr_layer *p, *new;
int l, id, oid;
unsigned long bm;
id = *starting_id;
restart:
p = idp->top; //根top
l = idp->layers; //l=1
pa[l--] = NULL; //p[1]=NULL;l=0
while (1) {
n = (id >> (IDR_BITS*l)) & IDR_MASK; //計算對應的n值,屬於[0,31]
bm = ~p->bitmap; //取反位圖
m = find_next_bit(&bm, IDR_SIZE, n); //>>>1 find_next_bit 位圖中偏移量為n處查找'1'
if (m == IDR_SIZE) { //位圖滿了
l++;
oid = id;
id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;
if (id >= 1 << (idp->layers * IDR_BITS)) {
*starting_id = id;
return IDR_NEED_TO_GROW;
}
p = pa[l];
BUG_ON(!p);
sh = IDR_BITS * (l + 1);
if (oid >> sh == id >> sh)
continue;
else
goto restart;
}
if (m != n) { //期望的n值被占用,但可找到可用的m值
sh = IDR_BITS*l;
id = ((id >> sh) ^ n ^ m) << sh; //>>>2 重新計算id值
}
if ((id >= MAX_ID_BIT) || (id < 0))
return IDR_NOMORE_SPACE;
if (l == 0) //l==0跳出while循環
break;
if (!p->ary[m]) {
new = get_from_free_list(idp);
if (!new)
return -1;
new->layer = l-1;
rcu_assign_pointer(p->ary[m], new);
p->count++;
}
pa[l--] = p;
p = p->ary[m];
}
pa[l] = p; //pa[0]=p 也就是idr_layer14
return id;
}
>>>find_next_bit
[cpp] view plaincopy
#define find_next_bit(p,sz,off) _find_next_bit_le(p,sz,off) //>>_find_next_bit_le
該宏的意思是在p指向的(大小為sz的)位圖表中的第off個位置開始找尋可用(為"1")的格子,找到返回該位
_find_next_bit_le是匯編代碼實現的定義在/arch/arm/lib/findbit.S
[cpp]
ENTRY(_find_next_bit_le)
teq r1, #0
beq 3b
ands ip, r2, #7
beq 1b @ If new byte, goto old routine
ARM( ldrb r3, [r0, r2, lsr #3] )
THUMB( lsr r3, r2, #3 )
THUMB( ldrb r3, [r0, r3] )
movs r3, r3, lsr ip @ shift off unused bits
bne .L_found
orr r2, r2, #7 @ if zero, then no bits here
add r2, r2, #1 @ align bit pointer
b 2b @ loop for next bit
ENDPROC(_find_next_bit_le)
.L_found找到合適的跳轉
[cpp]
.L_found:
#if __LINUX_ARM_ARCH__ >= 5
rsb r0, r3, #0
and r3, r3, r0
clz r3, r3
rsb r3, r3, #31
add r0, r2, r3
#else
tst r3, #0x0f
addeq r2, r2, #4
movne r3, r3, lsl #4
tst r3, #0x30
addeq r2, r2, #2
movne r3, r3, lsl #2
tst r3, #0x40
addeq r2, r2, #1
mov r0, r2
#endif
cmp r1, r0 @ Clamp to maxbit
movlo r0, r1
mov pc, lr
>>>id值的計算的補充說明
首先前面n的取值n = (id >> (IDR_BITS*l)) & IDR_MASK;
IDR_MASK的定義#define IDR_MASK ((1 << IDR_BITS)-1)也就是說IDR_MASK=31等於2進制的1,1111b
所以&IDR_MASK只是框定n值落在0~31之間,掩碼作用
那麼不出意外的話n = (id >> (IDR_BITS*l))
接著
sh = IDR_BITS*l;
id = ((id >> sh) ^ n ^ m) << sh;
帶入表達式中
id=((id >> IDR_BITS*l) ^ (id >> (IDR_BITS*l)) ^ m) << IDR_BITS*l;
異或的操作是相同為1,不同為0,結合起來化簡得
id = ((1 ^ m) << sh=m<<sh
圖C
^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_
已經借用id0走了過場,下面分析下其他情況
[cpp]
static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)
{
struct idr_layer *pa[MAX_LEVEL]; //定義父idr_layer數組
int id;
id = idr_get_empty_slot(idp, starting_id, pa); //獲取id
if (id >= 0) {
rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],(struct idr_layer *)ptr);
//pa[0]->ary[id]=ptr
pa[0]->count++; //idr_layer->count++
idr_mark_full(pa, id); //標記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; //獲取根top
layers = idp->layers; //獲取層數量 layers=1
if (unlikely(!p)) { //FALSE
if (!(p = get_from_free_list(idp)))
return -1;
p->layer = 0;
layers = 1;
}
while ((layers < 6) && (id >= (1 << (layers*5)))) { //參考圖B,如果id值超過或等於對應層所能容納的最大數,則進入循環
layers++; //增加層數量
if (!p->count) { //0~31沒使用,直接使用32就屬於這種情況
p->layer++; //由於32需要添加1層的,所以之前的層的層號需要+1
continue; //層數量也需要加1
}
if (!(new = get_from_free_list(idp))) { //空閒鏈表中獲取新的idr_layer
spin_lock_irqsave(&idp->lock, flags); //分配失敗,--空閒idr_layer鏈表缺貨
for (new = p; p && p != idp->top; new = p) { //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; //新的idr_layer->ary[0]指向舊的idr_layer
new->count = 1; //新的idr_layer計數加1
new->layer = layers-1; //設置新的idr_layer的層號
if (p->bitmap == IDR_FULL) //若舊的(葉子)idr_layer的id全用過了
__set_bit(0, &new->bitmap); //那麼標記下新(父)idr_layer位圖的第0位
p = new; //根top指向新的idr_layer
}
rcu_assign_pointer(idp->top, p); //設置根top
idp->layers = layers; //更新層數量
v = sub_alloc(idp, &id, pa); //獲取id
if (v == IDR_NEED_TO_GROW) //該層id號全用完了,必須擴大idr_layer層數量
goto build_up;
return(v);
}
static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa)
{
int n, m, sh;
struct idr_layer *p, *new;
int l, id, oid;
unsigned long bm;
id = *starting_id;
restart:
p = idp->top; //獲取根top
l = idp->layers; //獲取層數量l=1
pa[l--] = NULL; //pa[1]=NULL,l=0
while (1) {
n = (id >> (5*l)) & IDR_MASK; //n做處理 屬於[0,31]
bm = ~p->bitmap; //位圖取反
m = find_next_bit(&bm, IDR_SIZE, n); //查找n開始能用的位
if (m == IDR_SIZE) { //id表滿了
l++; 層數+1
oid = id;
id = (id | ((1 << (5 * l)) - 1)) + 1; //id或上掩碼再+1
if (id >= 1 << (idp->layers * 5)) { //需要添加層
*starting_id = id;
return IDR_NEED_TO_GROW;
}
p = pa[l];
BUG_ON(!p);
sh = 5 * (l + 1);
if (oid >> sh == id >> sh)
continue;
else
goto restart;
}
if (m != n) { //期望id給用但有可用id
sh = 5*l;
id = ((id >> sh) ^ n ^ m) << sh; //id設置為可用id
}
if ((id >= MAX_ID_BIT) || (id < 0))
return IDR_NOMORE_SPACE;
if (l == 0) //一層層循環計算直到到達葉子處l才為0
break;
if (!p->ary[m]) { //葉子m為空
new = get_from_free_list(idp); //從空閒鏈表拿一個idr_layer
if (!new)
return -1;
new->layer = l-1; //設置新鏈表層數
rcu_assign_pointer(p->ary[m], new); //葉子m指向新鏈表
p->count++; //使用計數加1
}
pa[l--] = p; //pa[大]=節點
p = p->ary[m]; //p=節點->葉子m
}
pa[l] = p; //pa[小]=葉子
return id;
}
來個效果圖id=4吧
id=32情況(idr_layer13的位圖1標記錯了)
1024情況
四.查找id
1.idr_find
[cpp]
void *idr_find(struct idr *idp, int id)
{
int n;
struct idr_layer *p;
p = rcu_dereference_raw(idp->top); //獲取根top
if (!p)
return NULL;
n = (p->layer+1) * IDR_BITS; //計算最外層的n值
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_raw(p->ary[(id >> n) & IDR_MASK]); //一次獲取an ... a0
}
return((void *)p);
}
EXPORT_SYMBOL(idr_find);
前面講過32進制的id值算法
當構建完idr機制之後
id=top->ary[an]->ary[a(n-1)]->....->ary[a0]來獲得
借助圖片分析下(idr_layer13的位圖標記有錯)
五idr操作
1. idr_remove idr_remove_all 移除
[cpp] www.2cto.com
void idr_remove(struct idr *idp, int id)
{
struct idr_layer *p;
struct idr_layer *to_free;
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]) {
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);
kmem_cache_free(idr_layer_cache, p);
}
return;
}
EXPORT_SYMBOL(idr_remove);
移除全部
[cpp]
void idr_remove_all(struct idr *idp)
{
int n, id, max;
int bt_mask;
struct idr_layer *p;
struct idr_layer *pa[MAX_LEVEL];
struct idr_layer **paa = &pa[0];
n = idp->layers * IDR_BITS;
p = idp->top;
rcu_assign_pointer(idp->top, NULL);
max = 1 << n;
id = 0;
while (id < max) {
while (n > IDR_BITS && p) {
n -= IDR_BITS;
*paa++ = p;
p = p->ary[(id >> n) & IDR_MASK];
}
bt_mask = id;
id += 1 << n;
/* Get the highest bit that the above add changed from 0->1. */
while (n < fls(id ^ bt_mask)) {
if (p)
free_layer(p);
n += IDR_BITS;
p = *--paa;
}
}
idp->layers = 0;
}
EXPORT_SYMBOL(idr_remove_all);
2.idr_replace 替換
[cpp]
void *idr_replace(struct idr *idp, void *ptr, int id)
{
int n;
struct idr_layer *p, *old_p;
p = idp->top;
if (!p)
return ERR_PTR(-EINVAL);
n = (p->layer+1) * IDR_BITS;
id &= MAX_ID_MASK;
if (id >= (1 << n))
return ERR_PTR(-EINVAL);
n -= IDR_BITS;
while ((n > 0) && p) {
p = p->ary[(id >> n) & IDR_MASK];
n -= IDR_BITS;
}
n = id & IDR_MASK;
if (unlikely(p == NULL || !test_bit(n, &p->bitmap)))
return ERR_PTR(-ENOENT);
old_p = p->ary[n];
rcu_assign_pointer(p->ary[n], ptr);
return old_p;
}
EXPORT_SYMBOL(idr_replace);
六.idr空閒鏈表的銷毀
idr_destroy
[cpp]
void idr_destroy(struct idr *idp)
{
while (idp->id_free_cnt) {
struct idr_layer *p = get_from_free_list(idp);
kmem_cache_free(idr_layer_cache, p);
}
}
EXPORT_SYMBOL(idr_destroy);
七.用法
1.api函數
[cpp]
void *idr_find(struct idr *idp, int id); //查找id對應的指針
int idr_pre_get(struct idr *idp, gfp_t gfp_mask); //分配idr_layer空閒鏈表
int idr_get_new(struct idr *idp, void *ptr, int *id); //獲取id,捆綁指針ptr
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id); //起始數值獲取id,捆綁指針ptr
int idr_for_each(struct idr *idp,int (*fn)(int id, void *p, void *data), void *data);
void *idr_get_next(struct idr *idp, int *nextid);
void *idr_replace(struct idr *idp, void *ptr, int id); //替換id捆綁的指針
void idr_remove(struct idr *idp, int id); //移除id
void idr_remove_all(struct idr *idp); //移除所有id
void idr_destroy(struct idr *idp); //銷毀idr_layer空閒鏈表
void idr_init(struct idr *idp); //初始化idr
2.大致用法
1.idr_init聲明設置idr
2.idr_pre_get分配空閒idr_layer鏈表
3.id_get_new/idr_get_new_above分配id並將id與指針ptr捆綁
4.利用idr_find根據id獲取指針ptr
5.idr_remove/idr_remove_all移除分配的id
6.idr_destroy銷毀空閒idr_layer鏈表
7.idr_replace替換id