云计算之路-阿里云上:“黑色1秒”问题与2009年Xen一个补丁的故事

在之前对“黑色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日志

xen git日志

2009年3月31日,Keir Fraser向Xen代码库提交了Yu Ke完成的补丁代码。在这个提交中,只字未提坑的事。

这个坑究竟有没有被填平呢?照之前Keir Fraser的性格,应该不会有让有坑的代码提交上去。所以,我们更关注的是这个坑究竟是怎么被填平的?

于是,我们又回到Xen-devel mailing list中寻找。。。

终于找到了:[Xen-devel] [PATCH] cpuidle: suspend/resume scheduler tick timer during cpu idle entry/exit V2 - msg#01762

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()先执行。

xen sched_tick_suspend

而对于我们遇到的“黑色1秒”问题,只要阿里云从内心承认是Xen的问题,我们就觉得出人意料了!

不管怎么样,这次是黑色n秒问题最重要的一次突破,同时也让我们遇到了千载难逢的学习Xen的好机会!

posted @ 2014-05-15 15:42  博客园团队  阅读(4727)  评论(27编辑  收藏  举报