这篇文章给大家分享的是介绍虚拟机内存热插拔从最初的virtio-balloon到如今virtio-mem的演化之路。
内存热插拔技术演化的时间线:
|
virtio-balloon |
DIMM-based memory hot(un)plug |
virtio-mem |
出现时间 |
2008年 |
2015年 |
2020年 |
内存热插拔技术的主要区别:
对比项 |
virtio-balloon |
DIMM-based memory hot(un)plug |
virtio-mem |
虚拟化类型 |
半虚拟化 |
全虚拟化 |
半虚拟化 |
内存粒度 |
Page, e.g. 4KB |
>=128MB on x86 Linux |
4MB on x86-64 |
硬件架构依赖 |
No |
Yes,依赖ACPI |
No |
NUMA |
不支持 |
支持 |
支持 |
virtio-balloon
Virtio-Balloon可以认为是早期的间接实现内存热插拔的功能,但其实没有热插拔,而是动态增减内存大小,并且依赖于客户机里的virtio_balloon驱动,对客户机来说并没有硬件上的增减。
Virtio-Balloon即内存气球,可以用来动态调整虚拟机内存,在VM和hypervisor之间迁移物理内存。virtio-balloon遵循Virtio半虚拟化规范,包含了虚拟机里virtio-balloon前端驱动和hypervisor模拟的virtio-balloon后端设备。
Virtio-Balloon除了用来动态调整虚拟机内存,还支持监控虚机内存指标的功能。
Virtio-Balloon在节约内存和灵活分配内存方面有明显的优势,其好处有如下三点:
1、因为能够控制和监控ballooning,所以ballooning能够潜在地节约大量的内存。它不同于内存页共享技术(KSM是内核自发完成的、不可控),VM系统的内存只有在通过命令行调整balloon时才会随之改变,所以能够监控系统内存并验证ballooning引起的变化。
2、Ballooning对内存的调节很灵活,既可以精细的请求少量内存,又可以粗犷的请求大量的内存。
3、hypervisor使用ballooning让VM归还部分内存,从而可以缓解其内存压力。而且从气球中回收的内存也不要求一定要被分配给另外某个进程(或另外的VM)。
Virtio-Balloon进行内存复用本身存在一些问题:
1、Guest对内存变化会进行感知,Balloon特性本身是kernel所具备的,本身是通过修改识别的内存信息来限制Guest中的使用。所以不是很友好。
2、内存复用需要实时监控,发现客户虚拟机内存使用过多还要及时归还内存,对于系统本身做这个功能有很大的局限性。
3、Balloon特性的内存复用并不是本质上进行内存的冗余复用,仅仅是借东墙补西墙,当虚拟机都大量使用内存时候,并不能实际突破物理内存上限。
4、如果有大量内存从VM系统中回收,Ballooning可能会降低VM操作系统运行的性能。一方面,内存的减少,可能会让VM中作为磁盘数据缓存的内存被放到气球中,从而VM中的磁盘I/O访问会增加;另一方面,如果处理机制不够好,也可能让VM中正在运行的进程由于内存不足而执行失败。
5、内存的动态增加或减少,可能会使内存被过度碎片化,从而降低内存使用时的性能。另外,内存的变化会影响到VM内核对内存使用的优化,比如:内核起初根据目前状态对内存的分配采取了某个策略,而突然由于balloon的效果让可用内存减少了很多,这时起初的内存策略可能就不是太优化的了。
virtio-balloon工作原理
内存气球有两种操作:
气球收缩:宿主机的内存还给虚拟机,相当于VM内存热缩减
气球膨胀:虚拟机的内存被拿掉给宿主机,相当于VM内存热添加
气球收缩(balloon deflate)的工作原理:
1、VM里的virtio-balloon驱动收到hypervisor的请求后,释放掉VM内存气球占用的部分内存。
2、Guest OS可以使用VM气球释放出来的内存,从而实现VM内存热添加。
气球膨胀(balloon inflate)的工作原理:
1、Guest virtio-balloon驱动收到hypervisor的请求后,VM内存气球膨胀,气球中的内存就不能被客户机访问,从而实现VM内存缩减。
2、hypervisor可以重用膨胀的内存(例如:用于其他VM)
virtio-balloon的已知问题
1、设计上的漏洞:Guest可以重复使用膨胀的内存和伪造balloon stat;Hypervisor不能拒绝任何 膨胀/收缩 的请求。
2、基于4KB page,hypervisor不能支持大页/不同page size
3、不支持NUMA
4、不支持VFIO
DIMM-based memory hot(un)plug
基于模拟DIMM的内存热插拔(DIMM-based memory hot(un)plug),是指hypervisor通过hypervisor对DIMM硬件技术进行软件模拟以实现虚拟机内存热插拔。
目前Qemu的内存热插拔通常指的就是基于模拟DIMM的内存热插拔。
基于模拟DIMM的内存热插拔,除了需hypervisor对DIMM硬件进行模拟,还需要依赖Guest系统支持,如Linux 内核仅在的 64 位架构上支持内存热插拔,例如 x86_64、arm64、ppc64、s390x 和 ia64。Linux内存热插拔的粒度取决于架构。例如x86_64 使用 128 MiB,ppc64 使用 16 MiB。
Linux内核不能主动感知到PCI总线上DIMM内存条的插拔,需要有固件先触发一个内存热插拔事件通知Linux内核,Linux内核才能可以开始热插拔内存。在支持 ACPI 的平台,例如 x86_64,可以通过 ACPI固件去支持内存热插拔通知。
Linux编译时配置将 CONFIG_ACPI_HOTPLUG_MEMORY 配置为内核模块,所以我们需要首先加载一个内核模块:acpi_memhotplug.ko。
ACPI (Advanced Configuration and Power Interface) 是由业界一些软硬件公司共同开发的开放式工业规范。它能使软、硬件、操作系统(OS),主机板和外围设备,依照一定的方式管理用电情况和系统硬件产生的 Hot-Plug 事件,让操作系统从用户的角度上直接支配即插即用设备,不同于以往直接通过基于 BIOS 的方式的管理。
什么是DIMM?
DIMM全称Double-Inline Memory Module,中文名叫双列直插式存储模块,是一种硬件技术,是指奔腾CPU推出后出现的新型内存条,它提供了64位的数据通道。
在80286时代,内存颗粒(Chip)是直接插在主板上的,叫做DIP(Dual In-line Package)。到了80386时代,换成1片焊有内存颗粒的电路板,叫做SIMM(Single-Inline Memory Module)。由阵脚形态变化成电路板带来了很多好处模块化,安装便利等等,由此DIY市场才有可能产生。当时SIMM的位宽是32bit,即一个周期读取4个字节,到了奔腾时,位宽变为64bit,即8个字节,于是SIMM就顺势变为DIMM(Double-Inline Memory Module)。这种形态一直延续至今,也是内存条的基本形态。
现在DIMM分为很多种:
RDIMM: 全称(Registered DIMM),寄存型模组,主要用在服务器上,为了增加内存的容量和稳定性分有ECC和无ECC两种,但市场上几乎都是ECC的。
UDIMM:全称(Unbuffered DIMM),无缓冲型模组,这是我们平时所用到的标准台式电脑DIMM,分有ECC和无ECC两种,一般是无ECC的。
SO-DIMM:全称(Small Outline DIMM),小外型DIMM,笔记本电脑中所使用的DIMM,分ECC和无ECC两种。
Mini-DIMM:DDR2时代新出现的模组类型,它是Registered DIMM的缩小版本,用于刀片式服务器等对体积要求苛刻的高端领域。
DIMM插槽:指用来插DIMM内存条的插槽,主板所支持的内存种类和容量都由内存插槽来决定的,一个插槽只能插一根DIMM条。
DIMM的软件模拟实现
hypervisor会先alloc一块内存,作为memory device的内存,添加到memory slot上,guest里面通过acpi的事件,得知memory device热添加。
如果guest是linux的话,在/sys/devices/system/memory目录下会增加新的memory目录,选择online为0的,修改为1就能让memory上线。例如,执行echo 1 > /sys/devices/system/memory/memory/memory64/online 执行完命令后,通过free -h或者cat /proc/meminfo就可以看到内存增加了。
pc-dimm 设备 (hw/mem/pc-dimm.c) 用来表示一个内存条。内存可以通过创建一个新的 pc-dimm 设备实现热插功能。虽然名称中有 pc 字段,但该设备也可与 ppc、s390 机器配合使用。要注意的是,vm 启动时的初始 RAM 可能无法使用 “pc-dimm” 设备建模,并且无法热拔出。vm RAM 本身并不在 pc-dimm 对象的容器中,也就是说,pc-dimm 对象必须与内存后端对象关联。
DIMM的已知问题
1、一些架构不支持DIMM,例如:s390x
2、一些架构不支持memory hotplug通告,例如:ARM64要在guest手动操作
virtio-mem
内存气球(virtio-balloon)和基于模拟 DIMM的热插拔(DIMM-based memory hot(un)plug)都可以动态调整虚拟机内存的大小。但是这两种传统方法提供的灵活性有限,与 vNUMA 和快速操作系统重启等重要技术不兼容,或者不适用于托管不受信任的虚拟机。
为了克服这些限制,KVM社区在2018年开始计划引入了 virtio-mem,这是一种基于 VIRTIO 的半虚拟化内存设备,专为云环境中的细粒度、NUMA 感知内存热插拔而设计。
相比基于模拟 DIMM的热插拔, virtio-mem能以更小的粒度为每个 NUMA 节点插拔内存,例如 在x86-64 上支持最小粒度4 MiB。与内存气球相比,virtio-mem 完全支持 NUMA,并通过设计支持快速操作系统重启,同时保证可以可靠地检测到尝试使用比约定更多内存的恶意虚拟机。
virtio-mem的技术特点是细粒度和支持NUMA。
virtio-mem遵循Virtio半虚拟化规范,包含了虚拟机里virtio-mem前端驱动和hypervisor模拟的virtio-mem后端设备。
使用virtio-mem时,hypervisor在启动虚机时就按虚机的最大内存值向物理机申请内存。
virtio-mem的设计目标
virtio-mem的设计结合了virtio-balloon和DIMM-based memory hot(un)plug的优点,避免了已知问题。
- 适用于所有架构, 提供了一种灵活的、跨架构的内存热插拔解决方案,避免了现有技术、架构和接口造成的许多限制
- 完全在hypervisor内部管理大小更改
- 提供一种安全的方式来检测恶意客户端
- 支持不同page sizes/ 大页
- 支持NUMA
- 在x86-64上,目前可以在运行Linux的虚拟机上以4个MiB粒度添加/删除内存,而在不久的将来,目标是支持2个MiB粒度。
virtio-mem的工作原理
virtio-mem devices技术要点:
- 每个device在VM物理地址空间中管理一个专用的memory region
- 每个device都可以分配给VM的一个NUMA node
- 以块为粒度(eg:>= 4 MB on x86-64)
virtio-mem devices的三个主要属性:
- Size:当前已添加的内存
- Maximum size: 最多可以添加多少内存
- Requested size: 请求Guest driver 增/减 内存以达到请求的大小。
virtio-mem目前平台支持
Linux Kernel从v5.8-rc1开始支持virtio-mem
Windows目前还不支持virtio-mem
QEMU 从v5.1.0-rc1开始支持virtio-mem
libvirt 从v7.9.0-rc1开始支持virtio-mem
参考资料
virtio-mem主页
"virtio-mem: Paravirtualized Memory" talk at KVM Forum 2018
"Virtio-(balloon|pmem|mem): Managing Guest Memory" talk at KVM Forum 2020
virtio-spec: Add virtio-mem device specification
https://github.com/oasis-tcs/virtio-spec/blob/master/virtio-mem.tex
Qemu memory-hotplug
https://github.com/qemu/qemu/blob/master/docs/memory-hotplug.txt
Linux kernel memory-hotplug
https://www.kernel.org/doc/html/latest/admin-guide/mm/memory-hotplug.html