​[kvm][qemu]vm exit的优化

2018-04-09 11:29:25 浏览数 (1)

前言: 减少vm exit的次数,提高虚拟机的性能。 本文对比几种场景,讨论kvm的性能优化方案。 本分方案中,host和guest都使用Linux4.4。相比更早的Linux版本,Linux4.4的虚拟化更加完善。如果有不了解的朋友,可以了解一下apicv技术,和相关的posted-interrupt和PV-EOI。 本文中,工具使用systemtap,获取到vm exit的reason和次数。 分析: 1,网卡虚拟化 初始条件: a,为了避免外部中断带来的干扰,把物理网卡的中断绑定到物理机的CPU0上,让虚拟机跑在物理机的CPU2和CPU3上。 b,虚拟机内部启动nginx,在虚拟机外部使用webbench模拟网络流量。 对比三种情况,模拟网卡e1000,virtio-net,virtio-net vhost三种方式下的数据: a,e1000纯虚拟网卡下,数据稳定后,截取其中一秒钟的数据: ------------------------------------------------------------- [ EXIT_REASON_EXTERNAL_INTERRUPT] [01] count = 2198 [ EXIT_REASON_PENDING_INTERRUPT] [07] count = 339 [ EXIT_REASON_CPUID] [10] count = 18 [ EXIT_REASON_HLT] [12] count = 9617 [ EXIT_REASON_IO_INSTRUCTION] [30] count = 6763 [ EXIT_REASON_MSR_READ] [31] count = 6763 [ EXIT_REASON_MSR_WRITE] [32] count = 13325 [ EXIT_REASON_PAUSE_INSTRUCTION] [40] count = 2056 [ EXIT_REASON_EPT_VIOLATION] [48] count = 45 [ EXIT_REASON_EPT_MISCONFIG] [49] count = 61918 对于e1000网卡来说,最大量的vm exit原因是 EXIT_REASON_EPT_MISCONFIG,也就是guest使用mmio访问虚拟网卡设备的时候产生的。 b,virtio-net虚拟网卡下,数据稳定后,截取其中一秒钟的数据: ------------------------------------------------------------- [ EXIT_REASON_EXTERNAL_INTERRUPT] [01] count = 2118 [ EXIT_REASON_PENDING_INTERRUPT] [07] count = 558 [ EXIT_REASON_CPUID] [10] count = 18 [ EXIT_REASON_HLT] [12] count = 5988 [ EXIT_REASON_MSR_WRITE] [32] count = 5165 [ EXIT_REASON_PAUSE_INSTRUCTION] [40] count = 843 [ EXIT_REASON_EPT_VIOLATION] [48] count = 45 [ EXIT_REASON_EPT_MISCONFIG] [49] count = 6742 对于virtio-net网卡来说,因为主要的网卡数据放到了virtio vring buf中,不需要使用大量的端口模拟, 相对来说量不是很大的EXIT_REASON_EPT_MISCONFIG用来做virtio kick host。 c,virtio-net vhost虚拟网卡下,数据稳定后,截取其中一秒钟的数据: ------------------------------------------------------------- [ EXIT_REASON_EXTERNAL_INTERRUPT] [01] count = 2428 [ EXIT_REASON_PENDING_INTERRUPT] [07] count = 853 [ EXIT_REASON_CPUID] [10] count = 18 [ EXIT_REASON_HLT] [12] count = 7087 [ EXIT_REASON_MSR_WRITE] [32] count = 6464 [ EXIT_REASON_PAUSE_INSTRUCTION] [40] count = 44 [ EXIT_REASON_EPT_MISCONFIG] [49] count = 21996 对于virtio-net vhost来说,性能比virtio-net更上了一个台阶,EXIT_REASON_EPT_MISCONFIG用来做virtio kick host的次数比virtio-net更加高。 几种虚拟网卡的原理,作者分析过《[linux][network]虚拟网卡技术分析》一文中有提到。这里的数据也再一次证实了虚拟网卡的性能:virtio-net vhost > virtio-net > e1000。 如果不是网卡直通的场景下,virtio-net vhost 是最优选择。guest linux对virtio支持很好,guest windows中需要安装virtio-net驱动。 2,虚拟磁盘 对比纯虚拟设备ide设备,分配了一块hda盘;以及一块virtio-blk设备,即vda盘。在guest中使用iozone测试directIO 8k大小。 a,hda纯虚拟磁盘下,数据稳定后,截取其中一秒钟的数据: ------------------------------------------------------------- [ EXIT_REASON_EXTERNAL_INTERRUPT] [01] count = 1360 [ EXIT_REASON_PENDING_INTERRUPT] [07] count = 1047 [ EXIT_REASON_HLT] [12] count = 8716 [ EXIT_REASON_IO_INSTRUCTION] [30] count = 163237 虚拟ide设备,需要使用pio来模拟设备读写,大量的 EXIT_REASON_IO_INSTRUCTION也是意料之中。 b,virtio-blk下,数据稳定后,截取其中一秒钟的数据: ------------------------------------------------------------- [ EXIT_REASON_EXTERNAL_INTERRUPT] [01] count = 961 [ EXIT_REASON_PENDING_INTERRUPT] [07] count = 266 [ EXIT_REASON_HLT] [12] count = 5343 [ EXIT_REASON_MSR_WRITE] [32] count = 8582 [ EXIT_REASON_EPT_MISCONFIG] [49] count = 8573 virtio-blk的表现更好一些。数据都是使用virtio vring buf来传输的, EXIT_REASON_EPT_MISCONFIG是guest用来kick host用的。 实际表现来说,virtio-blk也是比ide好很多。在大块连续数据的读写上,virtio-blk接近物理硬盘的最大速度,而ide大约只有50MB/s的样子(当然和硬件具体型号也有关)。 使用virtio-blk不但速度更快,也更节省CPU,降低那么多的vm exit,以及实际的top来看也是如此。 3,external interrupt 产生的原因比较多。但是基本就是网卡,磁盘,timer,LOC,RES。下面逐个分析: 网卡&磁盘:虚拟机的vCPU1正在物理机的CPU3执行,这时候,物理机网卡eth0收到网络报文,如果中断请求刚好发送给了物理机的CPU3,那么虚拟机的vCPU1就需要vm exit。这种情况,配置irq affinity,让网卡的irq发送到物理机的CPU0上,就可以避免这种external interrupt引起的vm exit。对于磁盘,尝试pin住qemu的iothread到其他CPU上,hdd的效果不明显,毕竟企业级的hdd的iops也不过200多;ssd则不然。 Timer:在Host上,Linux在启动阶段,如果发现了apic timer,会优先使用精度更高的apic timer,并关闭掉i8254,也就是PIT。这个对虚拟机的vm exit没有影响。 LOC:有兴趣的朋友可以尝试一下,如果虚拟机空载的时候,LOC产生的并不会很多,可是在虚拟机里面运行stress来压CPU,就会看到差不多每秒500次左右。这个原因是:vCPU在Host是一个task,在Host上的CFS调度上,周期性产生irq,在处理irq的时候,就会从CFS中选择vRuntime最少的task来执行。如果在guest空载的情况下,no_hz默认的行为是no_hz_idle(Ubuntu发行版的默认参数),就会减少timer的irq。那么有没有办法解决这个?可以考虑使用CONFIG_NO_HZ_FULL选项来编译kernel,配置对应的参数。 RES:Rescheduling interrupts,一般是调度系统使用,互相之间发送IPI。res很高的时候,看到vmstat的cs也很高,每一次的vm exit之后,都会调用到native_smp_send_reschedule(这个函数会触发IPI发送)。作者没有想到什么好的办法来避免RES升高,只能通过其他的vm exit减少来减少触发。 作者抓到了大量的backtrace: native_smp_send_reschedule 0x0 [kernel] kvm_vcpu_kick 0x85 [kvm] __apic_accept_irq 0x338 [kvm] kvm_irq_delivery_to_apic_fast 0xfe [kvm] kvm_irq_delivery_to_apic 0x65 [kvm] apic_reg_write 0x11f [kvm] kvm_x2apic_msr_write 0x54 [kvm] kvm_set_msr_common 0x51c [kvm] vmx_set_msr 0xb2 [kvm_intel] kvm_set_msr 0x41 [kvm] handle_wrmsr 0x58 [kvm_intel] vmx_handle_exit 0x1d9 [kvm_intel] kvm_arch_vcpu_ioctl_run 0x4c3 [kvm] kvm_vcpu_ioctl 0x33d [kvm] do_vfs_ioctl 0x298 [kernel] sys_ioctl 0x79 [kernel] 4,hlt hlt是因为guest内部CPU使用率上有剩余,执行idle。就会调用hlt指令,让自己进入省电的状态。 初始条件,在一个guest空载的Linux4.4。 在默认情况下: ------------------------------------------------------------- [ EXIT_REASON_HLT] [12] count = 32 [ EXIT_REASON_MSR_WRITE] [32] count = 90 [ EXIT_REASON_EPT_VIOLATION] [48] count = 45 但是在上文的数据中,看到hlt明显更多。因为guest的内部CPU没有吃满。比如说,外部来了一个网络请求,就会唤醒guest执行处理,guest回包完,就执行idle。这样就会产生大量的 EXIT_REASON_HLT。 有没有办法消除掉hlt引起的vm exit呢? 答案是有,而且有两种! 其一,在guest linux4.4的启动参数中增加idle=poll。这一样的意思就是告诉kernel在执行idle的时候,不再执行hlt,而是执行poll。另外,网上也有文章说在启动参数中增加no-hlt。作者尝试无效后,查看kernel的代码,发现在commit 27be457000211a6903968dfce06d5f73f051a217中(2013年的时候),移除掉了no-hlt能力。再后来,发现了idle=poll。使用idle=poll的效果就是host上真的不会看到vm exit了,但是side effect就是host上看到guest的CPU一直是吃满的。 其二,修改host的kvm。修改kvm的vmx代码,让guest在执行hlt的不退出,效果就是host上真的不会看到vm exit了,但是side effect就是host上看到guest的CPU一直是吃满的。 基于以上的情况,到底hlt有没有必要优化? 作者以为:对于绝大多数情况,都没有必要的。如果guest真的调用了hlt,就说明它真的有空闲利用率。但是,对于并发度极高的网络io类型的高性能服务器,在pin住CPU的情况下,可以考虑尝试一下,当然还要看具体的数据。

5,ple & pause 在guest内部执行pause指令,可以让vm exit。不过逻辑上有些复杂,下面一段选自intel开发文档: • PAUSE. The behavior of each of this instruction depends on CPL and the settings of the “PAUSE exiting” and “PAUSE-loop exiting” VM-execution controls: — CPL= 0. • If the “PAUSE exiting” and “PAUSE-loop exiting” VM-execution controls are both 0, the PAUSE instruction executes normally. • If the “PAUSE exiting” VM-execution control is 1, the PAUSE instruction causes a VM exit (the “PAUSE loop exiting” VM-execution control is ignored if CPL = 0 and the “PAUSE exiting” VM-execution control is 1). • If the “PAUSE exiting” VM-execution control is 0 and the “PAUSE-loop exiting” VM-execution control is 1, the following treatment applies. The processor determines the amount of time between this execution of PAUSE and the previous execution of PAUSE at CPL 0. If this amount of time exceeds the value of the VM-execution control field PLE_Gap, the processor considers this execution to be the first execution of PAUSE in a loop. (It also does so for the first execution of PAUSE at CPL 0 after VM entry.) Otherwise, the processor determines the amount of time since the most recent execution of PAUSE that was considered to be the first in a loop. If this amount of time exceeds the value of the VM-execution control field PLE_Window, a VM exit occurs. 在Linux4.4上,默认情况下,CPU_BASED_PAUSE_EXITING是没有被设置起来的,也就是说:不会因为guest里面执行了一次pause就发生vm exit。作者尝试过添加这个flag,vm exit的量相当大。 SECONDARY_EXEC_PAUSE_LOOP_EXITING一般是启用的,由于pause loop触发的vm exit数量和具体的逻辑有关。作者以为,如果是CPU独享的高性能情况下,可以考虑禁用:禁用方法可以在加载kmod的时候,增加参数即可(强制修改kmod当然也是可以的):insmod kvm-intel.ko ple_gap=0;如果是CPU超分配的情况下,使用默认参数就好,在非CPU密集型的计算中,作者用脚本抓到的双vCPU数据也就是每秒几十次的样子。 5,msr 上文中,可以看到很多wrmsr/rdmsr产生vm exit。作者抓到一组数据,用来分析一下原因: [ MSR_IA32_TSC_DEADLINE] [0x6e0] count = 346 [ ] [0x830] count = 23313 [ ] [0x80b] count = 2134 0x6e0是MSR_IA32_TSC_DEADLINE,用来设置lapic的next deadline,每秒钟的差不多三四百次。 0x830 和0x80b 都是APIC_BASE_MSR~APIC_BASE_MSR 0x3ff范围内的数据。其中: 0x80b 是IA32_X2APIC_EOI x2APIC EOI Register,那么可以使用pv eoi来优化。在qemu的启动参数中增加“ kvm_pv_eoi”,例如:-cpu host, kvm_pv_eoi。或者libvirt的xml中配置: <features> <apic eoi='on'/> </features> 0x830是IA32_X2APIC_EOI x2APIC EOI Register,在使用pv eoi之后,作者观察到wrmsr 0x830触发的vm exit也没有太多了。

0 人点赞