下图为示意图,读者可以参考该图来阅读代码。
个人认为对Linux下I/O资源的管理如掌握__request_region函数就能掌握其精髓。
Linux下对I/O资源主要用结构体resource来管理,管理的方法就是用resource来描述使用的I/O资源的状态,并将这些resource用如下两个resource作为表头按地址大小的顺序链接起来。
struct resource ioport_resource = {
.name = "PCI IO",
.start = 0,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
EXPORT_SYMBOL(ioport_resource);
struct resource iomem_resource = {
.name = "PCI mem",
.start = 0,
.end = -1,
.flags = IORESOURCE_MEM,
};
__request_region函数的主要功能为:查找resource链表中是否有与申请的I/O资源有冲突,如冲突则返回NULL,如不冲突则将新申请resource按resource地址从小到大的顺放插入至以ioport_resource或iomem_resource为表头(root)的单向指针链表中
/*
* This is compatibility stuff for IO resources.*
* Note how this, unlike the above, knows about
* the IO flag meanings (busy etc).
*
* request_region creates a new busy region.
*
* check_region returns non-zero if the area is already busy.
*
* release_region releases a matching busy region.
*/
/**
* __request_region - create a new busy resource region
* @parent: parent resource descriptor
* @start: resource start address
* @n: resource region size
* @name: reserving caller's ID string
* @flags: IO resource flags
*/
struct resource * __request_region(struct resource *parent,
resource_size_t start, resource_size_t n,
const char *name, int flags)
{
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL );//申请resource资源
if (!res)
return NULL;
res->name = name;
res->start = start;
res->end = start + n - 1;
res->flags = IORESOURCE_BUSY ;
res->flags |= flags;
write_lock(&resource_lock);
for (;;) {
struct resource *conflict;
conflict = __request_resource(parent, res); //从parent为表头的资源链表中检查是否当前申请资源冲突,如冲突则返回冲突资源,不冲突则将新申请资源resource结构体插入链表中
if (!conflict) //没有冲突资源(说明申请资源成功)则返回
break;
if (conflict != parent) { //如冲突资源为不是parent,则将冲突资源作为parent,此处可能有人会问为什么冲突资源不是parenet就将conflict作为 parent呢,将在下面回答该问题。
parent = conflict;
if (!(conflict->flags & IORESOURCE_BUSY))// IORESOURCE_BUSY表示正在申请的资源,此处表示如果冲突资源不属正在申请的资源则继续申请
continue;//,继续循环时parent发生了变化
}
/* Uhhuh, that didn't work out.. */ //申请资源与parent资源冲突退出。
kfree(res);
res = NULL;
break;
}
write_unlock(&resource_lock);
return res;
}
EXPORT_SYMBOL(__request_region);
下面我们再看__request_resource函数
该函数先确保新申请资源在root的范围之内,接着从第
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
resource_size_t start = new->start;
resource_size_t end = new->end;
struct resource *tmp, **p;
//检查申请资源与parent是否冲突
if (end < start)
return root;
if (start < root->start)
return root;
if (end > root->end)
return root;
//如上已确保新申请资源在parent的范围之内(条件A)
p = &root->child;//从第1个儿子开始查找是否与链表中的资源冲突
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {//!tmp,说明要对比资源不存在(条件B)。 tmp->start > end,说明对比资源在新增加资源后面(条件C)
//将新增资源插入到对比资源的前面(所以资源resource在链表中是以地址范围从小到大排序,如示意图所示)
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling;//从如上if 条件tmp->start > end不成立得出对比资源有可能与申请资源冲突或其地址范围在当前对比资源的后面,将兄弟指针保存以便后续用兄弟资源与新申请资源进行比较。
if (tmp->end < start)//如成立说明新申请资源在当前所对比资源的后面,则继续用兄弟资源与新资源进行对比。不成立说明新申请资源与当前资源有冲突
continue;
return tmp;//,tmp->end < start不成立,则说明有冲突,
}
}
解疑:在__request_region函数中为什么冲突资源不是parenet就将conflict作为 parent呢?
对此处的回答我也只是通过代码推理,并无找代码中的实例
假设您申请如上130~180的I/O空间,后来又将135~175的I/O resource 释放掉
您再申请135~145的I/O空间不就会遇到冲突资源,并将conflict资源作为parent再申请的情况了吗!