processor core不停地进出C4 idle state问题的解决
问题背景描述
package中有4个core,在core0/2/3上执行dead loop程序,控制它们不会idle,core1上不做任何操作。这样core1发现没有事情做的时候,就会进入到idle state,但是由于package 中的其他core一直busy,因此,core1只会进入到core-level idle state,整个package不会进入到idle state。
如果没有wakeup source将core1唤醒的话,按照ACPI spec的描述,core1应该可以一直待在idle state。但实际测试过程中,我发现core1在不停地进出C4 state。
1.查看所有可能的wakeup source
通过查看cat /proc/interrupts统计信息,发现rescheduling interrupts和local timer interrupts计数在不停地增加。
- 对于local timer interrupts,在tickless kernel中,CPU进入到C3+ idle state时,会停掉local APIC timer,让CPU在idle state待更长的时间,以节省更多的power。因此local timer interrupts只有当core1处于C0 state时才能打上来,绝不会是将CPU唤醒的wakeup source;
- 对于rescheduling interrupts,这个倒是有可能将CPU唤醒。
我在boot option中加入了isolcpus=1,不让core1参与任务调度。之后重新测试,
cat /proc/interrupts 查看rescheduling interrupts中断计数不再变化,但是core1依然在不停地进出C4 idle state。
2. 引入更多的debuginfo
在drivers/cpuidle/cpuidle.c文件,cpuidle_enter_state函数中,添加一些debug info,统计core1在C4 idle state中持续的时间。经过测试,我发现每次在C4 state中持续的时间大概是4s,4s之后退出C4,之后很快又再次进入到C4 idle state。
根据这个实验结果推测,core1上应该有一个周期大概为4s的wakeup source,而且看起来像是一个timer interrupt wakeup source。CPU在进入到C4之前,会把该timer的超期时间设置到broadcast timer中,由broadcast timer interrupt将core唤醒。
cat /proc/timer_list 查看一下各个core timer list上的事件,结果如下:
cpu: 1
clock 0:
.base: (ptrval)
.index: 0
.resolution: 10000000 nsecs
.get_time: ktime_get
active timers:
#0: < (ptrval)>, watchdog_timer_fn, S:01
expires at 24470000000-24470000000 nsecs [in 4063953389 to 4063953389
从上面的结果中不难发现在core1上有一个watchdog_timer,周期是4s。
在kernel boot option中加上nowatchdog 之后,core1就可以在C4 idle state持续比较长的时间了。
到这里,“没有wakeup source”,但core1不断进出C4的root cause就找到了。
soft watchdog 在每个core上会使用一个周期为4s的timer,使用hrtimer时钟,时间到了之后,执行watchdog_timer_fn,检查是否有soft lockup发生。watchdog使用的timer,在C0 state下是local APIC timer,当core1进入到C4 idle state(>C3)时,会停掉local APIC timer,切换到broadcast timer,在这个过程中,会把core1上active timer中最先到期的timer event设置到broadcast timer中,由broadcast timer来维护,因此,就会有一个周期为4s的“wakeup source”不停地把core1叫回到C0 state。