Linux 内核 内存管理memblock 分配器 ⑤ ( Linux 内核中定义 memblock 分配器的位置 | ARM64体系架构下 Linux内核初始化 memblock 分配器流程 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux 内核 内存管理memblock 分配器 ⑤ ( Linux 内核中定义 memblock 分配器的位置 | ARM64体系架构下 Linux内核初始化 memblock 分配器流程 )相关的知识,希望对你有一定的参考价值。
文章目录
一、Linux 内核中定义 memblock 分配器的位置
Linux 内核 定义 memblock 分配器 位置 :
Linux 内核源码 linux-4.12\\mm\\memblock.c#34 位置 , 定义了 struct memblock
类型的变量 , 在该结构体赋值时 , .bottom_up = false
将 bottom_up
设置为了 false , 表示内存从 高地址向下分配 ;
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_REGIONS,
.reserved.name = "reserved",
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
.physmem.regions = memblock_physmem_init_regions,
.physmem.cnt = 1, /* empty dummy entry */
.physmem.max = INIT_PHYSMEM_REGIONS,
.physmem.name = "physmem",
#endif
.bottom_up = false,
.current_limit = MEMBLOCK_ALLOC_ANYWHERE,
;
源码路径 : linux-4.12\\mm\\memblock.c#34
二、ARM64 体系架构下 Linux 内核初始化 memblock 分配器流程
先在 linux-4.12\\init#488 位置的 asmlinkage __visible void __init start_kernel(void)
内核启动函数中 , 调用 setup_arch(&command_line);
函数 ,
然后再调用 ARM64 体系架构对应的源码 , linux-4.12\\arch\\arm64\\mm\\init.c#362 位置的 void __init arm64_memblock_init(void)
函数 , 该函数是 初始化 memblock 分配器的核心函数 ;
ARM64 体系架构下 Linux 内核初始化 memblock 分配器流程 :
① 解析 " 设备树二进制文件 " /memory
节点 , 将 " 所有物理内存 " 纳入到 memblock 分配器 管理之下 ;
该步骤对应的 Linux 源代码是在 linux-4.12\\arch\\arm64\\mm\\init.c#367 位置 ;
/* Handle linux,usable-memory-range property */
fdt_enforce_memory_region();
源码路径 : linux-4.12\\arch\\arm64\\mm\\init.c#367
② 设置 全局变量 memstart_addr
, 用于记录 物理内存 的 起始地址 ;
/*
* Select a suitable value for the base of physical memory.
*/
memstart_addr = round_down(memblock_start_of_DRAM(),
ARM64_MEMSTART_ALIGN);
源码路径 : linux-4.12\\arch\\arm64\\mm\\init.c#379
③ 从 memblock 分配器 中 , 删除 " 线性映射区域 不能覆盖的 指定范围的 物理内存 " ;
/*
* Remove the memory that we will not be able to cover with the
* linear mapping. Take care not to clip the kernel which may be
* high in memory.
*/
memblock_remove(max_t(u64, memstart_addr + linear_region_size,
__pa_symbol(_end)), ULLONG_MAX);
源码路径 : linux-4.12\\arch\\arm64\\mm\\init.c#387
④ 使用 mem 可以指定 " 设备树二进制文件中指定命令行 " 的 " 可用内存 " 大小 ,
如果指定了该可用内存大小 , 需要从 memblock 分配器 中 , 删除该大小之外超出的部分物理内存 ;
/*
* Apply the memory limit if it was set. Since the kernel may be loaded
* high up in memory, add back the kernel region that must be accessible
* via the linear mapping.
*/
if (memory_limit != (phys_addr_t)ULLONG_MAX)
memblock_mem_limit_remove_map(memory_limit);
memblock_add(__pa_symbol(_text), (u64)(_end - _text));
源码路径 : linux-4.12\\arch\\arm64\\mm\\init.c#401
⑤ 将 内核镜像 占用的 物理内存 纳入到 memblock 分配器 管理之下 ;
/*
* Register the kernel text, kernel data, initrd, and initial
* pagetables with memblock.
*/
memblock_reserve(__pa_symbol(_text), _end - _text);
源码路径 : linux-4.12\\arch\\arm64\\mm\\init.c#456
⑥ 初始化 " 设备树二进制文件 " 内存区域 对应的 设备源文件相关字段和节点 ;
early_init_fdt_scan_reserved_mem();
源码路径 : linux-4.12\\arch\\arm64\\mm\\init.c#467
三、arm64_memblock_init 函数完整源码
void __init arm64_memblock_init(void)
const s64 linear_region_size = -(s64)PAGE_OFFSET;
/* Handle linux,usable-memory-range property */
fdt_enforce_memory_region();
/*
* Ensure that the linear region takes up exactly half of the kernel
* virtual address space. This way, we can distinguish a linear address
* from a kernel/module/vmalloc address by testing a single bit.
*/
BUILD_BUG_ON(linear_region_size != BIT(VA_BITS - 1));
/*
* Select a suitable value for the base of physical memory.
*/
memstart_addr = round_down(memblock_start_of_DRAM(),
ARM64_MEMSTART_ALIGN);
/*
* Remove the memory that we will not be able to cover with the
* linear mapping. Take care not to clip the kernel which may be
* high in memory.
*/
memblock_remove(max_t(u64, memstart_addr + linear_region_size,
__pa_symbol(_end)), ULLONG_MAX);
if (memstart_addr + linear_region_size < memblock_end_of_DRAM())
/* ensure that memstart_addr remains sufficiently aligned */
memstart_addr = round_up(memblock_end_of_DRAM() - linear_region_size,
ARM64_MEMSTART_ALIGN);
memblock_remove(0, memstart_addr);
/*
* Apply the memory limit if it was set. Since the kernel may be loaded
* high up in memory, add back the kernel region that must be accessible
* via the linear mapping.
*/
if (memory_limit != (phys_addr_t)ULLONG_MAX)
memblock_mem_limit_remove_map(memory_limit);
memblock_add(__pa_symbol(_text), (u64)(_end - _text));
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_start)
/*
* Add back the memory we just removed if it results in the
* initrd to become inaccessible via the linear mapping.
* Otherwise, this is a no-op
*/
u64 base = initrd_start & PAGE_MASK;
u64 size = PAGE_ALIGN(initrd_end) - base;
/*
* We can only add back the initrd memory if we don't end up
* with more memory than we can address via the linear mapping.
* It is up to the bootloader to position the kernel and the
* initrd reasonably close to each other (i.e., within 32 GB of
* each other) so that all granule/#levels combinations can
* always access both.
*/
if (WARN(base < memblock_start_of_DRAM() ||
base + size > memblock_start_of_DRAM() +
linear_region_size,
"initrd not fully accessible via the linear mapping -- please check your bootloader ...\\n"))
initrd_start = 0;
else
memblock_remove(base, size); /* clear MEMBLOCK_ flags */
memblock_add(base, size);
memblock_reserve(base, size);
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE))
extern u16 memstart_offset_seed;
u64 range = linear_region_size -
(memblock_end_of_DRAM() - memblock_start_of_DRAM());
/*
* If the size of the linear region exceeds, by a sufficient
* margin, the size of the region that the available physical
* memory spans, randomize the linear region as well.
*/
if (memstart_offset_seed > 0 && range >= ARM64_MEMSTART_ALIGN)
range = range / ARM64_MEMSTART_ALIGN + 1;
memstart_addr -= ARM64_MEMSTART_ALIGN *
((range * memstart_offset_seed) >> 16);
/*
* Register the kernel text, kernel data, initrd, and initial
* pagetables with memblock.
*/
memblock_reserve(__pa_symbol(_text), _end - _text);
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start)
memblock_reserve(initrd_start, initrd_end - initrd_start);
/* the generic initrd code expects virtual addresses */
initrd_start = __phys_to_virt(initrd_start);
initrd_end = __phys_to_virt(initrd_end);
#endif
early_init_fdt_scan_reserved_mem();
/* 4GB maximum for 32-bit only capable devices */
if (IS_ENABLED(CONFIG_ZONE_DMA))
arm64_dma_phys_limit = max_zone_dma_phys();
else
arm64_dma_phys_limit = PHYS_MASK + 1;
reserve_crashkernel();
reserve_elfcorehdr();
dma_contiguous_reserve(arm64_dma_phys_limit);
memblock_allow_resize();
以上是关于Linux 内核 内存管理memblock 分配器 ⑤ ( Linux 内核中定义 memblock 分配器的位置 | ARM64体系架构下 Linux内核初始化 memblock 分配器流程 )的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理memblock 分配器 ② ( memblock_type 内存块类型 | memblock_type 结构体成员分析 )
Linux 内核 内存管理memblock 分配器编程接口 ⑤ ( memblock_free 函数 | memblock_remove_range 函数 )
Linux 内核 内存管理memblock 分配器 ③ ( memblock_region 内存块区域 | memblock_region 结构体成员分析 | memblock 分配器标志位 )
Linux 内核 内存管理memblock 分配器编程接口 ① ( memblock 分配器编程接口简介 | memblock_add 函数原型分析 | memblock_add 函数源码 )
Linux 内核 内存管理memblock 分配器编程接口 ③ ( memblock_remove 函数 | memblock_remove_range 函数 )
Linux 内核 内存管理memblock 分配器编程接口 ④ ( memblock_alloc 函数 | memblock_alloc_base 函数 )