kenrel: 5.10
arch: aarch64
启动阶段经过arm64_memblock_init之后,通过gdb可以查看到memblock布局如下,此处的memblock可以理解为在线的memory
(gdb) p/x memblock
$5 = {
bottom_up = 0x0,
current_limit = 0xffffffffffffffff,
memory = {
cnt = 0x1,
max = 0x80,
total_size = 0x40000000,
regions = 0xffff8000120a8848,
name = 0xffff80001139cc28
},
reserved = {
cnt = 0x5,
max = 0x181,
total_size = 0x58a20c4,
regions = 0xffff8000120a9448,
name = 0xffff80001127e258
}
}
其中memblock.memory如下:
(gdb) p/x *(struct memblock_region *)0xffff8000120a8848
$6 = {
base = 0x40000000,
size = 0x40000000,
flags = 0x0,
nid = 0x10
}
memblock.reserved如下:
(gdb) p/x *(struct memblock_region *)0xffff8000120a9448@5
$9 = {{
base = 0x40200000,
size = 0x2592000,
flags = 0x0,
nid = 0x10
},
{
base = 0x42797000,
size = 0x9000,
flags = 0x0,
nid = 0x10
},
{
base = 0x48000000,
size = 0x100000,
flags = 0x0,
nid = 0x10
},
{
base = 0x7cdf8f38,
size = 0x90c4,
flags = 0x0,
nid = 0x10
},
{
base = 0x7ce02000,
size = 0x31fe000,
flags = 0x0,
nid = 0x10
}}
从如上可以看出,只有一个memory区域,5个reserved区域
__next_mem_pfn_range
/*
* Common iterator interface used to define for_each_mem_pfn_range().
*/
void __init_memblock __next_mem_pfn_range(int *idx, int nid,
unsigned long *out_start_pfn,
unsigned long *out_end_pfn, int *out_nid)
{
struct memblock_type *type = &memblock.memory;
struct memblock_region *r;
int r_nid;
while (++*idx < type->cnt) {
r = &type->regions[*idx];
r_nid = memblock_get_region_node(r);
if (PFN_UP(r->base) >= PFN_DOWN(r->base + r->size))
continue;
if (nid == MAX_NUMNODES || nid == r_nid)
break;
}
if (*idx >= type->cnt) {
*idx = -1;
return;
}
if (out_start_pfn)
*out_start_pfn = PFN_UP(r->base);
if (out_end_pfn)
*out_end_pfn = PFN_DOWN(r->base + r->size);
if (out_nid)
*out_nid = r_nid;
}
上面的函数主要是通过遍历memblock.memory下的每个memblock_region区域,找到node id为nid的memblock_region区域将其起始物理地址和结束物理地址转换为物理页帧号分别保存在out_start_pfn和out_end_pfn返回。
注意:物理页帧号并非从0开始,而是从实际memory的物理地址开始,如DDR memory地址为0x40000000,则物理页帧号为0x40000
这里列出memblock, memblock_type, memblock_region 结构体定义,以说明他们之间的关系
/**
* 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_NEED_MULTIPLE_NODES
int nid;
#endif
};
/**
* struct memblock_type - collection of memory regions of certain type
* @cnt: number of regions
* @max: size of the allocated array
* @total_size: size of all regions
* @regions: array of regions
* @name: the memory type symbolic name
*/
struct memblock_type {
unsigned long cnt;
unsigned long max;
phys_addr_t total_size;
struct memblock_region *regions;
char *name;
};
/**
* struct memblock - memblock allocator metadata
* @bottom_up: is bottom up direction?
* @current_limit: physical address of the current allocation limit
* @memory: usable memory regions
* @reserved: reserved memory regions
*/
struct memblock {
bool bottom_up; /* is bottom up direction? */
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
};
for_each_mem_pfn_range
/**
* for_each_mem_pfn_range - early memory pfn range iterator
* @i: an integer used as loop variable
* @nid: node selector, %MAX_NUMNODES for all nodes
* @p_start: ptr to ulong for start pfn of the range, can be %NULL
* @p_end: ptr to ulong for end pfn of the range, can be %NULL
* @p_nid: ptr to int for nid of the range, can be %NULL
*
* Walks over configured memory ranges.
*/
#define for_each_mem_pfn_range(i, nid, p_start, p_end, p_nid) \
for (i = -1, __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid); \
i >= 0; __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid))
根据前面__next_mem_pfn_range的定义,for_each_mem_pfn_range实际上就是遍历memblock.memory下的每个memblock_region区域,找到node id与参数 nid相等 的memblock_region区域,将它的起始pfn和结束pfn,node id,分别保存在p_start,p_end,p_nid返回。这里注意的是如果nid为MAX_NUMNODES,则表示遍历所有的node
本文详细解析了ARM64架构中memblock的数据结构,展示了如何通过GDB工具观察内存布局,并介绍了`__next_mem_pfn_range`函数的工作原理,用于遍历内存区域并筛选指定节点的范围。关键概念包括memblock_region、memory和reserved区域的管理。

1674

被折叠的 条评论
为什么被折叠?



