在上一篇 (https://sq.163yun.com/blog/article/177542610675490816) 中我们详细分析了numad对云主机进程设置内存和cpu亲和性的原理,今天我们继续分析一下libvirt中是如何根据下发的配置为云主机分配vcpu和内存亲和性的。
libvirt的配置项说明
kvm云主机作为宿主机上一个用户态qemu进程,libvirt在启动云主机的时候需要根据传入的参数对其进行CPU和内存亲和性的设置。libvirt支持对云主机做下列自定义配置
<vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu>
<cputune>
<vcpupin vcpu="0" cpuset="1-4,^2"/>
<emulatorpin cpuset="1-3"/>
</cputune>
<numatune>
<memory mode="strict" nodeset="1-4,^3"/>
<memnode cellid="0" mode="strict" nodeset="1"/>
<memnode cellid="2" mode="preferred" nodeset="2"/>
</numatune>
- vcpu
- cpuset qemu进程的绑定范围,如果未指定默认绑定到所有可用的物理核心上,对emulator和vcpu都生效。如果指定了emulatorpin,该配置对qemu主进程失效。如果指定了vcpupin,该配置对指定了vcpupin的vcpu失效。对其它未配置vcpupin的vcpu仍然有效。
- placement cpuset的分布方式,可以是auto或者static。如果指定了cpuset,默认为static或者numatune的placement。如果指定auto,libvirt会与numad服务通信获取建议的分布方式,cpuset参数指定的配置失效。
- cputune
- vcpupin cpuset 优先级最高,如果指定按照该值绑定vcpu,vcpu的cpuset失效。
- emulatorpin cpuset 优先级最高,如果指定按照该值绑定emulator。vcpu的cpuset失效。
- numatune memory 用于指定云主机内存在物理机numa node上的分配模式。如果
- mode 内存分布的模式,可以为interleave/strict/preferred,默认为strict。根据mode决定云主机的内存是否严格遵守nodeset的规则。
- nodeset 指定内存分配的具体节点
- placement static/auto可选,默认与vcpu placement一致,当指定了nodeset时默认为static。如果指定了auto,nodeset将被忽略,云主机将只从numad返回结果的node上申请内存。如果vcpu的placement指定了auto,会默认增加一个numatune的配置,placement的值为auto,mode为strict。
libvirt中的处理逻辑
libvirt在启动qemu进程过程中,会根据xml传入的参数确认最终的numa亲和性策略。
- 如果指定vcpu placement或者numatune memory placement为auto,libvirt会通过
numad -w vcpu:mem
的命令形式获取numad的分布建议。
- qemu进程fork出来之后根据注册的hook函数设置进程的内存numa亲和性。
- 如果指定numatune memory placement为static并且没有指定nodeset,直接忽略不做处理
- 如果指定numatune memory placement为static并且指定了nodeset,使用指定的nodeset
- 如果指定numatune memory placement为auto,nodeset使用numad提供的分布建议
- 如果指定numatune memory mode为strict,根据指定的nodeset设置qemu内存分布亲和性
- 如果指定numatune memory mode为preferred,要求nodeset中只能有一个target node
libvirt使用libnuma提供的接口为qemu进程设置numa node亲和性。
numa_available() 判断宿主机节点是否支持numa,在调用其它的libnuma接口之前必须先判断它的返回值。如果为负值其它所有接口都没有define
numa_max_node() 返回宿主机节点上最大的numa node编号
numa_set_bind_policy() 设置进程的node绑定策略为preferred或者strict。指定为preferred时允许kernel在目标node上没有足够内存时从其它node上申请,如果是strict在这种情况下会申请失败。如果指定为strict并且目标为多个target时,在某些内核版本下降只会从第一个node上分配内存
numa_set_membind() 设置进程的内存分布target node。
在cgroup中固化内存和CPU numa亲和性配置
- 指定numatune memory mode为strict时,如果指定placement为auto按照numad建议设置cpuset mem;如果指定numatune memory nodeset,按照这个值设置。
- 指定vcpu placement为auto按照numad建议设置cpuset cpus
- 指定vcpu cpuset按照这个值设置cpuset cpus
如果没有指定vcpu emulatorpin,通过sched_setaffinity
为qemu进程设置CPU亲和性
- 如果vcpu placement指定auto,按照numad建议设置进程亲和性
- 如果vcpu placement未指定或者指定static,按照cpuset值绑定。如果cpuset未指定,绑定所有可用pcpu
- 通过cgroup固化vcpu tune信息。
- 指定了vcpupin,为vcpu创建cpuset子系统下的子目录,将vcpu线程id加入子目录的task,并初始化cpu mask。
- 通过cgroup固化emulator tune信息
- 如果指定vcpu placement为auto,emulator cpu亲和性按照numad的建议绑定
- 或者按照指定的emulatorpin中的cpuset绑定
- 或者按照指定的vcpu cpuset绑定
- 如果宿主机节点不支持cgroup,还可以通过
sched_setaffinity
按照上面的逻辑为vcpu和emulator设置cpu绑定信息。
本文来自网易实践者社区,经作者岳文远授权发布。