参考文章,https://blog.csdn.net/liuhangtiant/article/details/80561148 个人认为,有配图,更易于理解
一.当第二块内存的结束地址小于第一块内存的起始地址时:
1.1 ~ 1.5步骤分析
int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, unsigned long flags)
{
bool insert = false;
phys_addr_t obase = base;
phys_addr_t end = base + memblock_cap_size(base, &size);
int i, nr_new;
if (!size)
return 0;
/* special case for empty array */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 1 || type->total_size);
type->regions[0].base = base;
type->regions[0].size = size;
type->regions[0].flags = flags;
memblock_set_region_node(&type->regions[0], nid);
type->total_size = size;
return 0;
}
repeat:
/*
* The following is executed twice. Once with %false @insert and
* then with %true. The first counts the number of regions needed
* to accomodate the new area. The second actually inserts them.
*/
base = obase;
nr_new = 0;
for (i = 0; i < type->cnt; i++) {
struct memblock_region *rgn = &type->regions[i];
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
// rbase为第一块内存的起始地址,end为第二块内存的结束地址
// 1.如果第2块内存的结束地址小于第一块内存的起始地址,说明
// 第二块内存低于第一块内存,此时应将第二块内存放在zone0处
// 第一块内存放在zone1处,交换下位置
// region必须从大到小排列。这里说的大小,是指region所代表的
// 内存的起始/结束地址,也就是说,region0的结束地址,必须小
// 于或等于region1的起始地址
if (rbase >= end) // 1.1
break; // 1.2 break退出循环
if (rend <= base)
continue;
/*
* @rgn overlaps. If it separates the lower part of new
* area, insert that portion.
*/
if (rbase > base) {
nr_new++;
if (insert)
memblock_insert_region(type, i++, base,
rbase - base, nid,
flags);
}
/* area below @rend is dealt with, forget about it */
base = min(rend, end);
} // end for
/* insert the remaining portion */
if (base < end) { // 1.3 成立
nr_new++; // nr_new ++
if (insert) // insert = false 不成立
memblock_insert_region(type, i, base, end - base,
nid, flags);
}
/*
* If this was the first round, resize array and repeat for actual
* insertions; otherwise, merge and return.
*/
if (!insert) { // 1.4 insert = false
while (type->cnt + nr_new > type->max) //type->cnt = 1 nr_new = 1 type->max=128
if (memblock_double_array(type, obase, size) < 0)
return -ENOMEM;
insert = true; // 1.5 insert = true,返回到repeat处
goto repeat;
} else {
memblock_merge_regions(type);
return 0;
}
}
1.6~1.10步骤分析
int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, unsigned long flags)
{
bool insert = false;
phys_addr_t obase = base;
phys_addr_t end = base + memblock_cap_size(base, &size);
int i, nr_new;
if (!size)
return 0;
/* special case for empty array */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 1 || type->total_size);
type->regions[0].base = base;
type->regions[0].size = size;
type->regions[0].flags = flags;
memblock_set_region_node(&type->regions[0], nid);
type->total_size = size;
return 0;
}
repeat: // 1.6 goto 跳到这个地方
/*
* The following is executed twice. Once with %false @insert and
* then with %true. The first counts the number of regions needed
* to accomodate the new area. The second actually inserts them.
*/
base = obase; // 第二块内存的起始地址
nr_new = 0; // nr_new 又重置为0
for (i = 0; i < type->cnt; i++) { // type->cnt = 1
struct memblock_region *rgn = &type->regions[i];
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
if (rbase >= end) // 1.7 第二块内存的结束地址小于第一块内存的起始地址,i=0
break;
if (rend <= base)
continue;
/*
* @rgn overlaps. If it separates the lower part of new
* area, insert that portion.
*/
if (rbase > base) {
nr_new++;
if (insert)
memblock_insert_region(type, i++, base,
rbase - base, nid,
flags);
}
/* area below @rend is dealt with, forget about it */
base = min(rend, end);
} // end for
/* insert the remaining portion */
if (base < end) { // 1.8 base < end成立
nr_new++; // nr_new++ nr_new = 1
if (insert) // insert = true
memblock_insert_region(type, i, base, end - base, nid, flags);// 1.9插入内存块
// 将第二块内存放到regions[0]中,第一块内存放到regions[1]中,i=0
// 同时,type->cnt++,type->total_size += size;
}
/*
* If this was the first round, resize array and repeat for actual
* insertions; otherwise, merge and return.
*/
if (!insert) {
while (type->cnt + nr_new > type->max)
if (memblock_double_array(type, obase, size) < 0)
return -ENOMEM;
insert = true;
goto repeat;
} else {
memblock_merge_regions(type); // 1.9
return 0;
}
}
static void __init_memblock memblock_merge_regions(struct memblock_type *type)
{
int i = 0;
/* cnt never goes below 1 */
while (i < type->cnt - 1) { // 1.10 type->cnt = 2
struct memblock_region *this = &type->regions[i];
struct memblock_region *next = &type->regions[i + 1];
/* 只有当第二个内存块(next region)的起始地址等于第一内存块(this region)的
* 起始地址加上size时,并且 node,flags应该相等,才符号merge条件
*
*/
if (this->base + this->size != next->base ||
memblock_get_region_node(this) !=
memblock_get_region_node(next) ||
this->flags != next->flags) {
BUG_ON(this->base + this->size > next->base);
i++;
continue;
}
this->size += next->size;
/* move forward from next + 1, index of which is i + 2 */
memmove(next, next + 1, (type->cnt - (i + 2)) * sizeof(*next));
type->cnt--;
}
}
二.当第二个内存块的起始地址大于第一个内存块的结束地址时
int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, unsigned long flags)
{
bool insert = false;
phys_addr_t obase = base;
phys_addr_t end = base + memblock_cap_size(base, &size);
int i, nr_new;
if (!size)
return 0;
/* special case for empty array */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 1 || type->total_size);
type->regions[0].base = base;
type->regions[0].size = size;
type->regions[0].flags = flags;
memblock_set_region_node(&type->regions[0], nid);
type->total_size = size;
return 0;
}
repeat:
/*
* The following is executed twice. Once with %false @insert and
* then with %true. The first counts the number of regions needed
* to accomodate the new area. The second actually inserts them.
*/
base = obase;
nr_new = 0;
for (i = 0; i < type->cnt; i++) {
struct memblock_region *rgn = &type->regions[i];
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
if (rbase >= end)
break;
// rend为第一块内存的结束地址,base为第二块内存的起始地址
if (rend <= base) // 2.1 当第二块内存的起始地址大于等于第一块内存的结束地址时
// 不处理,contine,即找出最后一个内存块,base >= rend
continue;
/*
* @rgn overlaps. If it separates the lower part of new
* area, insert that portion.
*/
if (rbase > base) {
nr_new++;
if (insert)
memblock_insert_region(type, i++, base,
rbase - base, nid,
flags);
}
/* area below @rend is dealt with, forget about it */
base = min(rend, end);
} // end for
/* insert the remaining portion */
if (base < end) { // 2.2 成立
nr_new++; // nr_new++ nr_new = 1
if (insert) // insert = false // 2.4 insert = true
memblock_insert_region(type, i, base, end - base,
nid, flags); // 2.5,此时i = 1
}
/*
* If this was the first round, resize array and repeat for actual
* insertions; otherwise, merge and return.
*/
if (!insert) {
while (type->cnt + nr_new > type->max)
if (memblock_double_array(type, obase, size) < 0)
return -ENOMEM;
insert = true; // 2.3 insert = true
goto repeat;
} else {
memblock_merge_regions(type);
return 0;
}
}