Kexec 切换概念¶
Kexec 切换 (KHO) 是一种机制,允许 Linux 在 kexec 期间保留内存区域,这些区域可能包含序列化的系统状态。
它引入了多个概念
KHO FDT¶
每个 KHO kexec 都带有一个 KHO 特定的扁平化设备树 (FDT) blob,用于描述保留的内存区域。 这些区域包含序列化的子系统状态,或在 kexec 期间不应触及的内存数据。 KHO 后,子系统可以从 KHO FDT 检索和恢复保留的内存区域。
KHO 仅使用 FDT 容器格式和 libfdt 库,但不遵守与普通设备树相同的属性语义:属性以本机字节序传递,并且不存在像 regs
和 ranges
这样的标准化属性,因此没有 #...-cells
属性。
KHO 仍在开发中。 FDT 模式不稳定,将来会发生变化。
暂存区域¶
要启动到 kexec,我们需要有一个物理上连续的内存范围,其中不包含移交的内存。 然后,Kexec 将目标内核和 initrd 放入该区域。 在页面分配器初始化之前,新内核专门使用此区域进行内存分配。
我们通过暂存区域保证我们始终拥有这样的区域:在首次启动时,KHO 分配多个物理上连续的内存区域。 由于 kexec 后,这些区域将被早期内存分配使用,因此每个 NUMA 节点都有一个暂存区域,外加一个满足不需要特定 NUMA 节点分配请求的暂存区域。 默认情况下,暂存区域的大小是根据启动期间分配的内存量计算的。 可以使用 kho_scratch
内核命令行选项显式定义暂存区域的大小。 初始化页面分配器时,暂存区域声明为 CMA,以便其内存可以在系统生命周期中使用。 CMA 保证没有移交页面落入该区域,因为移交页面必须位于静态物理内存位置,并且 CMA 强制只有可移动页面才能位于内部。
在 KHO kexec 之后,我们忽略 kho_scratch
内核命令行选项,而是重用最初分配的完全相同的区域。 这允许我们递归执行任意数量的 KHO kexec。 因为我们将此区域用于启动内存分配和 kexec blob 的目标内存,所以该内存区域的某些部分可能会被保留。 这些保留与下一个 KHO 无关,因为 kexec 甚至可以覆盖原始内核。
KHO 完成阶段¶
为了启用基于用户空间的 kexec 文件加载器,内核需要在执行实际的 kexec 之前能够提供描述当前内核状态的 FDT。 生成该 FDT 的过程称为序列化。 当 FDT 生成时,系统的某些属性可能会变得不可变,因为它们已经写入 FDT。 该状态称为 KHO 完成阶段。
公共 API¶
-
int kho_add_subtree(struct kho_serialization *ser, const char *name, void *fdt)¶
记录 KHO 根树中子 FDT 的物理地址。
参数
struct kho_serialization *ser
由 KHO 通知程序传递的序列化控制对象。
const char *name
子树的名称。
void *fdt
子树 blob。
描述
在 KHO 根 FDT 中创建一个名为 **name** 的新子节点,并记录 **fdt** 的物理地址。 **fdt** 的页面也必须由 KHO 保留,以便新内核在 kexec 后检索它。
debugfs blob 条目也在 /sys/kernel/debug/kho/out/sub_fdts/**name**
创建。
返回
成功时返回 0,失败时返回错误代码
参数
struct folio *folio
要保留的 folio。
描述
指示 KHO 在 kexec 期间保留整个 folio。 顺序也将被保留。
返回
成功时返回 0,失败时返回错误代码
-
int kho_preserve_phys(phys_addr_t phys, size_t size)¶
在 kexec 期间保留一个物理上连续的范围。
参数
phys_addr_t phys
范围的物理地址。
size_t size
范围的大小。
描述
指示 KHO 在 kexec 期间保留从 **phys** 到 **phys** + **size** 的内存范围。
返回
成功时返回 0,失败时返回错误代码
-
int kho_retrieve_subtree(const char *name, phys_addr_t *phys)¶
按名称检索保留的子 FDT。
参数
const char *name
传递给
kho_add_subtree()
的子 FDT 的名称。phys_addr_t *phys
如果找到,子 FDT 的物理地址将存储在 **phys** 中。
描述
检索名为 **name** 的保留子 FDT,并将其物理地址存储在 **phys** 中。
返回
成功时返回 0,失败时返回错误代码