IDR分配一个整数,并关联到一个指针。内核很多地方有这个应用场景,如device names,POSIX timers等。
IDR底层通过radix tree实现。相关文件:
lib/radix-tree.c
lib/idr.c
include/idr.h
有3种方式定义并初始化一个idr变量:
1 静态分配,然后初始化
1 2 | static struct idr dca_idr; idr_init(&dca_idr); |
2 静态分配同时初始化,不再需要使用idr_init()初始化
1 | static DEFINE_IDR(dca_idr); |
3 动态分配,然后初始化
1 2 | struct idr *dca_idr = kmalloc(xxx); idr_init(&dca_idr); |
分配ID的旧接口如下:
1 2 | int idr_pre_get( struct idr *idp, gfp_t gfp_mask); int idr_get_new( struct idr *idp, void *ptr, int *id); |
idr_pre_get用在原子上下文之外,用于预分配内存,不需要锁。而idr_get_new需要锁保护(radix tree操作需要锁保护),因此分成了两个接口。
Tejun指出了之前接口的缺陷,并提交了Patch,新的接口如下:
1 2 3 | void idr_preload(gfp_t gfp_mask); int idr_alloc( struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask); void idr_preload_end( void ); |
idr_perload不需要idr指针,它使用percpu buffer,如果分配成功返回非0,并且禁止抢占。
并发修改idr是不允许的,因此idr_alloc需要加锁。idr_alloc()分配[start, end)范围内未用的ID,如果end小于等于0,则等价于最大值。如果没有可用ID,则返回-ENOSPC,返回值大于等于0,则是成功分配的ID。
idr_preload_end,开启抢占。
分配ID典型的用法如下:
1 2 3 4 5 6 7 8 9 10 11 | idr_preload(GFP_KERNEL); spin_lock(&dca_idr_lock); ret = idr_alloc(&dca_idr, dca, 0, 0, GFP_NOWAIT); if (ret >= 0) dca->id = ret; spin_unlock(&dca_idr_lock); idr_preload_end(); if (ret < 0) return ret; |
删除分配的ID:
1 | void *idr_remove( struct idr *idr, unsigned long id) |
由于删除修改了IDR,因此需要上锁。如果id之前不在IDR中,返回NULL,否则返回关联的指针。
销毁IDR
1 | void idr_destroy( struct idr *idr) |
销毁所有IDR的内部内存。IDR变成空,可以释放IDR,或者重新使用。
参考:
https://lwn.net/Articles/536293/
idr: implement idr_alloc() and convert existing users. https://lwn.net/Articles/536019/
Re: [PATCHSET] idr: implement idr_alloc() and convert existing users. https://lwn.net/Articles/536352/
Re: [PATCHSET] idr: implement idr_alloc() and convert existing users. https://lwn.net/Articles/536353/