RV64架构下的Linux内核源码在memblock_double_array使用__va的疑问

内核启动过程中在memblock_double_array函数内调用了

new_array = addr ? __va(addr) : NULL;

然而再riscv64的情况下 kernel_map.va_pa_offset是在函数setup_bootmem内才设置的。

	/*
	 * In 64-bit, any use of __va/__pa before this point is wrong as we
	 * did not know the start of DRAM before.
	 */
	if (IS_ENABLED(CONFIG_64BIT) && IS_ENABLED(CONFIG_MMU))
		kernel_map.va_pa_offset = PAGE_OFFSET - phys_ram_base;

注释也写了在这之前不能用__va和__pa。memblock_double_array的第一次调用是在setup_bootmem之前。有人注意到这个地方了吗?这样没问题吗?

1 个赞

简单看了一下源码,因为memblock_double_array根本不会被调用

memblock_double_array这个函数前面有个

if (!memblock_can_resize)
	panic("memblock: cannot resize %s array\n", type->name);

而memblock_can_resize又是在memblock_allow_resize被设置为1的

void __init memblock_allow_resize(void)
{
	memblock_can_resize = 1;
}

在riscv上用gdb调试会发现setup_bootmem先于memblock_allow_resize

(gdb) b setup_bootmem
Breakpoint 1 at 0xffffffff80c07400: file arch/riscv/mm/init.c, line 222.
(gdb) b memblock_allow_resize
Breakpoint 2 at 0xffffffff80c19600: file mm/memblock.c, line 2107.
(gdb) c
Continuing.

Breakpoint 1, 0xffffffff80c07400 in setup_bootmem () at arch/riscv/mm/init.c:222
222             phys_addr_t vmlinux_end = __pa_symbol(&_end);
(gdb) 
Continuing.

Breakpoint 2, memblock_allow_resize () at mm/memblock.c:2107
2107            memblock_can_resize = 1;
(gdb)

至于调用memblock_double_array的地方,无非就是memblock_add_range和memblock_isolate_range函数

前者初期会直接return掉

if (type->regions[0].size == 0) {
	WARN_ON(type->cnt != 0 || 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;
	type->cnt = 1;
	return 0;
}

后者则是不满足while条件会被跳过

while (type->cnt + 2 > type->max)
	if (memblock_double_array(type, base, size) < 0)
		return -ENOMEM;
(gdb) print type->cnt
$3 = 1
(gdb) print type->max
$4 = 128
(gdb)
2 个赞

谢谢 确实是这样的

1 个赞