memblock(内存块)属于早期引导期间管理内存区域的方法之一,而通常的内核内存分配器尚未启动、运行。memblock将系统内存视为连续内存的集合区域,它属于在高地址管理内存, 维护两个链表, 即memory和reserved。memory链表维护系统的内存信息(在初始化阶段通过bios获取),对于任何内存分配,先去查找memory链表,然后在reserve链表上记录(新增一个节点或合并)。
memblock_reserve
x86系列架构中,memblock_reserve函数最早在early_reserve_memory函数中调用,执行顺序为:
start_kernel -> setup_arch -> early_reserve_memory -> memblock_reserve。
int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
{
phys_addr_t end = base + size - 1;
memblock_dbg("%s: [%pa-%pa] %pS\n", __func__,
&base, &end, (void *)_RET_IP_);
return memblock_add_range(&memblock.reserved, base, size, MAX_NUMNODES, 0);
}
memblock_add_range函数包含五个参数:memblock_type对象;内存区域的物理基地址;内存区域的大小;最大 NUMA 节点数;标志参数。先看memblock定义:
struct memblock memblock __initdata_memblock = {
.memory.regions = memblock_memory_init_regions,
.memory.cnt = 1, /* empty dummy entry */
.memory.max = INIT_MEMBLOCK_REGIONS,
.memory.name = "memory",
.reserved.regions = memblock_reserved_init_regions,
.reserved.cnt = 1, /* empty dummy entry */
.reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS,
.reserved.name = "reserved",
.bottom_up = false,
.current_limit = MEMBLOCK_ALLOC_ANYWHERE,
};
__initdata_memblock 展开:
define __initdata_memblock __meminitdata
...
#define __meminitdata __section(".meminit.data")
在.meminit.data段加载数据
memblock.reserved整理到结构大致为:
struct memblock_type reserved {
unsigned long cnt = 1;
unsigned long max = INIT_MEMBLOCK_RESERVED_REGIONS;
phys_addr_t total_size;
struct memblock_region *regions = memblock_reserved_init_regions, // 128个内存保留区域
char *name = "reserved";
};
memblock_reserved_init_regions定义如下:
static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock;
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS];
#endif
/**
* struct memblock_region - represents a memory region
* @base: base address of the region
* @size: size of the region
* @flags: memory region attributes
* @nid: NUMA node id
*/
struct memblock_region {
phys_addr_t base;
phys_addr_t size;
enum memblock_flags flags;
#ifdef CONFIG_NUMA
int nid;
#endif
};
进入memblock_add_range:
static int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, enum memblock_flags flags)
{
bool insert = false;
phys_addr_t obase = base;
phys_addr_t end = base + memblock_cap_size(base, &size); // 得到内存区域末端地址
int idx, nr_new;
struct memblock_region *rgn;
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:
base = obase;
nr_new = 0;
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) { // 如果获取到的基地址大于传入的基地址
#ifdef CONFIG_NUMA
WARN_ON(nid != memblock_get_region_node(rgn));
#endif
WARN_ON(flags != rgn->flags);
nr_new++;
if (insert)
memblock_insert_region(type, idx++, base,
rbase - base, nid,
flags); // 将当前内存区域插入内存块
}
/* area below @rend is dealt with, forget about it */
base = min(rend, end); // 获取到最小的地址为基地址
}
/* insert the remaining portion */
if (base < end) {
nr_new++;
if (insert)
memblock_insert_region(type, idx, base, end - base,
nid, flags); // 将当前内存区域插入内存块
}
if (!nr_new)
return 0;
/*
* 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); // 合并区域
return 0;
}
}
memblock_insert_region
static void __init_memblock memblock_insert_region(struct memblock_type *type,
int idx, phys_addr_t base,
phys_addr_t size,
int nid,
enum memblock_flags flags)
{
struct memblock_region *rgn = &type->regions[idx];
BUG_ON(type->cnt >= type->max);
memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn));
rgn->base = base;
rgn->size = size;
rgn->flags = flags;
memblock_set_region_node(rgn, nid);
type->cnt++;
type->total_size += size;
}