待機異常篇

待機異常篇

待機異常篇待機異常
1: 按Power key後,連early_suspend都沒進。待機異常
2: 可以進early_suspend,但進不了suspend待機異常
3: 可以進suspend,但出現:PM: Some devices failed to suspend待機異常
4: 可以進入到suspend_enter,suspend流程走完了,但很快被喚醒待機異常
5: 可以進入到suspend_enter,也不被喚醒,但電流很大,CPU也較燙

這裏寫圖片描述
這裏寫圖片描述

關於early_suspend:
由於/sys/power/autosleep在處理suspend時,並沒有像/sys/power/state那樣,有對earlysuspend的兼容處理,如:
Kernel/kernel/power/main.c

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
               const char *buf, size_t n)
{
....
#ifdef CONFIG_EARLYSUSPEND
        if (state == PM_SUSPEND_ON || valid_state(state)) {
            error = 0;
            request_suspend_state(state);
        }
#else
        error = pm_suspend(state);
#endif
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
               const char *buf, size_t n)
{
....
#ifdef CONFIG_EARLYSUSPEND
        if (state == PM_SUSPEND_ON || valid_state(state)) {
            error = 0;
            request_suspend_state(state);
        }
#else
        error = pm_suspend(state);
#endif

進入request_suspend_state會先處理earlysuspend,然後再進入suspend。
而autosleep在處理時,是直接進入pm_suspend,不會去處理earlysuspend。

因此,不同平臺處理earlysuspend的方式也不同,如Intel平臺是通過將所有/sys/power/early_suspend/xxdevic/early_suspend文件寫1使其對應的設備進入earlysuspend(代碼路徑:hardware\libhardware\modules\power\),該操作發生在上圖Android待機流程的blankAllDisplays,在blankAllDisplays函數裏,會先調用nativeSetInteractive進入early_suspend,再調用nativeSetAutoSuspend進入suspend。
/sys/power/early_suspend/xxdevic/early_suspend 節點通過下面接口生成:
device_create_file(&dev->pdev->dev, &dev_attr_early_suspend);
register_early_suspend_device(&dev->pdev->dev);

待機異常1 :按Power key後,連early_suspend都沒進。

問題分析
這種情況發生在Android待機流程中的goToSleepInternal,由於不滿足待機條件而無法進入待機。
不能進入待機的條件:

private boolean goToSleepNoUpdateLocked(long eventTime, int reason) {

        if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
                || !mBootCompleted || !mSystemReady) {
            return false;
        }
  • 1

事件時間和電源狀態一般不會是異常的因素,因此引起異常的主要是mBootCompleted 和mSystemReady 兩個條件。

調試方法
mBootCompleted在收到Intent.ACTION_BOOT_COMPLETED廣播後置1,而Intent.ACTION_BOOT_COMPLETED廣播是在進入到Launcher後發出的,frameworks\base\services\java\com\android\server\am\ActivityManagerService.java,因此當系統啓動異常時進入不到Launcher,就無法待機;還有一種情問,多Launcher選擇界面時也因沒進入Launcher而不發ACTION_BOOT_COMPLETED廣播而造成待不了機。

當ActivityManagerService跑起來就表示mSystemReady 爲true,可以通過查看logcat,如果看到有“Activity Manager”的log表示已跑ActivityManagerService,如果看到“** Failure starting bootstrap service”,表示啓動失敗。

待機異常2:可以進early_suspend,但進不了suspend

問題分析
進入suspend時,會調用pm_get_wakeup_count,如果系統存在wake_lock就會卡在這個函數裏,如:
pm_get_wakeup_count:
prepare_to_wait(&wakeup_count_wait_queue, &wait,
TASK_INTERRUPTIBLE);

當wake_lock釋放完畢時纔會喚醒該wait,如下inpr = 0:
wakeup_source_deactivate:
if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
wake_up(&wakeup_count_wait_queue);

調試方法
在串口終端下,不要連接USB,連USB會產生wake_lock,cat /sys/power/wakeup_count,這時會去調用pm_get_wakeup_count,如果cat也卡住,說明存在suspend確實是卡在wake_lock上。
Wake_lock有些是android進程加的和有些是kernel進程加的。可以通過命令cat /sys/kernel/debug/wakeup_sources 來查看所有的wake_lock,如下:

active_since 所在列,如果有數值大於0,表示其所對應的行是當前活動的wake_lock,如上圖,PowerManagerService.WakeLocks和dwc_wake_lock是活動的。
原代碼的打印有可能對不齊,下面的改動可以讓其對齊:

--- a/kernel/drivers/base/power/wakeup.c
+++ b/kernel/drivers/base/power/wakeup.c
@@ -842,8 +842,7 @@ static int print_wakeup_source_stats(struct seq_file *m,


-       ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t"
-                       "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
+       ret = seq_printf(m, "%-35s%-15lu%-15lu%-15lu%-15lu%-15lld%-15lld%-15lld%-15lld%-15lld\n",
                        ws->name, active_count, ws->event_count,
                        ws->wakeup_count, ws->expire_count,
                        ktime_to_ms(active_time), ktime_to_ms(total_time),
@@ -863,9 +862,8 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
 {


-       seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"
-               "expire_count\tactive_since\ttotal_time\tmax_time\t"
-               "last_change\tprevent_suspend_time\n");
+       seq_printf(m, "%-35s%-15s%-15s%-15s%-15s%-15s%-15s%-15s%-15s%-15s\n",
+                       "name","active_count","event_count","wakeup_count","expire_count","active_since","total_time","max_time","last_chang
  • 1

android加的wake_lock可以通過命令cat /sys/power/wake_lock來查看,再在kernel/kernel/power/main.c 的wake_lock_store函數里加上打印: printk(“pid %d wirte %s to /sys/power/wake_lock\n”,current->pid,buf);,將pid打印出來,然後cat /proc/進程ID/status 來查看具體進程(也可以 ps 進程ID號來查看)。

待機異常3: 可以進suspend,但出現:PM: Some devices failed to suspend

問題分析
有些設備在待機時失敗了。

調試方法
首先用命令cat /sys/kernel/debug/suspend_stats查看待機異常發生在哪個階段,如下:
success: 3
fail: 1
failed_freeze: 0
failed_prepare: 0
failed_suspend: 1
failed_suspend_late: 0
failed_suspend_noirq: 0
failed_resume: 0
failed_resume_early: 0
failed_resume_noirq: 0
failures:
last_failed_dev:

last_failed_errno: -16
0
last_failed_step: suspend

failed_suspend 的記數大於0表示在device_suspend階段異常。
從log可以看到,如果是這樣的字段:
active wakeup source: event3-556
PM: Some devices failed to suspend
則表示有些活動wakeup鎖還沒釋放。在kernel/drivers/base/power/wakeup.c的wakeup_source_add函數裏添加打印:
printk(“%s:%s\n”,FUNCTION,ws->name);
dump_stack();
跟蹤出哪個驅動,然後在該驅動裏跟蹤wakeup鎖。

如果log是這樣的:
PM: Device xxx failed to xxx: error x
PM: Some devices failed to suspend
則表示有設備在待機時失敗,根據設備名找到相應驅動,然後跟蹤該驅動suspend失敗的原因。

另外,將kernel/kernle/printk.c裏的ignore_loglevel置1:
Kernel/kernel/printk.c
static bool __read_mostly ignore_loglevel=1;
用命令echo 1 > /sys/power/pm_print_times 打開kernel/drivers/base/power/main.c裏的initcall_debug_report打印,這樣,在待機時就可以打印出哪些設備待機成功或失敗。

待機異常4: 可以進入到suspend_enter,suspend流程走完了,但很快被喚醒

問題分析
Suspend流程走完後被喚醒,說明有喚醒源異常,造成異常喚醒。

調試方法
首先用命令cat /sys/kernel/debug/suspend_stats查看待機過程是否正常,如果正常,那麼需要找出喚醒源。
打開kernel/kernel/irq/pm.c的resume_irqs函數裏的打印信息:

#ifdef CONFIG_PM_DEBUG
        if (desc->istate & IRQS_PENDING) {
            printk(KERN_DEBUG "Wakeup from IRQ %d %s\n",
                irq,
                desc->action && desc->action->name ?
                desc->action->name : "");
        }
#endif /* CONFIG_PM_DEBUG */
  • 1

從打印信息裏可以看出是哪個中斷號喚醒的,命令cat /proc/interrupts可以查看所有中斷。另外,在kernel/kernel/irq/manage.c 的__setup_irq函數里加打印及dump_stack,跟蹤哪個驅動註冊了該irq號:

printk("%s:irq = %d,name = %s\n",__FUNCTION__,irq,new->name ?
            new->name : "");
dump_stack();

待機異常5: 可以進入到suspend_enter,也不被喚醒,但電流很大,CPU也較燙

問題分析
進入suspend_enter且不被喚醒,說明系統已進入待機,應該是SOC沒進入待機狀態造成

調試方法
可以在suspend_enter加SOC power state的寄存器的打印,或者跟vendor要soc待機的調試手段,比如 intel平臺,可以通過查詢cat /sys/kernel/debug/mid_pmu_states 查詢cpu有幾次進入待機狀態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章