在之前對“黑色1秒”問題的分析博文中,我們將最大嫌疑對象鎖定在了Xen,在這篇博文我們將從Xen的角度進行分析。也許有人會問,為什么不知道天多高地多厚地去研究不屬於自己范圍的問題?只因我們對一個問題的強烈好奇心——究竟是不是我們用Windows的錯?
(注1:文中所說的Xen補丁問題只是提供一種分析問題的思路,我們遇到的“黑色1秒”問題與有沒有打這個補丁沒有關系)
(注2:關於這個Xen補丁背后的故事,推薦閱讀阿里雲分享的博文:雲計算之路:2009年Xen一個補丁背后那不為人知的故事)
2009年3月20日,來自Intel的Yu Ke通過Xen-dev Mailing List給來自Citrix的Keir Fraser(負責的Xen開發者之一)發了一封郵件,提交了Xen的一個patch——cpuidle: suspend/resume scheduler tick timer during cpu idle entry/exit.
這個補丁的用途是什么呢?
cpuidle can collaborate with scheduler to reduce unnecessary timer interrupt. For example, credit scheduler accounting timer doesn't need to be active at
idle time, so it can be stopped at cpuidle entry and resumed at cpuidle exit. This patch implements this function by adding two ops in scheduler:
tick_suspend/tick_resume, and implement them for credit scheduler.
上面的文字是郵件中的第一段內容,大意是:Xen中的cpuidle(在物理CPU沒有被給分配VCPU時,cpuidle可以讓物理CPU進入C-state,好處之一是省電,詳見這里)可以與CPU調度器(負責CPU的調度,關於Xen中的CPU調度,詳見這里)一起協作從而減少不必要的時鍾中斷。比如,當CPU空閑時,credit scheduler(Xen中的一種CPU調度算法,詳見這里)的accounting timer就沒有繼續工作的必要,所以可以讓它在cpuidle時停止工作,在退出cpuidle時再喚醒它。這個patch通過添加tick_suspend/tick_resum這兩個調度器操作來實現這個。
簡而言之,這個補丁的用途是——在CPU空閑時,讓調度器也休息;當CPU工作時,再把調度器喚醒。
應該這個補丁會帶來什么好處呢?
With this patch, under idle scenario, timer interrupt frequency decreased from ~100HZ to ~10HZ, and average C state residency increase from ~10ms to larger than 100ms. Also in a two-socket machine, about 4% idle power saving is observed.
上面的文字是文字是郵件中的第二段內容,大意是:用了這個補丁,在CPU空閑場景下,時鍾中斷頻率由~100HZ減少到~10HZ,平均的C狀態(CPU的電源管理狀態,詳見這里)停留時間從10ms增加到100ms(看到這讓我們想到“黑色0.1秒”)。在有2顆CPU的機器上,從觀察情況看,可以節省4%的電量。
簡單言之,這個補丁的好處是——省電。
這個補丁的臨床試驗情況怎么樣?
However, one issue is observed with this patch, i.e. there is soft-lockup in dom0 occasionally. This issue is still under debugging. Currently we already find a >1s VCPUOP_set_singleshot_timer timeout, which imply this may be a dom0 issue. we are working hard to figure the root cause.
上面的文字是文字是郵件中的第三段內容,大意是:應用這個補丁后,觀察到一個症狀。在dom0(Xen中的特權虛擬機,“內部包含了真實的設備驅動,可直接訪問物理硬件”)中偶爾會出現軟死鎖(soft-lockup),而且發現大於1秒的VCPUOP_set_singleshot_timer(詳見Xen的domain.c代碼)超時的情況,這暗示了dom0中可能藏着某個坑。
注意!這里的1秒!這個“1秒”與“黑色1秒”中的“1秒”,是偶然的巧合,還是本是同根生?這個地方是一個非常重要的線索,讓我們看到了走出“黑色1秒”最有希望的一線光明。
繼續看這封郵件中接下來的內容:
Considering the very visible effect of this patch, and the issue mentioned above only occurs when cpuidle is enabled, and has no impact to normal user, we
finally decide to send out this patch to see if it is possible for 3.4 inclusion. In the bug fix phase, we will send out bug fix for the issue.
大意是:考慮到這個補丁誘人的省電效果(省電就是省錢啊),而且上面提到的問題(疑似黑色1秒)只在啟用cpuidle的情況下發生,不會影響到正常普通用戶(這地方有點疑惑,什么才是普通用戶?),最終決定發出這個補丁(似乎缺少了一點不放過任何一個問題的偏執)。
接下來我們看看負責向Xen提交代碼的Keir Fraser的回復:
I don't really want to take the patch while it is soft locking up. I would expect linux-2.6.18-xen.hg:22 to avoid lockup warnings due to too long
singleshot timeouts (I assume you are testing with the 2.6.18 tree?).
Personally I would rather have cpuidle be enabled by default (or even always with no disable option) and get existing Cx benefits for everyone, rather
than have a slightly broken cpuidle option.
Is there a reason not to turn on cpuidle by default now? Or even enable and
then remove the boot option?
還好Keir Fraser不願放過這個小問題,他希望解決軟死鎖問題之后再發布這個補丁,而且他希望默認開啟cpuidle。這里可以看出Keir Fraser堅持的原因,如果默認開啟cpuidle,這個問題的影響就會很大。
再看一下Yu Ke緊接着的回復:
Right, I am testing it with 2.6.18 tree. I am also looking into the dom0 code, to see if should change dom0.
There is no obvious obstacle to turn on cpuidle by default. According to our testing and measurement, cpuidle is pretty stable now, maybe it is time to enable it by default.
Keir Fraser的堅持打動了Yu Ke,決定尋找並填掉這個坑,並且他也希望默認開啟cpuidle。
那Yu Ke究竟有沒有找到這個坑,有沒有填平這個坑?
去哪里尋找答案?我們想到了Xen的git日志:
2009年3月31日,Keir Fraser向Xen代碼庫提交了Yu Ke完成的補丁代碼。在這個提交中,只字未提坑的事。
這個坑究竟有沒有被填平呢?照之前Keir Fraser的性格,應該不會有讓有坑的代碼提交上去。所以,我們更關注的是這個坑究竟是怎么被填平的?
於是,我們又回到Xen-devel mailing list中尋找。。。
2009年3月26日Yu Ke向Keir Fraser發了一封郵件並提交了填好坑的代碼:
Hi Keir,
This attached is the version 2 of the patch. The major update is fixing the soft-lockup issue.
The root cause is: sched_tick_suspend will call __stop_timer and may raise TIMER_SOFTIRQ if the timer deadline is changed. In this case, the assumption of
no softirq pending in acpi_processor_idle is broken, and later the hpet broadcast wakeup IPI will not be delivered to this CPU, since its softirq pending bit is set. Then the CPU will be sleeping until random external interrupt happen. To fix this issue, the sched_tick_suspend is moved before the softirq pending bit checking, to keep the assumption correct.I also measure the performance by SPECJbb in dom0, no performance degradation observed.
Best Regards
Ke
原來Yu Ke遇到的“黑色1秒”問題的原因是這樣的:
前面我們說過這個補丁的用途是在CPU空閑時,讓調度器也休息;當CPU工作時,再把調度器喚醒。讓調度器休息調用的是sched_tick_suspend(),而sched_tick_suspend則會調用_stop_timer(),如果這時時鍾(Timer)的時間戳被改變,會觸發TIMER_SOFTIRQ(時鍾軟中斷,欲了解軟中斷的知識,詳見這里),而坑就是這個軟中斷造成的。
開始的補丁代碼是這樣的:
if ( softirq_pending(smp_processor_id()) ) do_softirq(); sched_tick_suspend(); //...
開始的softirq_pending代碼就是為了處理軟中斷,之后的代碼就認為不會再出現軟中斷,可是在執行sched_tick_suspend()時在某種條件下(比如時鍾時間戳被改變)會觸發軟中斷,造成softirq pending bit被設置了值;當CPU由空閑狀態進入工作狀態時(比如某個線程被調度到該CPU執行),系統會發出hpet(High Precision Event Timer)廣播喚醒IPI(Inter-processor interrupt)從而喚醒CPU,但由於當時處於softirq pending狀態,hpet到達不了這個CPU,於是CPU沒有被喚醒,繼續悠閑着。而那個需要被執行的線程只能傻傻地等着,直到CPU被外部的中斷喚醒。
打個比方來說明一下CPU的這種狀態。你中午吃過飯閑着沒事想睡會覺,於是在iPad定了一下鬧鍾就開始睡覺;但是一個同事在你睡覺的時候把iPad設置為了靜音,結果鬧鍾准時工作了,你卻聽不到,繼續做着美夢直到有人叫你或打你手機,你才開始干活。
由此,我們聯想到,我們遭遇“黑色1秒”問題時,CPU很可能處於同一種狀況。HTTP.SYS的一個線程被調度到一顆空閑的CPU核,系統給美夢中的CPU發了一個通知——“快起床,開始快活啦!”,結果某種原因造成CPU沒收到,繼續美夢。而HTTP.SYS的線程只能傻等着,直到1秒后,有人直接去敲CPU的門把CPU叫醒,於是CPU開始忙活起來。
這時你也許會問,如果真是這樣的話,那只會造成當時線程卡住。為什么“黑色1秒”期間,所有的HTTP.SYS線程都會卡住?那是因為HTTP.SYS不是一般人,它運行於Windows的內核模式,我們猜測HTTP.SYS很有可能用到spinlock,所以只要一個線程卡住,所有線程都無法干活(猜測依據的是我們在曾經遭遇的“黑色10秒”期間寫的一篇博文,其中有一句話:“SpinLock是在Windows內核級別使用了”)。而如果是用戶模式的線程(比如ASP.NET線程),就不會這樣。從昨天進一步的監測數據看,在“黑色1秒”期間有時有些ASP.NET線程會卡住,但不會全部卡住。
在之前的分析中,我們有這樣的猜想:
黑色10秒,黑色30秒,黑色1秒,黑色5秒。。。就叫黑色n秒吧。不管黑色多少秒,這些都只是問題的表象,而真正的黑色在虛擬機層面,更准確地說就是Xen。
而這一次,我們要作出更更准確的猜想——黑色n秒的問題很可能出在Xen的CPU調度環節。
2009年Xen那個的補丁引發的“黑色1秒”問題的解決方法出人意料地簡單——只要把softirq_pending與sched_tick_suspend()的代碼調換一下位置,讓sched_tick_suspend()先執行。
而對於我們遇到的“黑色1秒”問題,只要阿里雲從內心承認是Xen的問題,我們就覺得出人意料了!
不管怎么樣,這次是黑色n秒問題最重要的一次突破,同時也讓我們遇到了千載難逢的學習Xen的好機會!