KVM 暂停轮询系统

KVM 暂停轮询系统在 KVM 中提供了一个功能,在某些情况下,通过在客户机选择不再运行时(通过让出)之后的一段时间内在主机中进行轮询,可以减少客户机的延迟。也就是说,当客户机 vcpu 让出时,或者在 powerpc 的情况下,当单个 vcore 的所有 vcpu 都让出时,主机内核会轮询唤醒条件,然后将 cpu 让给调度程序,以便让其他东西运行。

在客户机可以非常快速地再次运行的情况下,轮询可以提供延迟优势,至少可以节省我们通过调度程序的一次行程,通常在几个微秒的数量级,尽管性能优势取决于工作负载。如果在轮询间隔期间没有收到唤醒源,或者运行队列上的其他任务是可运行的,则会调用调度程序。因此,暂停轮询对于唤醒周期非常短的工作负载特别有用,在这种情况下,暂停轮询所花费的时间被最小化,并且不调用调度程序所节省的时间是可以区分的。

通用暂停轮询代码在以下位置实现:

virt/kvm/kvm_main.c: kvm_vcpu_block()

powerpc kvm-hv 特定情况在以下位置实现:

arch/powerpc/kvm/book3s_hv.c: kvmppc_vcore_blocked()

暂停轮询间隔

在调用调度程序之前进行轮询的最长时间(称为暂停轮询间隔)会根据轮询的感知有效性而增加和减少,以试图限制无意义的轮询。此值存储在 vcpu 结构中

kvm_vcpu->halt_poll_ns

或者在 powerpc kvm-hv 的情况下,存储在 vcore 结构中

kvmppc_vcore->halt_poll_ns

因此,这是一个每个 vcpu(或 vcore)的值。

在轮询期间,如果在暂停轮询间隔内收到唤醒源,则该间隔保持不变。如果在轮询间隔期间没有收到唤醒源(因此调用了调度程序),则有两种选择,要么轮询间隔和总阻塞时间[0]小于全局最大轮询间隔(请参阅下面的模块参数),要么总阻塞时间大于全局最大轮询间隔。

如果轮询间隔和总阻塞时间都小于全局最大轮询间隔,则可以增加轮询间隔,以希望下次在更长的轮询间隔期间,在主机轮询时收到唤醒源,并且将获得延迟优势。轮询间隔在函数 grow_halt_poll_ns() 中增长,并乘以模块参数 halt_poll_ns_grow 和 halt_poll_ns_grow_start。

如果总阻塞时间大于全局最大轮询间隔,则主机永远不会轮询足够长的时间(受全局最大值限制)以在轮询间隔期间唤醒,因此最好将其缩小以避免无意义的轮询。轮询间隔在函数 shrink_halt_poll_ns() 中缩小,并除以模块参数 halt_poll_ns_shrink,或者如果 halt_poll_ns_shrink == 0,则设置为 0。

值得注意的是,此调整过程尝试调整到某个稳态轮询间隔,但仅对于以近似恒定速率出现的唤醒才能真正做到这一点,否则轮询间隔将不断调整。

[0] 总阻塞时间

调用暂停轮询函数和接收到唤醒源之间的时间(无论是否在该函数中调用了调度程序)。

模块参数

kvm 模块有 4 个可调模块参数,用于调整全局最大轮询间隔、初始值(从 0 开始增长)以及轮询间隔增长和缩小的速率。这些变量在 include/linux/kvm_host.h 中定义,并在 virt/kvm/kvm_main.c 中作为模块参数,或者在 powerpc kvm-hv 的情况下在 arch/powerpc/kvm/book3s_hv.c 中定义。

模块参数

描述

默认值

halt_poll_ns

全局最大轮询间隔,定义每个 vcpu 的轮询间隔的上限值。

KVM_HALT_POLL_NS_DEFAULT

(每个架构的值)

halt_poll_ns_grow

在 grow_halt_poll_ns() 函数中,暂停轮询间隔乘以的值。

2

halt_poll_ns_grow_start

在 grow_halt_poll_ns() 函数中,从零开始增长的初始值。

10000

halt_poll_ns_shrink

在 shrink_halt_poll_ns() 函数中,暂停轮询间隔除以的值。

2

这些模块参数可以从以下 sysfs 文件中设置:

/sys/module/kvm/parameters/

注意:这些模块参数是系统范围的值,不能

在每个虚拟机的基础上进行调整。

下次暂停时,新的和现有的 vCPU 都会接收到对这些参数的任何更改,但使用 KVM_CAP_HALT_POLL 的虚拟机除外(请参阅下一节)。

KVM_CAP_HALT_POLL

KVM_CAP_HALT_POLL 是一项虚拟机功能,允许用户空间在每个虚拟机的基础上覆盖 halt_poll_ns。使用 KVM_CAP_HALT_POLL 的虚拟机完全忽略 halt_poll_ns(但仍然遵守 halt_poll_ns_grow、halt_poll_ns_grow_start 和 halt_poll_ns_shrink)。

有关此功能的更多信息,请参阅最终 KVM(基于内核的虚拟机)API 文档

其他说明

  • 设置 halt_poll_ns 模块参数时应谨慎,因为较大的值有可能将原本几乎完全空闲的机器上的 cpu 使用率驱动到 100%。这是因为即使客户机在唤醒期间几乎没有完成任何工作,并且间隔很远,如果该周期短于全局最大轮询间隔 (halt_poll_ns),则主机将始终在整个阻塞时间内进行轮询,从而使 cpu 利用率达到 100%。

  • 暂停轮询本质上是在功耗和延迟之间进行权衡,应使用模块参数来调整对此的偏好。空闲的 cpu 时间本质上会转换为主机内核时间,目的是在进入客户机时减少延迟。

  • 只有当该 cpu 上没有其他任务可运行时,主机才会进行暂停轮询,否则轮询将立即停止,并调用调度程序以允许该其他任务运行。因此,这不允许客户机导致 cpu 的拒绝服务。