kernel: 5.10
arch: arm64
/**
* memblock_isolate_range - isolate given range into disjoint memblocks
* @type: memblock type to isolate range for
* @base: base of range to isolate
* @size: size of range to isolate
* @start_rgn: out parameter for the start of isolated region
* @end_rgn: out parameter for the end of isolated region
*
* Walk @type and ensure that regions don't cross the boundaries defined by
* [@base, @base + @size). Crossing regions are split at the boundaries,
* which may create at most two more regions. The index of the first
* region inside the range is returned in *@start_rgn and end in *@end_rgn.
*
* Return:
* 0 on success, -errno on failure.
*/
static int __init_memblock memblock_isolate_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int *start_rgn, int *end_rgn)
{
phys_addr_t end = base + memblock_cap_size(base, &size);
int idx;
struct memblock_region *rgn;
*start_rgn = *end_rgn = 0;
if (!size)
return 0;
/* we'll create at most two more regions */
while (type->cnt + 2 > type->max)
if (memblock_double_array(type, base, size) < 0)
return -ENOMEM;
for_each_memblock_type(idx, type, rgn) {
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
if (rbase >= end)
break;
if (rend <= base)
continue;
if (rbase < base) {
/*
* @rgn intersects from below. Split and continue
* to process the next region - the new top half.
*/
rgn->base = base;
rgn->size -= base - rbase;
type->total_size -= base - rbase;
memblock_insert_region(type, idx, rbase, base - rbase,
memblock_get_region_node(rgn),
rgn->flags);
} else if (rend > end) {
/*
* @rgn intersects from above. Split and redo the
* current region - the new bottom half.
*/
rgn->base = end;
rgn->size -= end - rbase;
type->total_size -= end - rbase;
memblock_insert_region(type, idx--, rbase, end - rbase,
memblock_get_region_node(rgn),
rgn->flags);
} else {
/* @rgn is fully contained, record it */
if (!*end_rgn)
*start_rgn = idx;
*end_rgn = idx + 1;
}
}
return 0;
}
memblock_isolate_range实际上就是通过遍历memblock_type的所有memblock_region区间,将请求区间(base,size)与memblock_type重叠的memblock_region区间分离出来,如果请求区间跨越两个memblock_region区间的边界,则会创建新的memblock_region区间并插入。最后通过start_rgn 和end_rgn记录分离区间的索引,以供后续操作,当然此时的区间与原有区间可能已经不一样了,它只代表重叠的部分。
这里有一个例子:
from:https://stackoverflow.com/questions/58525960/about-linux-v5-4-kernel-arm64-memblock-init-fdt-enforce-memory-region
As an example think of a memory range 0-10 and 3 memory blocks A[0-3], B[4-8], C[9-10], with crash kernel being on 2-6. In this case memblock_isolate_range() will split A to two blocks A1[0-1] and A2[2-3], and B to B1[4-6] and B2[6-8], and will put the index of A1 on start_rgn and index of B2 on end_rgn. So between start_rgn and end_rgn we’ll get A2[2-3] and B1[4-6] that include the reserved region.
假设有一段内存区域【0,9】,当前已经有三段memblock_region区间,物理地址区间分别为[0,3], [4,8],[9,10], 现在有段请求物理区间地址为[2,6],经过memblock_isolate_range,会划分为[0,2],[2,3],[4,6],[6,8],[9,10]的memblock_region区间,也就是会创建[0,2], [2,3], [4,6], [6,8]几个区间,start_rgn和end_rgn分别记录了重叠区间的起始和结束索引,此处分别为1和3(比实际加1)
从上面的例子可以看出,memblock_isolate_range的目的是为了找到与之前区间有重叠的部分,并为重叠部分新建区间,同时将之前区域分割成与请求区间有重叠的部分和没有重叠的部分,将没有重叠的部分的区间重新插入到内存区域。如上原有区间[0,3], [4,8]与请求区间[2,6]产生重叠,则将其分割为[0,2] [2,3],[4,6],[6,8], 其中原有区间分割出的[0,2],[6,8]重新插入到内存区域,[2,3],[4,6]将作为隔离区间等待处理。
如下通过打印可以看到: