移动终端 2015 年 11 月 15 日

解决Linux下笔记本休眠立即唤醒的问题

这篇文章讲述了作者在使用Arch Linux的X250笔记本中遇到的一个问题,即笔记本在休眠状态下会自动唤醒,导致在包里发烫,可能会引起安全隐患。经过多次尝试,作者发现了问题,并在文章中贴出了相关日志。该问题的解决需要进一步的调试和排查,但本文提供了一个有用的线索。
我的X250上装的是Arch Linux,平时基本不关机,合上盖子自动休眠之后就塞进包里,使用时打开盖子立即j就能恢复工作状态,非常方便!最近有几次从包里拿出笔记本发现很烫,机器机器竟然一直在开机状态,不知道什么时候从休眠状态下恢复了。 因为笔记本上的锂电池受热可能着火、爆炸等,而笔记本在工作状态下会持续发热,并且在密闭的包里热量无法散出,感觉像背着个定时炸弹!因为这种情况只是偶尔出现,比较难重现,所以很久都没找到原因。 一次偶然的机会,发现笔记本在合上盖子后立即唤醒(后台放着音乐比较好识别),我反复开盖、盒盖重复了几次之后问题依然存在。终于可以轻而易举的重现故障了!先查下日志:
[ 8404.075516] PM: Syncing filesystems ... done.
[ 8404.104497] PM: Preparing system for sleep (mem)
[ 8404.105247] Freezing user space processes ... (elapsed 0.002 seconds) done.
[ 8404.107587] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
[ 8404.108854] PM: Suspending system (mem)
[ 8404.108882] Suspending console(s) (use no_console_suspend to debug)
[ 8404.236279] wlp3s0: deauthenticating from 64:09:80:19:1f:5f by local choice (Reason: 3=DEAUTH_LEAVING)
[ 8404.263918] sd 1:0:0:0: [sdb] Synchronizing SCSI cache
[ 8404.264044] sd 0:0:0:0: [sda] Synchronizing SCSI cache
[ 8404.271481] sd 0:0:0:0: [sda] Stopping disk
[ 8404.302657] e1000e: EEE TX LPI TIMER: 00000011
[ 8404.447885] sd 1:0:0:0: [sdb] Stopping disk
[ 8404.600381] PM: suspend of devices complete after 490.755 msecs
[ 8404.615527] PM: late suspend of devices complete after 15.120 msecs
[ 8404.618575] ehci-pci 0000:00:1d.0: System wakeup enabled by ACPI
[ 8404.618741] e1000e 0000:00:19.0: System wakeup enabled by ACPI
[ 8404.618749] xhci_hcd 0000:00:14.0: System wakeup enabled by ACPI
[ 8404.632644] PM: noirq suspend of devices complete after 17.093 msecs
[ 8404.633307] ACPI: Preparing to enter system sleep state S3
[ 8404.752167] ACPI : EC: EC stopped
[ 8404.752168] PM: Saving platform NVS memory
[ 8404.752178] Disabling non-boot CPUs ...
[ 8404.753618] smpboot: CPU 1 is now offline
[ 8404.756158] smpboot: CPU 2 is now offline
[ 8404.758128] smpboot: CPU 3 is now offline
[ 8404.760139] ACPI: Low-level resume complete
[ 8404.760191] ACPI : EC: EC started
[ 8404.760192] PM: Restoring platform NVS memory
[ 8404.760552] Enabling non-boot CPUs ...
[ 8404.760584] x86: Booting SMP configuration:
[ 8404.760584] smpboot: Booting Node 0 Processor 1 APIC 0x1
[ 8404.764928]  cache: parent cpu1 should not be sleeping
[ 8404.765047] CPU1 is up
[ 8404.765065] smpboot: Booting Node 0 Processor 2 APIC 0x2
[ 8404.769409]  cache: parent cpu2 should not be sleeping
[ 8404.769521] CPU2 is up
[ 8404.769542] smpboot: Booting Node 0 Processor 3 APIC 0x3
[ 8404.773916]  cache: parent cpu3 should not be sleeping
[ 8404.774084] CPU3 is up
[ 8404.777285] ACPI: Waking up from system sleep state S3
[ 8405.024300] acpi LNXPOWER:02: Turning OFF
[ 8405.047423] ehci-pci 0000:00:1d.0: System wakeup disabled by ACPI
[ 8405.048213] xhci_hcd 0000:00:14.0: System wakeup disabled by ACPI
[ 8405.049221] PM: noirq resume of devices complete after 18.244 msecs
[ 8405.051155] PM: early resume of devices complete after 1.892 msecs
[ 8405.051370] e1000e 0000:00:19.0: System wakeup disabled by ACPI
[ 8405.052923] rtc_cmos 00:02: System wakeup disabled by ACPI
[ 8405.060278] iwlwifi 0000:03:00.0: L1 Enabled - LTR Enabled
[ 8405.062923] sd 0:0:0:0: [sda] Starting disk
[ 8405.064194] sd 1:0:0:0: [sdb] Starting disk
[ 8405.066954] iwlwifi 0000:03:00.0: L1 Enabled - LTR Enabled
...
[ 8405.834966] PM: resume of devices complete after 783.018 msecs
[ 8405.835507] PM: Finishing wakeup.
从日志里面看来笔记本的确是进入睡眠状态之后立即被唤醒,能够将笔记本从休眠状唤醒的事件定义在/proc/acpi/wakeup这个文件里,只要将无关的事件禁用,就可以查出是哪个事件唤醒系统了。
lance@lance_tp ~ $ cat /proc/acpi/wakeup 
Device	S-state	  Status   Sysfs node
LID	  S4	*enabled   platform:PNP0C0D:00
SLPB	  S3	*enabled   platform:PNP0C0E:00
IGBE	  S4	*enabled   pci:0000:00:19.0
EXP2	  S4	*disabled  pci:0000:00:1c.1
XHCI	  S3	*disabled  pci:0000:00:14.0
EHC1	  S3	*enabled   pci:0000:00:1d.0

猜测LID是笔记本盖子、SLPB是睡眠按钮,IGBE是以太网(Wake on LAN?),其他几个貌似是USB的什么功能?

禁用或启用某个事件可以用开关控制:
echo IGBE | sudo tee /proc/acpi/wakeup
最后发现是IGBEXHCI这两个事件开启时会立即唤醒,只要将这两个事件禁用,在怎么折腾休眠后都不会立即唤醒了!写个服务每次开机的时候设置一下:
[Unit]
Description=disable some events to wake up device
After=systemd-udev-settle.service

[Service]
Type=idle
ExecStart=/opt/bin/for_systemd/suspend_event.sh
RemainAfterExit=no

[Install]
WantedBy=multi-user.target

注意After=systemd-udev-settle.service,在udev初始化完成之前设置无效!

调用的设置脚本suspend_event.sh(我只保留了LIDSLPB,其它用不上的都禁用了):
#!/bin/sh
# @Author: lance
# @Date:   2015-10-07 22:38:51
# @Last Modified by:   lance
# @Last Modified time: 2015-10-07 22:42:21
#
# Some events will wakeup right after suspend, disable them
stat=$(cat /proc/acpi/wakeup)
wakers=(IGBE EXP2 XHCI EHC1)
for waker in ${wakers[@]}; do
    is_en=$(echo "${stat}" | grep $waker | grep disabled);
    if  [ -z "$is_en" ]; then
        echo disable wakeup of $waker...
        echo $waker | tee /proc/acpi/wakeup;
    fi
done