系统状态更改¶
一些用户非常不愿意重启系统。这就带来了提供更多 livepatch 并保持它们之间一定兼容性的需求。
使用累积式 livepatch 维护更多的 livepatch 要容易得多。每个新的 livepatch 完全替换任何旧的 livepatch。它可以保留、添加甚至删除修复。而且由于原子替换(atomic replace)特性,用任何其他版本替换 livepatch 的任何版本通常都是安全的。
问题可能随影子变量(shadow variables)和回调(callbacks)而出现。它们可能会改变系统行为或状态,从而使得返回并使用旧的 livepatch 或原始内核代码不再安全。此外,任何新的 livepatch 都必须能够检测已安装的 livepatch 已经进行了哪些更改。
这就是 livepatch 系统状态跟踪发挥作用的地方。它允许:
存储操纵和恢复系统状态所需的数据
使用更改 ID(change 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 所支持的系统状态更改变体的数字。
可以使用两个函数来操纵该状态:
获取与给定 livepatch 和状态 ID 关联的
struct klp_state。
获取与给定特征 ID(feature id)和已安装 livepatch 关联的
struct klp_state。
2. Livepatch 兼容性¶
系统状态版本用于防止加载不兼容的 livepatch。该检查在启用 livepatch 时进行。规则如下:
允许任何完全崭新的系统状态修改。
对于已经修改过的系统状态,允许具有相同或更高版本的系统状态修改。
累积式 livepatch 必须处理来自已安装 livepatch 的所有系统状态修改。
允许非累积式 livepatch 触及已经修改过的系统状态。
3. 支持的场景¶
Livepatch 有其生命周期,系统状态更改也是如此。每个兼容的 livepatch 都必须支持以下场景:
当 livepatch 被启用且该状态尚未被正在被替换的 livepatch 修改时,修改系统状态。
当系统状态修改已经由正在被替换的 livepatch 完成时,接管或更新该系统状态修改。
当 livepatch 被禁用时,恢复原始状态。
当转换(transition)被回滚时,恢复先前的状态。它可能是原始系统状态,也可能是由正在被替换的 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 的状态。