因为给Surface Pro 3做button driver的移植,遇到了各种问题。3月份的时候这个driver已经work了,然后太忙,就没来得及发正式patch。于是到五月份的时候,又准备把patch发出去了。结果bugzilla上的兄弟说在最新内核上,这个patch完全不work。我傻眼了,早知道当时能work的时候就应该发出去了,现在又得从头定位,不得不进行bisect来找regression。这个bisect是很耗时的好吗!
于是我在自己机器上试了一下这个patch,发现主要是touch screen没反应,其他的button还好。
结果我耗了好几天,找到了regression,先是这个patch引入的:
commit d32932d02e1869be838cea3ace42467c360db377
Author: Jiang Liu <[email protected]>
Date: Mon Apr 13 14:11:59 2015 +0800
x86/irq: Convert IOAPIC to use hierarchical irqdomain interfaces
bisect shows this one breaks the sp3 driver, need to take a look at why it break touchscreen driver.
这个patch把我的i2c controller的中断搞没了。于是和这位兄弟联系上,他就正好远控我的机器,找出了bug
的root cause(其实我发现他是根本没有进行调试,直接就通过一堆命令在故障机器上,就定位出了问题,太强了)
修复的patch在这里,应该最近会收进内核:
http://www.gossamer-threads.com/lists/linux/kernel/2227028
这个patch倒是把i2c的中断弄回来了,而且打上这个patch后的那个错误commit版本,touch screen可以工作了。不过当我把
内核更新到4.2-rc4后,touchscreen又不工作了。于是我只要又做bisect,发现错误commit 2是:
commit 20dacb71ad283b9506ee7e01286a424999fb8309
ACPI / PM: Rework device power management to follow ACPI 6
于是花了一个周末分析他的这个补丁,原来他主要做了三件事,都是在changelog里描述的:
1)device的D3hot状态任意条件下有效(修改前D3cold的角色),并且只有当系统提供了_PS3接口时,我们才认为D3cold有效(对,这个就是最新ACPI spec6里规定的)
2)device的状态只能从高耗能到低耗能状态,除非你是想从低耗能切换到最高的耗能D0状态;
3)先设置device本身的耗能状态(_PSx),再设置device拥有的power resource的状态(以前是反着来)
这么看,我们还看不出什么问题是吧。但我敏锐的猜测到这个故障一定和power resource的打开和关闭有联系,
于是我打开acpi debug 和 device_pm.c的dynamic_debug(这个patch改动的文件)然后检查dmesg 启动信息,发现了可疑的一处打印:
有一个i2c总线下的设备,当他尝试从D3hot切换到D0时,他竟然把自己的power resource关闭了。也就是说,
当一个设备说,我要启动了,然后他却把自己的电源关闭了,然后报告给用户说,我启动好了。
仔细检查代码,发现故障的情景应该是这样:
1) device尝试从D0切换到D3cold,最终的效果是device的状态设置为D3hot,然后自己的power resource全部关闭,此时power resource的refcount为0;
2)接着由于runtime pm的缘故,驱动又把device尝试从D3hot提升到D0运行态,结果在acpi_power_transition里,
由于有如下判断代码:
* * First we reference all power resources required in the target list * (e.g. so the device doesn't lose power while transitioning). Then, * we dereference all power resources used in the current list. */ if (state < ACPI_STATE_D3_COLD) result = acpi_power_on_list( &device->power.states[state].resources); if (!result && device->power.state < ACPI_STATE_D3_COLD) acpi_power_off_list( &device->power.states[device->power.state].resources);于是这段代码首先把D0对应的power resource先打开,再把D3hot对应的power resource关闭,由于
在Surface Pro 3上,D0和D3hot对应的power resource是同一个,于是等于这个power resource没有开启,
但最终device的状态却设置为了开启D0状态。
可以看出,问题的根源在于,D3hot对应的power resource状态,是不确定的,有可能是开启(用户要求从
D0切换到D3hot,最终power resource的refcount为1),也有可能是关闭(用户要求从D0直接切换到D3cold,最终
power resource的refcount为0),于是上面代码acpi_power_off_list有可能关闭处于D3hot的power resource(refcount为0),
也可能不去关闭(refcount为1),那么前者就会引起我们描述的故障现象。解决方案我写了一个patch,
就是发现如果处于D3hot状态的device已经将他的power resource关闭了,那么就不要再去关一次。这样device
切换到D0后,他的电源就是打开的,也就解决了我们这个问题。
于是我把patch发出来组内讨论,结果他们告诉我,在我发patch的前两天,刚有一个解决方案进upstream了:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/acpi/device_pm.c?id=71b65445f0ed04c2afe3660f829779fddb2890c1
恩好吧。又慢了一步。
不过我的surface pro 3的button driver倒是可以正常工作了,希望能尽快被收进去。
https://bugzilla.kernel.org/show_bug.cgi?id=84651