前言
随着云计算技术与服务的发展和进步,愈来愈多的客户选择将业务部署到云端。但因为引入了虚拟化层,在业务部署过程当中常常会遇到IO问题,一般也不易调试。本文主要介绍利用perf、systemtap等工具,帮助一位托管云客户调试IO性能问题,来分析虚拟环境下Windows IO的性能。前端
问题出现
有一次,托管云客户本身搭建了虚拟化环境,在同一台宿主机上建立windows 2008 R2 和 Centos6.5虚拟机,用fio分别测试其随机读性能,windows 2008 R2的IOPS大约在18K,而Linux的IOPS却能够达到100K左右。
• 客户测试用的fio 配置
[global]
ioengine=windowsaio
direct=1
iodepth=64
thread=1
size=20g
numjobs=1
[4k]
bs=4k
filename=d:test.img
rw=randread
测试结果windows
win_fio1
• 云主机IO栈api
io stack
云主机环境下,整个IO栈相对较长,涉及到Guest OS中的应用层/文件系统/Block层以及驱动层,虚拟化层,宿主机OS文件系统/Block层以及驱动层。由于涉及面多,因此其中任何一个环节出现问题都会形成性能降低,也为作IO的Tracing增长了难度。浏览器
从此次获得的信息来看,首先排除了宿主机文件系统和Block层以及驱动层的问题,由于一样状况的配置,Linux系统并无问题。
因此目前主要集中于两点
•Guest OS(Windows系统)
•fio程序
•文件系统/Block layer
•VirtIO Block驱动 虚拟机为Guest OS提供的是Virtio Block设备
•QEMU工具
如何排除QEMU的嫌疑?
对于IOPS的性能问题,很容易想到两种可能性:
•IO延时太高
•设备支持IO队列过短性能
在队列的问题方面,Linux和Windows虚拟机对应的Virtio Block设备都是同样的,那么就须要确认延时问题。测试
QEMU 完成Block IO花了多长时间?
幸运的是,Stefan Hajnoczi已经为QEMU添加了Tracing的特性,所以能够很方便的统计出QEMU从接收到一个IO请求到完成所用的具体时长。优化
从上述统计来看,平均IO完成时间在130us,由此暂时排除QEMU 层形成过高延时的影响。另外,若是关注这种动态Tracing的overhead,从测试观察上大体接近20%。
排除队列和延时问题,可能形成影响的也只有Guest OS了。
•VirtIO Block驱动的问题?
至少更新到最新稳定版本的Virtio-Win驱动,仍然存在一样的问题。
•Windows 文件系统/Block层的问题?
原生Windows系统在确认后并无作任何配置上的修改。
fio测试程序的问题网站
为何Linux上fio没有问题呢?ui
两种可能性
在性能排查过程当中,老是很容易陷入死局,常常会问究竟是哪儿出了问题?所以一切可能影响的因素彷佛都没有作任何变更。从经验来看,大部分性能问题均可以分红两种可能:
•on cpu
•off cpu
从新来看这个问题 ,在基本排除IO延时问题后,对应的问题还有两种可能性:
•CPU极其忙碌,可是大部分时间并非在作IO处理;
•CPU常常处于空闲状态,那相应的也没有主要在处理IO。
注:之因此说到目前为止并不能排除IO延时的影响,是由于只排除了QEMU Block层可能的影响,可是还有Guest OS(此次暂时忽略Guest OS)。
先看测试过程当中,虚拟机的CPU消耗状况。
top -H -p 36256
win_fio1
从上图来看,QEMU主线程的cpu负载已经达到90%以上,彷佛符合on cpu类问题。一般来讲,解决这类问题最好的办法就是用perf进程采样,而后生成火焰图,由于首先查看CPU具体消耗在什么地方是一个不错的选择。
perf record -a -g -p 36256 sleep 20
生成火焰图:
win2008-bad
能够清楚的看到,cpu大部分消耗都是KVM的操做,其中最主要的消耗是vmx_handle_exit。(真实的火焰图是一个矢量图,用浏览器查看很容易确认)。这里引发vmx_handle_exit主要有两点:
•访问IO Port(handle_pio)
•访问 MMIO(handle_apic_access)
既然KVM模块占了大部分,那就更但愿了解测试时KVM的真实行为,经过另外一个工具(kvm_stat)能够达到。
kvm_pio
除VM Entry和VM Exit事件外,最高的就是kvm_pio和 kvm_mmio,说明Windows确实有大量IO Port和MMIO操做,这也验证了在火焰图上所得出的结论。
在虚拟化里,IO Port或者MMIO均可能引发VM Exit,甚至是Heavy Exit。若是须要改善性能,通常都会尽可能避免这种状况,至少避免Heavy Exit.
•具体访问哪些IO Port和MMIO致使的VM Exit?
对于这个问题,KVM模块已经加了不少trace event,上面的kvm_stat也是利用这些trace event,只是并无把具体trace event信息打印出来。为了获取trace-event的信息,有不少前端工具,如trace-cmd、perf,都是不错的选择。
• 查看全部kvm模块的trace event
[xs3c@devhost1 ]# trace-cmd list -e | grep kvm
kvmmmu:kvm_mmu_pagetable_walk
kvmmmu:kvm_mmu_paging_element
kvmmmu:kvm_mmu_set_accessed_bit
kvmmmu:kvm_mmu_set_dirty_bit
kvmmmu:kvm_mmu_walker_error
kvmmmu:kvm_mmu_get_page
kvmmmu:kvm_mmu_sync_page
kvmmmu:kvm_mmu_unsync_page
kvmmmu:kvm_mmu_zap_page
kvm:kvm_entry
kvm:kvm_hypercall
kvm:kvm_pio
kvm:kvm_cpuid
kvm:kvm_apic
kvm:kvm_exit
kvm:kvm_inj_virq
kvm:kvm_inj_exception
kvm:kvm_page_fault
kvm:kvm_msr
kvm:kvm_cr
kvm:kvm_pic_set_irq
kvm:kvm_apic_ipi
kvm:kvm_apic_accept_irq
kvm:kvm_eoi
kvm:kvm_pv_eoi
kvm:kvm_write_tsc_offset
kvm:kvm_ple_window
kvm:kvm_vcpu_wakeup
kvm:kvm_set_irq
kvm:kvm_ioapic_set_irq
kvm:kvm_ioapic_delayed_eoi_inj
kvm:kvm_msi_set_irq
kvm:kvm_ack_irq
kvm:kvm_mmio
KVM模块添加了许多trace event的点,这里只抓起其中两个——kvm:kvm_pio和kvm:kvm_mmio。
trace-cmd-pio-mmio
经过统计发现主要访问的:
•IO Port是0x608和0xc050;
•MMIO是0xFEE003xx
经由qemu info mtree命令,能够查看IO Port 60八、c050以及FEE003xx分别对应的具体设备。
•IO Port
0000000000000608-000000000000060b (prio 0, RW): acpi-tmr 000000000000c040-000000000000c07f (prio 1, RW): virtio-pci
•MMIO
00000000fee00000-00000000feefffff (prio 4096, RW): icc-apic-container
c050能够忽略,这个被Virtio Block来作VM Exit。
到目前为止,能够判断出wnidows大量读取ACPI Power Manager Timer以及访问APIC寄存器,进而致使过多vm exit产生,消耗大量CPU资源,所以就能够具体讨论两个问题:
1.如何减小读取ACPI PM Timer寄存器而引发的VM Exit;
2.如何减小访问APIC MMIO致使的VM Exit。
如何减小读取ACPI PM Timer而引发的VM Exit?
从虚拟化层优化的思路来讲,减小IO Port引起的VM Exit一般会考虑是否能够利用Paravirtulization替换Full-virtualization 以达到目的,来看Windows在这方面是如何作的。
从Windows 7开始,微软为了使Windows 操做系统可以在HyperV获得更好性能,特地为Windows系统作了不少虚拟化方面的加强工做,其中就包括这里能够利用到的HyperV Timer,这个特性相似于Linux中的kvmclock。
从当前的支持状况来看:
•Windows 7
•Windows 7 SP1
•Windows Server 2008 R2
•Windows Server 2008 R2 SP1/SP2
•Windows 8/8.1/10
•Windows Server 2012
•Windows Server 2012 R2
这些Windows系统都包含虚拟化加强功能,更多的信息在微软官方网站。
2014年,RedHat工程师Vadim Rozenfeld和Peter Krempa 分别为qemu和libvirt添加了HyperV Timer的支持,因此能够直接经过libvirt使能HyperV Timer。
<clock …>
<timer name=’hypervclock’ present=’yes’/>
</clock>
另外,KVM里很早也支持了HyperV Timer,只是客户的宿主机内核版本并不支持该功能,因此须要为客户升级UCloud本身维护的内核版本。
•如何减小APIC ACCESS而引发 VM Exit?
Intel CPU也已经支持apic-v,一样升级到UCloud本身维护的内核版原本解决。
最终效果
win-fio-good
win-good
总结 从这个案例能够看出,跟物理环境相比,在虚拟化环境下,Windows IO性能较差时,并不必定真正是IO路径出现问题,多是一些虚拟化性能的问题对IO性能形成了很大影响。