![apple_guet apple_guet]()
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 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 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 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 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 = (1 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 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);}另:请参考:http://hi.baidu.com/zmdesperado/item/c75ac079c8cfba326cc37c25 01-26 01:44