让KVM支持滚动热升级:Multi-KVM
Multi-KVM 允许多个独立的 KVM 模块在同一台 Linux 主机上同时加载、卸载和运行。主要包括:
-
升级和回滚 KVM,而不会中断正在运行的虚拟机
-
允许在同一主机上运行具有不同参数的 KVM 模块
-
为 KVM 的 A/B 测试提供便利
1. 设计方案
隔离性
-
从整个内核中隐藏 KVM 内部结构
-
详细参见: https://lore.kernel.org/lkml/20230916003118.2540661-1-seanjc@google.com[PATCH 00/26] KVM: vfio: Hide KVM internals from others - Sean Christopherson
Multi KVM
-
将 kvm_intel.ko 和 kvm_amd.ko 合并为 kvm.ko
-
暴露多个 kvmN.ko 模块和 /dev/kvmN 设备
-
"N" 通过 Kconfig 字符串定义(默认为 null/off)
VAC
-
将 KVM 中的共享系统资源提取到新的 "基础模块"(VAC)中
-
VAC = Virtualization Acceleration Code 虚拟化加速代码 (不可升级单元的模块)
用户空间
-
无需更改用户空间 VMM
- 符号链接(/dev/kvm -> /dev/kvmN)
- kvm 绑定挂载等
-
不需要用户空间更改部署、使用方式和VM生命周期管理等
2.主要工作
-
将 KVM 内部结构与内核其他部分隔离
-
修改基本模块名称/命名空间
-
将 x86 厂商模块合并到 kvm.ko 中
-
其他
3.LKML Review
From: Sean Christopherson <seanjc@google.com>
Subject: [PATCH 00/26] KVM: vfio: Hide KVM internals from others
Date: Fri, 15 Sep 2023 17:30:52 -0700 [thread overview]
Message-ID: <20230916003118.2540661-1-seanjc@google.com> (raw)
Anish Ghulati (1):
KVM: arm64: Move arm_{psci,hypercalls}.h to an internal KVM path
Sean Christopherson (25):
vfio: Wrap KVM helpers with CONFIG_KVM instead of CONFIG_HAVE_KVM
vfio: Move KVM get/put helpers to colocate it with other KVM related
code
virt: Declare and define vfio_file_set_kvm() iff CONFIG_KVM is enabled
vfio: Add struct to hold KVM assets and dedup group vs. iommufd code
vfio: KVM: Pass get/put helpers from KVM to VFIO, don't do circular
lookup
KVM: Drop CONFIG_KVM_VFIO and just look at KVM+VFIO
x86/idt: Wrap KVM logic with CONFIG_KVM instead of CONFIG_HAVE_KVM
KVM: x86: Stop selecting and depending on HAVE_KVM
KVM: arm64: Stop selecting and depending on HAVE_KVM
KVM: s390: Stop selecting and depending on HAVE_KVM
KVM: MIPS: Make HAVE_KVM a MIPS-only Kconfig
KVM: arm64: Include KVM headers to get forward declarations
KVM: arm64: Move ARM specific headers in include/kvm to arch directory
KVM: Move include/kvm/iodev.h to include/linux as kvm_iodev.h
KVM: MIPS: Stop adding virt/kvm to the arch include path
KVM: PPC: Stop adding virt/kvm to the arch include path
KVM: s390: Stop adding virt/kvm to the arch include path
KVM: Standardize include paths across all architectures
perf/x86: KVM: Have perf define a dedicated struct for getting guest
PEBS data
entry/kvm: Drop @vcpu param from arch_xfer_to_guest_mode_handle_work()
entry/kvm: KVM: Move KVM details related to signal/-EINTR into KVM
proper
KVM: arm64: Move and consolidate "public" functions in asm/kvm_host.h
powerpc/xics: Move declaration of xics_wake_cpu() out of kvm_ppc.h
KVM: PPC: Rearrange code in kvm_ppc.h to isolate "public" information
KVM: Hide KVM internal data structures and values from kernel at-large
MAINTAINERS | 1 -
arch/arm64/Kconfig | 1 -
arch/arm64/include/asm/kvm_emulate.h | 3 +
arch/arm64/kvm/Kconfig | 2 -
arch/arm64/kvm/Makefile | 2 -
arch/arm64/kvm/arch_timer.c | 5 +-
arch/x86/Kconfig | 1 -
arch/x86/events/core.c | 5 +-
arch/x86/events/intel/core.c | 18 +-
arch/x86/events/perf_event.h | 3 +-
arch/x86/include/asm/hardirq.h | 2 +-
.
.
.
这是一个没被标记为RFC的代码提交, 目的是将 KVM 的内部结构从内核中隐藏起来。这里的 "内部"指的是数据结构、枚举、#defines、API等。这些数据结构、枚举、#defines、API等本应仅限于KVM使用, 但却由于kvm_host.h的存在而暴露在其他地方。由于 kvm_host.h(以及其他头文件) 存在于全局 include 路径中,因此在任何地方都会暴露出来。
隐藏 KVM 内部结构的动机是为了在无需重启主机的情况下安全地加载新的KVM 模块。新KVM模块加载而无需重启主机, 其中的 "新 "并不一定是严格意义上的更新, 只是 KVM 的不同版本。隐藏KVM 的内部结构意味着这些资源可以在不同的 KVM 实例中进行更改。例如: 可以修改 kvm_vcpu 结构的布局, 以引入与新的 KVM 实例相关的新字段, 引入与新功能相关的新字段或解决硬件错误。
所有这一切的最终目标是允许在一台主机上同时加载和运行多个KVM模块。例如: 修复部署问题、解决错误或加入新功能,而无需关闭主机上不相干的虚拟机。
目前,我们的首要目标是让 KVM x86 达到这样一种状态, 即 KVM x86 不会将对外部无用的信息暴露给内核。例如: 页面写入跟踪将使用KVM-GT技术, 使用新的 API。
我不认为我在此之前 "正式"提出过隐藏 KVM 内部细节的想法, 因此我决定不标记此 RFC, 因为这些改动最终并非不具备侵入性, 而且在最后六个补丁之前的所有修改都不会标记为RFC, 即使隐藏KVM内部细节的实现最终被否决。
理想情况下, 将会有 5 个独立的提交, 如果我们想以这种方式合并这些内容,我觉得或许也可以实现。例如按照下列步骤提交:
(1) VFIO 清理
(2) 删除 HAVE_KVM
(3) 清理 Makefile
(4) x86 perf 清理
(5) 实现KVM隐藏细节
HAVE_KVM 和 virt/kvm 包括的东西, 并不是严格意义上的必需, 但我还是把它们包括进来了, 因为它们也是又必要处理的。
除了 #ifdef __KVM__ 方法, 我们还探索了其他几种方法, 但都非常糟糕。 我真正想做到的(现在也想做到)是把大部分的 kvm_host.h (以及其他 KVM 头文件) 藏在 virt/kvm 中, 但每次尝试都以失败告终。即使有 __KVM__ 宏保护也无济于事, 原因是每个架构的 kvm_host.h 与通用的 kvm_host.h 交织在一起。试图以迭代补丁提交看起来比较复杂(每个补丁不可避免地会滚成一头巨兽)。
我们考虑过的另一个想法(这是我想到的, 我甚至为在内部提出这个想法而感到羞愧)。那就是将所有头文件移到 virt/kvm 下, 在全局头文件路径中添加, virt/kvm/include 到全局头文件路径中, 然后让 KVM x86 在配置默认为隐藏 virt/kvm/include。
我讨厌这个想法因为它开创了一个不好的先例,需要大量的文件移动, 而不会给其他架构带来任何好处。我希望用 #ifdef __KVM__ 来保护 KVM 的内部结构,可以让我们慢慢地把代码整理干净, 以便有一天 KVM 只向内核的其他部分暴露少量的 API。