KVM 暂停轮询系统¶
KVM 暂停轮询系统在 KVM 内部提供了一项功能,该功能在某些情况下可以通过在宿主机中进行一段时间的轮询来减少客户机的延迟,即在客户机选择通过让出(cedeing)不再运行时进行轮询。也就是说,当客户机 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 的拒绝服务。