libvirt对qemu虚拟机的numa亲和性管理机制

达芬奇密码2018-07-23 09:47

在上一篇 (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绑定信息。


本文来自网易实践者社区,经作者岳文远授权发布。