Kexec 切换概念

Kexec 切换 (KHO) 是一种机制,允许 Linux 在 kexec 期间保留内存区域,这些区域可能包含序列化的系统状态。

它引入了多个概念

KHO FDT

每个 KHO kexec 都带有一个 KHO 特定的扁平化设备树 (FDT) blob,用于描述保留的内存区域。 这些区域包含序列化的子系统状态,或在 kexec 期间不应触及的内存数据。 KHO 后,子系统可以从 KHO FDT 检索和恢复保留的内存区域。

KHO 仅使用 FDT 容器格式和 libfdt 库,但不遵守与普通设备树相同的属性语义:属性以本机字节序传递,并且不存在像 regsranges 这样的标准化属性,因此没有 #...-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

struct folio *kho_restore_folio(phys_addr_t phys)

从保留的内存中重新创建 folio。

参数

phys_addr_t phys

folio 的物理地址。

返回

成功时返回指向 struct folio 的指针,失败时返回 NULL。

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,失败时返回错误代码

int kho_preserve_folio(struct folio *folio)

在 kexec 期间保留一个 folio。

参数

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,失败时返回错误代码