启动时内存管理¶
早期系统初始化无法使用“普通”内存管理,因为它尚未设置。但仍需要为各种数据结构分配内存,例如物理页分配器。
一个名为 memblock
的专用分配器执行启动时内存管理。特定架构的初始化必须在 setup_arch()
函数中设置它,并在 mem_init()
函数中将其拆除。
一旦早期内存管理可用,它会提供各种函数和宏用于内存分配。分配请求可以定向到第一个(也可能是唯一)节点,或者 NUMA 系统中的特定节点。存在在分配失败时导致恐慌的 API 变体,也有不会恐慌的。
Memblock 还提供了多种控制其自身行为的 API。
Memblock 概述¶
Memblock 是一种在早期启动期间管理内存区域的方法,此时常规的内核内存分配器尚未启动和运行。
Memblock 将系统内存视为连续区域的集合。这些集合有几种类型:
memory
- 描述内核可用的物理内存;这可能与系统中实际安装的物理内存不同,例如当内存受mem=
命令行参数限制时reserved
- 描述已分配的区域physmem
- 描述启动期间可用的实际物理内存,无论可能存在的限制和内存热插拔/热拔;physmem
类型仅在某些架构上可用。
每个区域由 struct memblock_region
表示,该结构定义了区域的范围、其属性以及 NUMA 系统上的 NUMA 节点 ID。每种内存类型由 struct memblock_type
描述,该结构包含一个内存区域数组以及分配器元数据。“memory”和“reserved”类型被很好地封装在 struct memblock
中。该结构在构建时静态初始化。区域数组的初始大小,“memory”类型为 INIT_MEMBLOCK_MEMORY_REGIONS
,“reserved”类型为 INIT_MEMBLOCK_RESERVED_REGIONS
。“physmem”类型的区域数组初始大小为 INIT_PHYSMEM_REGIONS
。memblock_allow_resize() 在添加新区域时启用区域数组的自动调整大小。应谨慎使用此功能,以确保为区域数组分配的内存不会与应保留的区域(例如 initrd)重叠。
早期架构设置应使用 memblock_add()
或 memblock_add_node()
函数告知 memblock 物理内存布局。第一个函数不将区域分配给 NUMA 节点,适用于 UMA 系统。然而,它也可以在 NUMA 系统上使用,并在后续设置过程中使用 memblock_set_node()
将区域分配给 NUMA 节点。memblock_add_node()
直接执行此类分配。
一旦 memblock 设置完毕,就可以使用以下 API 变体之一分配内存:
memblock_phys_alloc*() - 这些函数返回已分配内存的物理地址
memblock_alloc*() - 这些函数返回已分配内存的虚拟地址。
请注意,这两种 API 变体都隐含地假设了允许的内存范围和回退方法。有关更详细的描述,请查阅 memblock_alloc_internal()
和 memblock_alloc_range_nid()
函数的文档。
随着系统启动的进行,特定架构的 mem_init() 函数会将所有内存释放给伙伴页分配器。
除非某个架构启用 CONFIG_ARCH_KEEP_MEMBLOCK
,否则 memblock 数据结构(“physmem”除外)将在系统初始化完成后被丢弃。
函数和结构体¶
以下是 memblock 数据结构、函数和宏的描述。其中一些实际上是内部的,但既然它们有文档,省略它们会很愚蠢。此外,阅读内部函数的描述有助于理解其底层实际发生的情况。
-
enum memblock_flags¶
内存区域属性定义
常量
MEMBLOCK_NONE
无特殊要求
MEMBLOCK_HOTPLUG
固件在早期启动期间提供的内存映射中指示的、可热插拔(可热拔插)的系统 RAM 内存区域(例如,以后可能被热拔插的内存范围)。如果在内核命令行中设置了“movable_node”,则尝试保持此内存区域可热拔插。不适用于早期启动后添加(“热插拔”)的 memblock。
MEMBLOCK_MIRROR
镜像区域
MEMBLOCK_NOMAP
不添加到内核直接映射中,并在内存映射中视为保留;有关详细信息,请参阅
memblock_mark_nomap()
的描述MEMBLOCK_DRIVER_MANAGED
始终通过驱动检测并添加的内存区域,且从未在固件提供的内存映射中指示为系统 RAM。这与内核资源树中的 IORESOURCE_SYSRAM_DRIVER_MANAGED 相对应。
MEMBLOCK_RSRV_NOINIT
struct pages 未初始化的内存区域(仅适用于保留区域)。
MEMBLOCK_RSRV_KERN
为内核使用而保留的内存区域,可以通过 memblock_reserve_kern() 或通过 memblock 分配 API 显式保留。所有 memblock 分配都会设置此标志。
MEMBLOCK_KHO_SCRATCH
kexec 在移交模式下可以传递给下一个内核的内存区域。在早期启动期间,我们尚不了解所有内存保留,因此我们从前一个内核中获取我们知道可以使用的临时内存。这是在此阶段唯一可以进行分配的内存。
-
struct memblock_region¶
表示一个内存区域
定义:
struct memblock_region {
phys_addr_t base;
phys_addr_t size;
enum memblock_flags flags;
#ifdef CONFIG_NUMA;
int nid;
#endif;
};
成员
base
区域的基地址
size
区域的大小
flags
内存区域属性
nid
NUMA 节点 ID
-
struct memblock_type¶
某种类型的内存区域集合
定义:
struct memblock_type {
unsigned long cnt;
unsigned long max;
phys_addr_t total_size;
struct memblock_region *regions;
char *name;
};
成员
cnt
区域数量
max
已分配数组的大小
total_size
所有区域的总大小
regions
区域数组
name
内存类型的符号名称
-
struct memblock¶
memblock 分配器元数据
定义:
struct memblock {
bool bottom_up;
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
};
成员
bottom_up
是自下而上方向吗?
current_limit
当前分配限制的物理地址
memory
可用内存区域
reserved
保留内存区域
-
for_each_physmem_range¶
for_each_physmem_range (i, type, p_start, p_end)
遍历不包含在 type 中的 physmem 区域。
参数
i
用作循环变量的 u64
type
指向 memblock_type 的指针,该类型从迭代中排除,可以是
NULL
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
-
__for_each_mem_range¶
__for_each_mem_range (i, type_a, type_b, nid, flags, p_start, p_end, p_nid)
遍历 type_a 中的 memblock 区域,且不包含在 type_b 中。如果 type_b 为 NULL,则仅遍历 type_a。
参数
i
用作循环变量的 u64
type_a
指向要迭代的 memblock_type 的指针
type_b
指向 memblock_type 的指针,该类型从迭代中排除
nid
节点选择器,
NUMA_NO_NODE
表示所有节点flags
根据内存属性从块中选择
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
p_nid
指向 int 的指针,用于范围的 nid,可以是
NULL
-
__for_each_mem_range_rev¶
__for_each_mem_range_rev (i, type_a, type_b, nid, flags, p_start, p_end, p_nid)
反向遍历 type_a 中的 memblock 区域,且不包含在 type_b 中。如果 type_b 为 NULL,则仅反向遍历 type_a。
参数
i
用作循环变量的 u64
type_a
指向要迭代的 memblock_type 的指针
type_b
指向 memblock_type 的指针,该类型从迭代中排除
nid
节点选择器,
NUMA_NO_NODE
表示所有节点flags
根据内存属性从块中选择
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
p_nid
指向 int 的指针,用于范围的 nid,可以是
NULL
-
for_each_mem_range¶
for_each_mem_range (i, p_start, p_end)
遍历内存区域。
参数
i
用作循环变量的 u64
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
-
for_each_mem_range_rev¶
for_each_mem_range_rev (i, p_start, p_end)
反向遍历 type_a 中的 memblock 区域,且不包含在 type_b 中。如果 type_b 为 NULL,则仅反向遍历 type_a。
参数
i
用作循环变量的 u64
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
-
for_each_reserved_mem_range¶
for_each_reserved_mem_range (i, p_start, p_end)
遍历所有保留的 memblock 区域
参数
i
用作循环变量的 u64
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
描述
遍历 memblock 的保留区域。Memblock 初始化后即可使用。
-
for_each_mem_pfn_range¶
for_each_mem_pfn_range (i, nid, p_start, p_end, p_nid)
早期内存 pfn 范围迭代器
参数
i
用作循环变量的整数
nid
节点选择器,
MAX_NUMNODES
表示所有节点p_start
指向 ulong 的指针,用于范围的起始 pfn,可以是
NULL
p_end
指向 ulong 的指针,用于范围的结束 pfn,可以是
NULL
p_nid
指向 int 的指针,用于范围的 nid,可以是
NULL
描述
遍历已配置的内存范围。
-
for_each_free_mem_pfn_range_in_zone_from¶
for_each_free_mem_pfn_range_in_zone_from (i, zone, p_start, p_end)
从给定点开始遍历特定区域的空闲 memblock 区域
参数
i
用作循环变量的 u64
zone
所有内存块所在的区域
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
描述
遍历特定区域中空闲的(memory && !reserved)memblock 区域,从当前位置继续。Memblock 初始化后即可使用。
-
for_each_free_mem_range¶
for_each_free_mem_range (i, nid, flags, p_start, p_end, p_nid)
遍历空闲的 memblock 区域
参数
i
用作循环变量的 u64
nid
节点选择器,
NUMA_NO_NODE
表示所有节点flags
根据内存属性从块中选择
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
p_nid
指向 int 的指针,用于范围的 nid,可以是
NULL
描述
遍历 memblock 中空闲的(memory && !reserved)区域。Memblock 初始化后即可使用。
-
for_each_free_mem_range_reverse¶
for_each_free_mem_range_reverse (i, nid, flags, p_start, p_end, p_nid)
反向遍历空闲的 memblock 区域
参数
i
用作循环变量的 u64
nid
节点选择器,
NUMA_NO_NODE
表示所有节点flags
根据内存属性从块中选择
p_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
p_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
p_nid
指向 int 的指针,用于范围的 nid,可以是
NULL
描述
以相反顺序遍历 memblock 中空闲的(memory && !reserved)区域。Memblock 初始化后即可使用。
-
void memblock_set_current_limit(phys_addr_t limit)¶
设置当前分配限制,以允许将分配限制在启动期间当前可访问的范围。
参数
phys_addr_t limit
新的限制值(物理地址)
-
unsigned long memblock_region_memory_base_pfn(const struct memblock_region *reg)¶
获取内存区域的最低 pfn
参数
const struct memblock_region *reg
memblock_region 结构体
返回值
与内存区域相交的最低 pfn
-
unsigned long memblock_region_memory_end_pfn(const struct memblock_region *reg)¶
获取内存区域的结束 pfn
参数
const struct memblock_region *reg
memblock_region 结构体
返回值
保留区域的 end_pfn
-
unsigned long memblock_region_reserved_base_pfn(const struct memblock_region *reg)¶
获取保留区域的最低 pfn
参数
const struct memblock_region *reg
memblock_region 结构体
返回值
与保留区域相交的最低 pfn
-
unsigned long memblock_region_reserved_end_pfn(const struct memblock_region *reg)¶
获取保留区域的结束 pfn
参数
const struct memblock_region *reg
memblock_region 结构体
返回值
保留区域的 end_pfn
-
for_each_mem_region¶
for_each_mem_region (region)
遍历内存区域
参数
region
循环变量
-
for_each_reserved_mem_region¶
for_each_reserved_mem_region (region)
遍历保留内存区域
参数
region
循环变量
-
phys_addr_t __init_memblock __memblock_find_range_bottom_up(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align, int nid, enum memblock_flags flags)¶
自下而上查找空闲区域的工具函数
参数
phys_addr_t start
候选范围的起始
phys_addr_t end
候选范围的结束,可以是
MEMBLOCK_ALLOC_ANYWHERE
或MEMBLOCK_ALLOC_ACCESSIBLE
phys_addr_t size
要查找的空闲区域的大小
phys_addr_t align
要查找的空闲区域的对齐方式
int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点enum memblock_flags flags
根据内存属性从块中选择
描述
从 memblock_find_in_range_node()
调用的工具函数,自下而上查找空闲区域。
返回值
成功时返回找到的地址,失败时返回 0。
-
phys_addr_t __init_memblock __memblock_find_range_top_down(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align, int nid, enum memblock_flags flags)¶
自上而下查找空闲区域的工具函数
参数
phys_addr_t start
候选范围的起始
phys_addr_t end
候选范围的结束,可以是
MEMBLOCK_ALLOC_ANYWHERE
或MEMBLOCK_ALLOC_ACCESSIBLE
phys_addr_t size
要查找的空闲区域的大小
phys_addr_t align
要查找的空闲区域的对齐方式
int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点enum memblock_flags flags
根据内存属性从块中选择
描述
从 memblock_find_in_range_node()
调用的工具函数,自上而下查找空闲区域。
返回值
成功时返回找到的地址,失败时返回 0。
-
phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end, int nid, enum memblock_flags flags)¶
在给定范围和节点中查找空闲区域
参数
phys_addr_t size
要查找的空闲区域的大小
phys_addr_t align
要查找的空闲区域的对齐方式
phys_addr_t start
候选范围的起始
phys_addr_t end
候选范围的结束,可以是
MEMBLOCK_ALLOC_ANYWHERE
或MEMBLOCK_ALLOC_ACCESSIBLE
int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点enum memblock_flags flags
根据内存属性从块中选择
描述
在指定的范围和节点中,查找大小为 size 并按 align 对齐的空闲区域。
返回值
成功时返回找到的地址,失败时返回 0。
-
phys_addr_t __init_memblock memblock_find_in_range(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align)¶
在给定范围中查找空闲区域
参数
phys_addr_t start
候选范围的起始
phys_addr_t end
候选范围的结束,可以是
MEMBLOCK_ALLOC_ANYWHERE
或MEMBLOCK_ALLOC_ACCESSIBLE
phys_addr_t size
要查找的空闲区域的大小
phys_addr_t align
要查找的空闲区域的对齐方式
描述
在指定的范围中,查找大小为 size 并按 align 对齐的空闲区域。
返回值
成功时返回找到的地址,失败时返回 0。
-
void memblock_discard(void)¶
如果内存和保留数组已分配,则将其丢弃
参数
void
无参数
-
int __init_memblock memblock_double_array(struct memblock_type *type, phys_addr_t new_area_start, phys_addr_t new_area_size)¶
将 memblock 区域数组的大小加倍
参数
struct memblock_type *type
正在加倍的区域数组的 memblock 类型
phys_addr_t new_area_start
要避免重叠的内存范围的起始地址
phys_addr_t new_area_size
要避免重叠的内存范围的大小
描述
将 type 区域数组的大小加倍。如果 memblock 用于为一个新的保留区域数组分配内存,并且存在一个先前已分配的内存范围 [new_area_start, new_area_start + new_area_size] 等待保留,请确保新数组使用的内存不与此范围重叠。
返回值
成功时返回 0,失败时返回 -1。
-
void __init_memblock memblock_merge_regions(struct memblock_type *type, unsigned long start_rgn, unsigned long end_rgn)¶
合并相邻的兼容区域
参数
struct memblock_type *type
要扫描的 memblock 类型
unsigned long start_rgn
从 (start_rgn - 1) 开始扫描
unsigned long end_rgn
扫描结束于 (end_rgn - 1)。扫描 type 并在 [start_rgn - 1, end_rgn) 范围内合并相邻的兼容区域。
-
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)¶
插入新的 memblock 区域
参数
struct memblock_type *type
要插入的 memblock 类型
int idx
插入点的索引
phys_addr_t base
新区域的基地址
phys_addr_t size
新区域的大小
int nid
新区域的节点 ID
enum memblock_flags flags
新区域的标志
描述
将新的 memblock 区域 [base, base + size) 插入到 type 的 idx 位置。type 必须已经有足够的额外空间来容纳新区域。
-
int __init_memblock memblock_add_range(struct memblock_type *type, phys_addr_t base, phys_addr_t size, int nid, enum memblock_flags flags)¶
添加新的 memblock 区域
参数
struct memblock_type *type
要添加新区域的 memblock 类型
phys_addr_t base
新区域的基地址
phys_addr_t size
新区域的大小
int nid
新区域的 nid
enum memblock_flags flags
新区域的标志
描述
将新的 memblock 区域 [base, base + size) 添加到 type 中。新区域允许与现有区域重叠 - 重叠不会影响已存在的区域。添加后,type 保证是最小的(所有相邻的兼容区域都已合并)。
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_add_node(phys_addr_t base, phys_addr_t size, int nid, enum memblock_flags flags)¶
在 NUMA 节点内添加新的 memblock 区域
参数
phys_addr_t base
新区域的基地址
phys_addr_t size
新区域的大小
int nid
新区域的 nid
enum memblock_flags flags
新区域的标志
描述
将新的 memblock 区域 [base, base + size) 添加到“memory”类型中。有关模式详细信息,请参阅 memblock_add_range()
的描述。
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)¶
添加新的 memblock 区域
参数
phys_addr_t base
新区域的基地址
phys_addr_t size
新区域的大小
描述
将新的 memblock 区域 [base, base + size) 添加到“memory”类型中。有关模式详细信息,请参阅 memblock_add_range()
的描述。
返回值
成功时返回 0,失败时返回 -errno。
-
bool __init_memblock memblock_validate_numa_coverage(unsigned long threshold_bytes)¶
检查未分配节点 ID 的内存量是否小于阈值
参数
unsigned long threshold_bytes
可以具有未分配节点 ID 的最大内存大小(以字节为单位)。
描述
有缺陷的固件可能会报告不属于任何节点的内存。检查此类内存量是否低于 threshold_bytes。
返回值
成功时返回 true,失败时返回 false。
-
int __init_memblock memblock_isolate_range(struct memblock_type *type, phys_addr_t base, phys_addr_t size, int *start_rgn, int *end_rgn)¶
将给定范围隔离成不相交的 memblock
参数
struct memblock_type *type
要隔离范围的 memblock 类型
phys_addr_t base
要隔离的范围基址
phys_addr_t size
要隔离的范围大小
int *start_rgn
隔离区域起始的输出参数
int *end_rgn
隔离区域结束的输出参数
描述
遍历 type 并确保区域不越过 [base, base + size) 定义的边界。跨越边界的区域将在边界处被分割,这最多会创建两个额外的区域。范围内的第一个区域的索引在 *start_rgn 中返回,范围后的第一个区域的索引在 *end_rgn 中返回。
返回值
成功时返回 0,失败时返回 -errno。
-
void __init_memblock memblock_free(void *ptr, size_t size)¶
释放启动内存分配
参数
void *ptr
启动内存分配的起始地址
size_t size
启动内存块的大小(以字节为单位)
描述
释放先前由 memblock_alloc_xx() API 分配的启动内存块。释放的内存将不会被释放到伙伴分配器。
-
int __init_memblock memblock_phys_free(phys_addr_t base, phys_addr_t size)¶
释放启动内存块
参数
phys_addr_t base
启动内存块的物理起始地址
phys_addr_t size
启动内存块的大小(以字节为单位)
描述
释放先前由 memblock_phys_alloc_xx() API 分配的启动内存块。释放的内存将不会被释放到伙伴分配器。
-
int __init_memblock memblock_setclr_flag(struct memblock_type *type, phys_addr_t base, phys_addr_t size, int set, int flag)¶
设置或清除内存区域的标志
参数
struct memblock_type *type
要设置/清除标志的 memblock 类型
phys_addr_t base
区域的基地址
phys_addr_t size
区域的大小
int set
设置或清除标志
int flag
要更新的标志
描述
此函数隔离区域 [base, base + size),并设置/清除标志。
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_mark_hotplug(phys_addr_t base, phys_addr_t size)¶
用 MEMBLOCK_HOTPLUG 标志标记可热插拔内存。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_clear_hotplug(phys_addr_t base, phys_addr_t size)¶
清除指定区域的 MEMBLOCK_HOTPLUG 标志。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_mark_mirror(phys_addr_t base, phys_addr_t size)¶
用 MEMBLOCK_MIRROR 标志标记镜像内存。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_mark_nomap(phys_addr_t base, phys_addr_t size)¶
用 MEMBLOCK_NOMAP 标志标记内存区域。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
描述
标记有 MEMBLOCK_NOMAP
的内存区域将不会被添加到物理内存的直接映射中。这些区域仍将由内存映射覆盖。内存映射中代表 NOMAP 内存帧的 struct page 将是 PageReserved()。
注意
如果正在标记为 MEMBLOCK_NOMAP
的内存是从 memblock 分配的,则调用方必须通知 kmemleak 忽略该内存。
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_clear_nomap(phys_addr_t base, phys_addr_t size)¶
清除指定区域的 MEMBLOCK_NOMAP 标志。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
返回值
成功时返回 0,失败时返回 -errno。
-
int __init_memblock memblock_reserved_mark_noinit(phys_addr_t base, phys_addr_t size)¶
用 MEMBLOCK_RSRV_NOINIT 标志标记一个保留内存区域,这将导致该区域的 struct pages 不会被初始化。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
描述
对于标记有 MEMBLOCK_RSRV_NOINIT
的保留内存区域,struct pages 将不会被初始化。
返回值
成功时返回 0,失败时返回 -errno。
-
int memblock_mark_kho_scratch(phys_addr_t base, phys_addr_t size)¶
将内存区域标记为 MEMBLOCK_KHO_SCRATCH。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
描述
只有标记有 MEMBLOCK_KHO_SCRATCH
的内存区域才会在 kexec 移交的早期启动期间被考虑用于分配。
返回值
成功时返回 0,失败时返回 -errno。
-
int memblock_clear_kho_scratch(phys_addr_t base, phys_addr_t size)¶
清除指定区域的 MEMBLOCK_KHO_SCRATCH 标志。
参数
phys_addr_t base
区域的基物理地址
phys_addr_t size
区域的大小
返回值
成功时返回 0,失败时返回 -errno。
-
void __next_mem_range(u64 *idx, int nid, enum memblock_flags flags, struct memblock_type *type_a, struct memblock_type *type_b, phys_addr_t *out_start, phys_addr_t *out_end, int *out_nid)¶
用于
for_each_free_mem_range()
等的下一个函数。
参数
u64 *idx
指向u64循环变量的指针
int nid
节点选择器,
NUMA_NO_NODE
表示所有节点enum memblock_flags flags
根据内存属性从块中选择
struct memblock_type *type_a
指向获取内存范围的memblock_type的指针
struct memblock_type *type_b
指向排除内存的memblock_type的指针
phys_addr_t *out_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
phys_addr_t *out_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
int *out_nid
指向 int 的指针,用于范围的 nid,可以是
NULL
描述
从 *idx 中找到第一个匹配 nid 的区域,填充输出参数,并更新 *idx 以供下一次迭代。 *idx 的低32位包含type_a的索引,高32位索引type_b中每个区域之前的区域。例如,如果type_b区域如下所示:
0:[0-16), 1:[32-48), 2:[128-130)
高32位索引以下区域。
0:[0-0), 1:[16-32), 2:[48-128), 3:[130-MAX)
由于两个区域数组都已排序,函数会同步推进两个索引并返回每个交集。
-
void __init_memblock __next_mem_range_rev(u64 *idx, int nid, enum memblock_flags flags, struct memblock_type *type_a, struct memblock_type *type_b, phys_addr_t *out_start, phys_addr_t *out_end, int *out_nid)¶
for_each_*_range_rev() 的通用下一个函数
参数
u64 *idx
指向u64循环变量的指针
int nid
节点选择器,
NUMA_NO_NODE
表示所有节点enum memblock_flags flags
根据内存属性从块中选择
struct memblock_type *type_a
指向获取内存范围的memblock_type的指针
struct memblock_type *type_b
指向排除内存的memblock_type的指针
phys_addr_t *out_start
指向 phys_addr_t 的指针,用于范围的起始地址,可以是
NULL
phys_addr_t *out_end
指向 phys_addr_t 的指针,用于范围的结束地址,可以是
NULL
int *out_nid
指向 int 的指针,用于范围的 nid,可以是
NULL
描述
从type_a中找到未在type_b中标记为不适合的下一个范围。
-
int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size, struct memblock_type *type, int nid)¶
设置memblock区域的节点ID
参数
phys_addr_t base
要设置节点ID的区域的基地址
phys_addr_t size
要设置节点ID的区域的大小
struct memblock_type *type
要设置节点ID的memblock类型
int nid
要设置的节点ID
描述
将memblock type 区域在 [base, base + size) 范围内的节点ID设置为 nid。必要时,跨区域边界的区域会被分割。
返回值
成功时返回 0,失败时返回 -errno。
-
void __init_memblock __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone, unsigned long *out_spfn, unsigned long *out_epfn)¶
for_each_*_range_in_zone()的迭代器
参数
u64 *idx
指向u64循环变量的指针
struct zone *zone
所有内存块所在的区域
unsigned long *out_spfn
指向 ulong 的指针,用于范围的起始 pfn,可以是
NULL
unsigned long *out_epfn
指向 ulong 的指针,用于范围的结束 pfn,可以是
NULL
描述
此函数旨在作为for_each_mem_range类型迭代器的 zone/pfn 特定包装器。它们专门用于延迟内存初始化例程中,因此我们在整个代码中重复了许多相同的逻辑。因此,与其在多个位置拥有它,不如将其集中到一个新的迭代器中,该迭代器可以完成它们所需的一切,这似乎更有意义。
-
phys_addr_t memblock_alloc_range_nid(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end, int nid, bool exact_nid)¶
分配引导内存块
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
phys_addr_t start
要分配的内存区域的下界(物理地址)
phys_addr_t end
要分配的内存区域的上界(物理地址)
int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点bool exact_nid
控制分配是否回退到其他节点
描述
如果 end == MEMBLOCK_ALLOC_ACCESSIBLE
,则从 memblock.current_limit 限制的内存区域进行分配。
如果指定的节点无法容纳请求的内存且 exact_nid 为 false,则分配将回退到系统中的任何节点。
对于具有内存镜像的系统,首先尝试从启用镜像的区域分配,然后从任何内存区域重试。
此外,函数使用 kmemleak_alloc_phys 分配引导内存块,它永不报告为泄漏。
返回值
成功时为分配的内存块的物理地址,失败时为0
。
-
phys_addr_t memblock_phys_alloc_range(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end)¶
在指定范围内分配内存块
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
phys_addr_t start
要分配的内存区域的下界(物理地址)
phys_addr_t end
要分配的内存区域的上界(物理地址)
描述
在 start 和 end 之间分配 size 字节。
返回值
成功时返回分配的内存块的物理地址,失败时返回0
。
-
phys_addr_t memblock_phys_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid)¶
从指定的NUMA节点分配内存块
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点
描述
从指定的NUMA节点分配内存块。如果该节点没有可用内存,则尝试从系统中的任何节点分配。
返回值
成功时返回分配的内存块的物理地址,失败时返回0
。
-
void *memblock_alloc_internal(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid, bool exact_nid)¶
分配引导内存块
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
phys_addr_t min_addr
要分配的内存区域的下界(物理地址)
phys_addr_t max_addr
要分配的内存区域的上界(物理地址)
int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点bool exact_nid
控制分配是否回退到其他节点
描述
使用memblock_alloc_range_nid()
分配内存块,并将返回的物理地址转换为虚拟地址。
如果无法满足 min_addr 限制,则会放弃该限制,并回退到 min_addr 以下的内存进行分配。其他约束,例如节点和镜像内存,将再次在memblock_alloc_range_nid()
中处理。
返回值
成功时返回分配的内存块的虚拟地址,失败时返回NULL。
-
void *memblock_alloc_exact_nid_raw(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid)¶
在精确节点上分配引导内存块,不清零内存
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
phys_addr_t min_addr
优先从中分配内存区域的下界(物理地址)
phys_addr_t max_addr
优先从中分配内存区域的上界(物理地址),或者
MEMBLOCK_ALLOC_ACCESSIBLE
表示仅从memblock.current_limit值限制的内存中分配int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点
描述
公共函数,如果启用,提供额外的调试信息(包括调用者信息)。不清零已分配的内存。
返回值
成功时返回分配的内存块的虚拟地址,失败时返回NULL。
-
void *memblock_alloc_try_nid_raw(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid)¶
分配引导内存块,不清零内存且不发生panic
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
phys_addr_t min_addr
优先从中分配内存区域的下界(物理地址)
phys_addr_t max_addr
优先从中分配内存区域的上界(物理地址),或者
MEMBLOCK_ALLOC_ACCESSIBLE
表示仅从memblock.current_limit值限制的内存中分配int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点
描述
公共函数,如果启用,提供额外的调试信息(包括调用者信息)。不清零已分配的内存,如果请求无法满足则不发生panic。
返回值
成功时返回分配的内存块的虚拟地址,失败时返回NULL。
-
void *memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid)¶
分配引导内存块
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
phys_addr_t min_addr
优先从中分配内存区域的下界(物理地址)
phys_addr_t max_addr
优先从中分配内存区域的上界(物理地址),或者
MEMBLOCK_ALLOC_ACCESSIBLE
表示仅从memblock.current_limit值限制的内存中分配int nid
要查找的空闲区域的 nid,
NUMA_NO_NODE
表示任意节点
描述
公共函数,如果启用,提供额外的调试信息(包括调用者信息)。此函数会将已分配的内存清零。
返回值
成功时返回分配的内存块的虚拟地址,失败时返回NULL。
-
void *__memblock_alloc_or_panic(phys_addr_t size, phys_addr_t align, const char *func)¶
尝试分配内存并在失败时panic
参数
phys_addr_t size
要分配的内存块的大小(字节)
phys_addr_t align
区域和块大小的对齐
const char *func
调用函数名
描述
此函数尝试使用memblock_alloc分配内存,如果失败,则会使用格式化的消息调用panic。此函数不应直接使用,请使用宏memblock_alloc_or_panic。
-
void memblock_free_late(phys_addr_t base, phys_addr_t size)¶
直接将页面释放到buddy分配器
参数
phys_addr_t base
启动内存块的物理起始地址
phys_addr_t size
启动内存块的大小(以字节为单位)
描述
这仅在memblock分配器已被拆除但系统仍在初始化时有用。页面直接释放到buddy分配器。
-
unsigned long memblock_estimated_nr_free_pages(void)¶
从memblock角度返回估计的空闲页面数
参数
void
无参数
描述
在启动期间,子系统可能需要在buddy提供精确数字之前,获得整个系统中空闲页面的粗略估计。尤其是在启用CONFIG_DEFERRED_STRUCT_PAGE_INIT的情况下,从buddy获得的数字在启动期间可能非常不精确。
返回值
从memblock角度估计的空闲页面数。
-
bool __init_memblock memblock_is_region_memory(phys_addr_t base, phys_addr_t size)¶
检查区域是否为内存的子集
参数
phys_addr_t base
要检查区域的基地址
phys_addr_t size
要检查区域的大小
描述
检查区域 [base, base + size) 是否为内存块的子集。
返回值
如果为false则返回0,如果为true则返回非零值
-
bool __init_memblock memblock_is_region_reserved(phys_addr_t base, phys_addr_t size)¶
检查区域是否与保留内存相交
参数
phys_addr_t base
要检查区域的基地址
phys_addr_t size
要检查区域的大小
描述
检查区域 [base, base + size) 是否与保留内存块相交。
返回值
如果相交则为True,否则为False。
-
void memblock_free_all(void)¶
将空闲页面释放到buddy分配器
参数
void
无参数
-
int reserve_mem_find_by_name(const char *name, phys_addr_t *start, phys_addr_t *size)¶
通过名称查找保留内存区域
参数
const char *name
附加到保留内存区域的名称
phys_addr_t *start
如果找到,保存起始地址
phys_addr_t *size
如果找到,保存地址的大小。
描述
只有当找到 name 时,start 和 size 才会更新。
返回值
如果找到则为1,如果未找到则为0。
-
int reserve_mem_release_by_name(const char *name)¶
释放具有给定名称的保留内存区域
参数
const char *name
附加到保留内存区域的名称
描述
强制释放保留内存区域中的页面,以便这些内存可以用作空闲内存。释放后,保留区域的大小变为0。
返回值
如果释放则为1,如果未找到则为0。