系统状态变更

有些用户非常不愿意重启系统。 这就需要提供更多的 livepatch 并保持它们之间的一些兼容性。

使用累积 livepatch 可以更容易地维护更多的 livepatch。 每个新的 livepatch 完全替换任何旧的 livepatch。 它可以保留、添加甚至删除修复。 并且由于原子替换功能,通常可以安全地将任何版本的 livepatch 替换为任何其他版本。

问题可能出在影子变量和回调上。 它们可能会改变系统行为或状态,使其不再安全地返回并使用旧的 livepatch 或原始内核代码。 此外,任何新的 livepatch 都必须能够检测到已经安装的 livepatch 所做的更改。

这就是 livepatch 系统状态跟踪发挥作用的地方。 它允许

  • 存储操作和恢复系统状态所需的数据

  • 使用更改 ID 和版本定义 livepatch 之间的兼容性

1. Livepatch 系统状态 API

系统状态可能会被多个 livepatch 回调或新使用的代码修改。 此外,必须能够找到已安装的 livepatch 所做的更改。

每个修改后的状态都由 struct klp_state 描述,请参阅 include/linux/livepatch.h。

每个 livepatch 定义一个 struct klp_states 数组。 它们提到 livepatch 修改的所有状态。

livepatch 作者必须为每个 struct klp_state 定义以下两个字段

  • id

    • 用于标识受影响的系统状态的非零数字。

  • version

    • 描述给定 livepatch 支持的系统状态更改变体的数字。

可以使用以下两个函数操作状态

2. Livepatch 兼容性

系统状态版本用于防止加载不兼容的 livepatch。 检查在启用 livepatch 时完成。 规则是

  • 允许任何全新的系统状态修改。

  • 对于已修改的系统状态,允许使用相同或更高版本的系统状态修改。

  • 累积 livepatch 必须处理来自已安装的 livepatch 的所有系统状态修改。

  • 允许非累积 livepatch 触及已修改的系统状态。

3. 支持的场景

Livepatch 有其生命周期,系统状态变更也是如此。 每个兼容的 livepatch 都必须支持以下场景

  • 当启用 livepatch 并且该状态尚未被正在被替换的 livepatch 修改时,修改系统状态。

  • 接管或更新系统状态修改,如果它已经被正在被替换的 livepatch 完成。

  • 禁用 livepatch 时,恢复原始状态。

  • 还原转换时,恢复先前的状态。 可能是原始系统状态或正在被替换的 livepatch 所做的状态修改。

  • 当发生错误且无法启用 livepatch 时,删除任何已做的更改。

4. 预期用法

系统状态通常由 livepatch 回调修改。 每个回调的预期角色如下

pre_patch()

  • 必要时分配 *state->data*。 分配可能会失败,并且 *pre_patch()* 是唯一可以停止加载 livepatch 的回调。 如果数据已由先前安装的 livepatch 提供,则不需要分配。

  • 在转换完成之前,执行新代码所需的任何其他准备操作。 例如,初始化 *state->data*。

    系统状态本身通常在 *post_patch()* 中修改,此时整个系统都能够处理它。

  • 发生错误时清理自己的烂摊子。 这可以通过自定义代码或显式调用 *post_unpatch()* 来完成。

post_patch()

  • 当它们兼容时,从先前的 livepatch 复制 *state->data*。

  • 进行实际的系统状态修改。 最终允许新代码使用它。

  • 确保 *state->data* 具有所有必要的信息。

  • 当不再需要时,从替换的 livepatch 中释放 *state->data*。

pre_unpatch()

  • 防止 livepatch 添加的代码依赖于系统状态更改。

  • 还原系统状态修改。

post_unpatch()

  • 通过检查 *klp_get_prev_state()* 区分转换反转和 livepatch 禁用。

  • 在转换反转的情况下,恢复先前的系统状态。 这可能意味着什么都不做。

  • 删除任何不再需要的设置或数据。

注意

*pre_unpatch()* 通常执行与 *post_patch()* 对称的操作。 唯一的区别是它仅在禁用 livepatch 时被调用。 因此,它不需要关心任何先前安装的 livepatch。

*post_unpatch()* 通常执行与 *pre_patch()* 对称的操作。 它也可能在转换反转期间被调用。 因此,它必须处理先前安装的 livepatch 的状态。