linux
下request_mem_region 的粗略理解
文章来源
:
http
:
//gliethttp.cublog.cn
Linux
把基于I/O 映射方式的I/O 端口和基于内存映射方式的I/O 端口资源统称为“I/O 区域” (I/O Region )。I/O Region 仍然是一种I/O 资源,因此它仍然可以用resource 结构类型来描述。
Linux
是以一种倒置的树形结构来管理每一类I/O 资源(如:I/O 端口、外设内存、DMA 和IRQ )的。每一类I/O 资源都对应有一颗倒置的资源树,树中的每一个节点都是一个resource 结构,而树的根结点root 则描述了该类资源的整个资源空间。
1
.
结构体
1
.
1
>
struct
resource iomem_resource
=
{
"PCI mem"
,
0x00000000
,
0xffffffff
,
IORESOURCE_MEM
};
1
.
2
>
struct
resource
{
const
char
*
name
;
unsigned
long
start
,
end
;
unsigned
long
flags
;
struct
resource
*
parent
,
*
sibling
,
*
child
;
};
2
.
调用函数
request_mem_region
(
S1D_PHYSICAL_REG_ADDR
,
S1D_PHYSICAL_REG_SIZE
,
"EpsonFB_RG"
)
#
define
request_mem_region
(
start
,
n
,
name
)
__request_region
(&
iomem_resource
,
(
start
),
(
n
),
(
name
))
__request_region
检查是否可以安全占用起始物理地址S1D_PHYSICAL_REG_ADDR 之后的连续S1D_PHYSICAL_REG_SIZE 字节大小空间
struct
resource
*
__request_region
(
struct
resource
*
parent
,
unsigned
long
start
,
unsigned
long
n
,
const
char
*
name
)
{
struct
resource
*
res
=
kmalloc
(
sizeof
(*
res
),
GFP_KERNEL
);
if
(
res
)
{
memset
(
res
,
0
,
sizeof
(*
res
));
res
->
name
=
name
;
res
->
start
=
start
;
res
->
end
=
start
+
n
-
1
;
res
->
flags
=
IORESOURCE_BUSY
;
write_lock
(&
resource_lock
);
for
(;;)
{
struct
resource
*
conflict
;
conflict
=
__request_resource
(
parent
,
res
);
//sibling parent
下的所有单元, 检测申请部分是否存在交叠冲突
if
(!
conflict
)
//conflict=0;
申请成功, 正常安置了[start,end] 到相应位置
break
;
if
(
conflict
!=
parent
)
{
parent
=
conflict
;
if
(!(
conflict
->
flags
&
IORESOURCE_BUSY
))
continue
;
}
kfree
(
res
);
//
检测到了资源交叠冲突,kfree 归还kmalloc 申请的内存
res
=
NULL
;
break
;
}
write_unlock
(&
resource_lock
);
}
return
res
;
}
static
struct
resource
*
__request_resource
(
struct
resource
*
root
,
struct
resource
*
new
)
{
unsigned
long
start
=
new
->
start
;
unsigned
long
end
=
new
->
end
;
struct
resource
*
tmp
,
**
p
;
if
(
end
<
start
)
return
root
;
if
(
start
<
root
->
start
)
return
root
;
if
(
end
>
root
->
end
)
return
root
;
p
=
&
root
->
child
;
//root
下的第一个链表元素*p.[child 链表是以I/O 资源物理地址从低到高的顺序排列的]
for
(;;)
{
tmp
=
*
p
;
if
(!
tmp
||
tmp
->
start
>
end
)
{
new
->
sibling
=
tmp
;
*
p
=
new
;
//
可以从root->child=null 开始我们的分析考虑, 此时tmp=null, 那么第一个申请将以!tmp 条件满足而进入
//
这时root->child 的值为new 指针,new->sibling = tmp = null; 当第二次申请发生时: 如果tmp->start > end 成立,
//
那么,root->child 的值为new 指针,new->sibling = tmp; 这样就链接上了,空间分布图如:
//child=[start,end]-->[tmp->start,tmp->end](1);
如果条件tmp->start > end 不成立,那么只能是!tmp 条件进入
//
那么,root->child 的值不变,tmp->sibling = new;new->sibling = tmp = null 这样就链接上了,空间分布图如:
//child=[child->start,child->end]-->[start,end](2);
//
当第三次申请发生时: 如果start 在(2) 中的[child->end,end] 之间, 那么tmp->end < start 将成立, 继而continue,
//
此时tmp = (2) 中的[start,end], 因为tmp->start < end, 所以继续执行p = &tmp->slibing = null,
//
因为tmp->end > start, 所以资源冲突, 返回(2) 中的[start,end] 域
//
综上的两个边界值情况和一个中间值情况的分析, 可以知道代码实现了一个从地地址到高地址的顺序链表
//
模型图:childe=[a,b]-->[c,d]-->[e,f], 此时有一个[x,y] 需要插入进去,tmp 作为sibling 指针游动
//tmp
指向child=[a,b],
//tmp
指向[a,b], 当tmp->start>y 时, 插入后的链接图为:child=[x,y]-->[a,b]-->[c,d]-->[e,f]-->null; 当tmp->end>=x 时, 冲突返回tmp
//tmp
指向[c,d], 当tmp->start>y 时, 插入后的链接图为:child=[a,b]-->[x,y]-->[c,d]-->[e,f]-->null; 当tmp->end>=x 时, 冲突返回tmp
//tmp
指向[e,f], 当tmp->start>y 时, 插入后的链接图为:child=[a,b]-->[c,d]-->[x,y]-->[e,f]-->null; 当tmp->end>=x 时, 冲突返回tmp
//tmp
指向null , 插入后的链接图为:child=[a,b]-->[c,d]-->[e,f]-->[x,y]-->null;
//
顺利的达到了检测冲突, 顺序链接的目的
new
->
parent
=
root
;
return
NULL
;
}
p
=
&
tmp
->
sibling
;
if
(
tmp
->
end
<
start
)
continue
;
return
tmp
;
}
}