前言
隨着雲計算技術與服務的發展和進步,愈來愈多的客戶選擇將業務部署到雲端。但因爲引入了虛擬化層,在業務部署過程當中常常會遇到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性能形成了很大影響。