1. 程式人生 > >Linux電源管理(6)_Generic PM之Suspend功能

Linux電源管理(6)_Generic PM之Suspend功能

1. 前言

Linux核心提供了三種Suspend: Freeze、Standby和STR(Suspend to RAM),在使用者空間向”/sys/power/state”檔案分別寫入”freeze”、”standby”和”mem”,即可觸發它們。

核心中,Suspend及Resume過程涉及到PM Core、Device PM、各個裝置的驅動、Platform dependent PM、CPU control等多個模組,涉及了console switch、process freeze、CPU hotplug、wakeup處理等過個知識點。就讓我們跟著核心程式碼,一一見識它們吧。

2. Suspend功能有關的程式碼分佈

核心中Suspend功能有關的程式碼包括PM core、Device PM、Platform PM等幾大塊,具體如下:

1)PM Core

kernel/power/main.c----提供使用者空間介面(/sys/power/state)

kernel/power/suspend.c----Suspend功能的主邏輯

kernel/power/suspend_test.c----Suspend功能的測試邏輯

kernel/power/console.c----Suspend過程中對控制檯的處理邏輯

kernel/power/process.c----Suspend過程中對程序的處理邏輯

2)Device PM

drivers/base/power/*----具體可參考“Linux電源管理(4)_Power Management Interface”的描述。

裝置驅動----具體裝置驅動的位置,不再涉及。

3)Platform dependent PM

include/linux/suspend.h----定義platform dependent PM有關的操作函式集

arch/xxx/mach-xxx/xxx.c或者

arch/xxx/plat-xxx/xxx.c----平臺相關的電源管理操作

3. suspend&resume過程概述

下面圖片對Linux suspend&resume過程做了一個概述,讀者可以順著這個流程閱讀核心原始碼。具體的說明,可以參考後面的程式碼分析。

suspend_flow

4. 程式碼分析

4.1 suspend入口

在使用者空間執行如下操作:

echo "freeze" > /sys/power/state

echo "standby" > /sys/power/state

echo "mem" > /sys/power/state

會通過sysfs觸發suspend的執行,相應的處理程式碼如下:


 
  1. staticssize_t state_store(struct kobject *kobj,struct kobj_attribute *attr,
  2. constchar*buf,size_t n)
  3. {
  4. suspend_state_t state;
  5. int error;
  6.  
  7. error = pm_autosleep_lock();
  8. if(error)
  9. return error;
  10.  
  11. if(pm_autosleep_state()> PM_SUSPEND_ON){
  12. error =-EBUSY;
  13. goto out;
  14. }
  15.  
  16. state = decode_state(buf, n);
  17. if(state < PM_SUSPEND_MAX)
  18. error = pm_suspend(state);
  19. elseif(state == PM_SUSPEND_MAX)
  20. error = hibernate();
  21. else
  22. error =-EINVAL;
  23.  
  24. out:
  25. pm_autosleep_unlock();
  26. return error ? error : n;
  27. }
  28.  
  29. power_attr(state);

power_attr定義了一個名稱為state的attribute檔案,該檔案的store介面為state_store,該介面在lock住autosleep功能後,解析使用者傳入的buffer(freeze、standby or mem),轉換成state引數。

state引數的型別為suspend_state_t,在include\linux\suspend.h中定義,為電源管理狀態在核心中的表示。具體如下:

 
  1. typedefint __bitwise suspend_state_t;
  2.  
  3. #define PM_SUSPEND_ON ((__force suspend_state_t)0)
  4. #define PM_SUSPEND_FREEZE ((__force suspend_state_t)1)
  5. #define PM_SUSPEND_STANDBY ((__force suspend_state_t)2)
  6. #define PM_SUSPEND_MEM ((__force suspend_state_t)3)
  7. #define PM_SUSPEND_MIN PM_SUSPEND_FREEZE
  8. #define PM_SUSPEND_MAX ((__force suspend_state_t)4)

根據state的值,如果不是(PM_SUSPEND_MAX,對應hibernate功能),則呼叫pm_suspend介面,進行後續的處理。 

pm_suspend在kernel/power/suspend.c定義,處理所有的suspend過程。  

4.2 pm_suspend & enter_state

pm_suspend的實現非常簡單,簡單的做一下引數合法性判斷,直接呼叫enter_state介面,如下:


 
  1. int pm_suspend(suspend_state_t state)
  2. {
  3. int error;
  4.  
  5. if(state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
  6. return-EINVAL;
  7.  
  8. error = enter_state(state);
  9. if(error){
  10. suspend_stats.fail++;
  11. dpm_save_failed_errno(error);
  12. }else{
  13. suspend_stats.success++;
  14. }
  15. return error;
  16. }

enter_state程式碼為:


 
  1. staticint enter_state(suspend_state_t state)
  2. {
  3. int error;
  4.  
  5. if(!valid_state(state))
  6. return-ENODEV;
  7.  
  8. if(!mutex_trylock(&pm_mutex))
  9. return-EBUSY;
  10.  
  11. if(state == PM_SUSPEND_FREEZE)
  12. freeze_begin();
  13.  
  14. printk(KERN_INFO "PM: Syncing filesystems ... ");
  15. sys_sync();
  16. printk("done.\n");
  17.  
  18. pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
  19. error = suspend_prepare(state);
  20. if(error)
  21. gotoUnlock;
  22.  
  23. if(suspend_test(TEST_FREEZER))
  24. gotoFinish;
  25.  
  26. pr_debug("PM: Entering %s sleep\n", pm_states[state]);
  27. pm_restrict_gfp_mask();
  28. error = suspend_devices_and_enter(state);
  29. pm_restore_gfp_mask();
  30.  
  31. Finish:
  32. pr_debug("PM: Finishing wakeup.\n");
  33. suspend_finish();
  34. Unlock:
  35. mutex_unlock(&pm_mutex);
  36. return error;
  37. }

主要工作包括:

a)呼叫valid_state,判斷該平臺是否支援該電源狀態。

suspend的最終目的,是讓系統進入可恢復的掛起狀態,而該功能必須有平臺相關程式碼的參與才能完成,因此核心PM Core就提供了一系列的回撥函式(封裝在platform_suspend_ops中),讓平臺程式碼(如arch/arm/mach-xxx/pm.c)實現,然後由PM Core在合適的時機呼叫。這些回撥函式包含一個valid函式,就是用來告知PM Core,支援哪些state。

最後看一下valid_state的實現(刪除了無關程式碼):

 
  1. bool valid_state(suspend_state_t state)
  2. {
  3. if(state == PM_SUSPEND_FREEZE){
  4. returntrue;
  5. }
  6. /*
  7. * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel
  8. * support and need to be valid to the lowlevel
  9. * implementation, no valid callback implies that none are valid.
  10. */
  11. return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
  12. }

如果是freeze,無需平臺程式碼參與即可支援,直接返回true。對於standby和mem,則需要呼叫suspend_ops的valid回掉,由底層平臺程式碼判斷是否支援。 

b)加互斥鎖,只允許一個例項處理suspend。

c)如果state是freeze,呼叫freeze_begin,進行suspend to freeze相關的特殊動作。我會在後面統一分析freeze的特殊動作,這裡暫不描述。

d)列印提示資訊,同步檔案系統。

e)呼叫suspend_prepare,進行suspend前的準備,主要包括switch console和process&thread freezing。如果失敗,則終止suspend過程。

f)然後,呼叫suspend_devices_and_enter介面,該介面負責suspend和resume的所有實際動作。前半部分,suspend console、suspend device、關中斷、呼叫平臺相關的suspend_ops使系統進入低功耗狀態。後半部分,在系統被事件喚醒後,處理相關動作,呼叫平臺相關的suspend_ops恢復系統、開中斷、resume device、resume console。

g)最後,呼叫suspend_finish,恢復(或等待恢復)process&thread,還原console。  

4.3 suspend_prepare

suspend_prepare的程式碼如下:

 
  1. staticint suspend_prepare(suspend_state_t state)
  2. {
  3. int error;
  4.  
  5. if(need_suspend_ops(state)&&(!suspend_ops ||!suspend_ops->enter))
  6. return-EPERM;
  7.  
  8. pm_prepare_console();
  9.  
  10. error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
  11. if(error)
  12. gotoFinish;
  13.  
  14. error = suspend_freeze_processes();
  15. if(!error)
  16. return0;
  17.  
  18. suspend_stats.failed_freeze++;
  19. dpm_save_failed_step(SUSPEND_FREEZE);
  20. Finish:
  21. pm_notifier_call_chain(PM_POST_SUSPEND);
  22. pm_restore_console();
  23. return error;
  24. }

主要工作為:

a)檢查suspend_ops是否提供了.enter回撥,沒有的話,返回錯誤。

b)呼叫pm_prepare_console,將當前console切換到一個虛擬console並重定向核心的kmsg(需要的話)。該功能稱作VT switch,後面我會在稍微詳細的介紹一下,但Linux控制檯子系統是相當複雜的,更具體的分析,要在控制檯子系統的分析文章中說明。

c)呼叫pm_notifier_call_chain,傳送suspend開始的訊息(PM_SUSPEND_PREPARE),後面會詳細描述。

d)呼叫suspend_freeze_processes,freeze使用者空間程序和一些核心執行緒。該功能稱作freezing-of-tasks,我會專門用一篇文章去分析它。本文就不再詳細說明了。

e)如果freezing-of-tasks失敗,呼叫pm_restore_console,將console切換回原來的console,並返回錯誤,以便能終止suspend。  

4.4 suspend_devices_and_enter

suspend_devices_and_enter的過程較為複雜,程式碼實現如下:

 
  1. int suspend_devices_and_enter(suspend_state_t state)
  2. {
  3. int error;
  4. bool wakeup =false;
  5.  
  6. if(need_suspend_ops(state)&&!suspend_ops)
  7. return-ENOSYS;
  8.  
  9. trace_machine_suspend(state);
  10. if(need_suspend_ops(state)&& suspend_ops->begin){
  11. error = suspend_ops->begin(state);
  12. if(error)
  13. gotoClose;
  14. }
  15. suspend_console();
  16. ftrace_stop();
  17. suspend_test_start();
  18. error = dpm_suspend_start(PMSG_SUSPEND);
  19. if(error){
  20. printk(KERN_ERR "PM: Some devices failed to suspend\n");
  21. gotoRecover_platform;
  22. }
  23. suspend_test_finish("suspend devices");
  24. if(suspend_test(TEST_DEVICES))
  25. gotoRecover_platform;
  26.  
  27. do{
  28. error = suspend_enter(state,&wakeup);
  29. }while(!error &&!wakeup && need_suspend_ops(state)
  30. && suspend_ops->suspend_again && suspend_ops->suspend_again());
  31.  
  32. Resume_devices:
  33. suspend_test_start();
  34. dpm_resume_end(PMSG_RESUME);
  35. suspend_test_finish("resume devices");
  36. ftrace_start();
  37. resume_console();
  38. Close:
  39. if(need_suspend_ops(state)&& suspend_ops->end)
  40. suspend_ops->end();
  41. trace_machine_suspend(PWR_EVENT_EXIT);
  42. return error;
  43.  
  44. Recover_platform:
  45. if(need_suspend_ops(state)&& suspend_ops->recover)
  46. suspend_ops->recover();
  47. gotoResume_devices;
  48. }

a)再次檢查平臺程式碼是否需要提供以及是否提供了suspend_ops。

b)呼叫suspend_ops的begin回撥(有的話),通知平臺程式碼,以便讓其作相應的處理(需要的話)。可能失敗,需要跳至Close處執行恢復操作(suspend_ops->end)。

c)呼叫suspend_console,掛起console。該介面由"kernel\printk.c"實現,主要是hold住一個lock,該lock會阻止其它程式碼訪問console。

d)呼叫ftrace_stop,停止ftrace功能。ftrace是一個很有意思的功能,後面再介紹。

e)呼叫dpm_suspend_start,呼叫所有裝置的->prepare和->suspend回撥函式(具體可參考“Linux電源管理(4)_Power Management Interface”的描述),suspend需要正常suspend的裝置。suspend device可能失敗,需要跳至 Recover_platform,執行recover操作(suspend_ops->recover)。

f)以上都是suspend前的準備工作,此時,呼叫suspend_enter介面,使系統進入指定的電源狀態。該介面的內容如下:

 
  1. staticint suspend_enter(suspend_state_t state,bool*wakeup)
  2. {
  3. int error;
  4.  
  5. if(need_suspend_ops(state)&& suspend_ops->prepare){
  6. error = suspend_ops->prepare();
  7. if(error)
  8. gotoPlatform_finish;
  9. }
  10.  
  11. error = dpm_suspend_end(PMSG_SUSPEND);
  12. if(error){
  13. printk(KERN_ERR "PM: Some devices failed to power down\n");
  14. gotoPlatform_finish;
  15. }
  16.  
  17. if(need_suspend_ops(state)&& suspend_ops->prepare_late){
  18. error = suspend_ops->prepare_late();
  19. if(error)
  20. gotoPlatform_wake;
  21. }
  22.  
  23. if(suspend_test(TEST_PLATFORM))
  24. gotoPlatform_wake;
  25.  
  26. /*
  27. * PM_SUSPEND_FREEZE equals
  28. * frozen processes + suspended devices + idle processors.
  29. * Thus we should invoke freeze_enter() soon after
  30. * all the devices are suspended.
  31. */
  32. if(state == PM_SUSPEND_FREEZE){
  33. freeze_enter();
  34. gotoPlatform_wake;
  35. }
  36.  
  37. error = disable_nonboot_cpus();
  38. if(error || suspend_test(TEST_CPUS))
  39. gotoEnable_cpus;
  40.  
  41. arch_suspend_disable_irqs();
  42. BUG_ON(!irqs_disabled());
  43.  
  44. error = syscore_suspend();
  45. if(!error){
  46. *wakeup = pm_wakeup_pending();
  47. if(!(suspend_test(TEST_CORE)||*wakeup)){
  48. error = suspend_ops->enter(state);
  49. events_check_enabled =false;
  50. }
  51. syscore_resume();
  52. }
  53.  
  54. arch_suspend_enable_irqs();
  55. BUG_ON(irqs_disabled());
  56.  
  57. Enable_cpus:
  58. enable_nonboot_cpus();
  59.  
  60. Platform_wake:
  61. if(need_suspend_ops(state)&& suspend_ops->wake)
  62. suspend_ops->wake();
  63.  
  64. dpm_resume_start(PMSG_RESUME);
  65.  
  66. Platform_finish:
  67. if(need_suspend_ops(state)&& suspend_ops->finish)
  68. suspend_ops->finish();
  69.  
  70. return error;
  71. }

        f1)該介面處理完後,會通過返回值告知是否enter成功,同時通過wakeup指標,告知呼叫者,是否有wakeup事件發生,導致電源狀態切換失敗。

        f2)呼叫suspend_ops的prepare回撥(有的話),通知平臺程式碼,以便讓其在即將進行狀態切換之時,再做一些處理(需要的話)。該回調可能失敗(平臺程式碼出現意外),失敗的話,需要跳至Platform_finish處,呼叫suspend_ops的finish回撥,執行恢復操作。

        f3)呼叫dpm_suspend_end,呼叫所有裝置的->suspend_late和->suspend_noirq回撥函式(具體可參考“Linux電源管理(4)_Power Management Interface”的描述),suspend late suspend裝置和需要在關中斷下suspend的裝置。需要說明的是,這裡的noirq,是通過禁止所有的中斷線的形式,而不是通過關全域性中斷的方式。同樣,該操作可能會失敗,失敗的話,跳至Platform_finish處,執行恢復動作。

        f4)呼叫suspend_ops的prepare_late回撥(有的話),通知平臺程式碼,以便讓其在最後關頭,再做一些處理(需要的話)。該回調可能失敗(平臺程式碼出現意外),失敗的話,需要跳至Platform_wake處,呼叫suspend_ops的wake回撥,執行device的resume、呼叫suspend_ops的finish回撥,執行恢復操作。

        f5)如果是suspend to freeze,執行相應的操作,包括凍結程序、suspended devices(引數為PM_SUSPEND_FREEZE)、cpu進入idle。如果有任何事件使CPU從idle狀態退出,跳至Platform_wake處,執行wake操作。

        f6)呼叫disable_nonboot_cpus,禁止所有的非boot cpu。也會失敗,執行恢復操作即可。

        f7)呼叫arch_suspend_disable_irqs,關全域性中斷。如果無法關閉,則為bug。

        f8)呼叫syscore_suspend,suspend system core。同樣會失敗,執行恢復操作即可。有關syscore,我會在另一篇文章中詳細描述。

        f9)如果很幸運,以上操作都成功了,那麼,切換吧。不過,別高興太早,還得呼叫pm_wakeup_pending檢查一下,這段時間內,是否有喚醒事件發生,如果有就要終止suspend。

        f10)如果一切順利,呼叫suspend_ops的enter回撥,進行狀態切換。這時,系統應該已經suspend了……

        f11)suspend過程中,喚醒事件發生,系統喚醒,該函式接著執行resume動作,並最終返回。resume動作基本上是suspend的反動作,就不再繼續分析了。

        f12)或者,由於意外,suspend終止,該函式也會返回。

g)suspend_enter返回,如果返回原因不是發生錯誤,且不是wakeup事件。則呼叫suspend_ops的suspend_again回撥,檢查是否需要再次suspend。再什麼情況下要再次suspend呢?需要看具體的平臺了,誰知道呢。

h)繼續resume操作,resume device、start ftrace、resume console、suspend_ops->end等等。

i)該函式返回後,表示系統已經resume。 

4.5 suspend_finish

比較簡單:


 
  1. staticvoid suspend_finish(void)
  2. {
  3. suspend_thaw_processes();
  4. pm_notifier_call_chain(PM_POST_SUSPEND);
  5. pm_restore_console();
  6. }

a)恢復所有的使用者空間程序和核心執行緒。

b)傳送suspend結束的通知。

c)將console切換回原來的。   

 

5. 重要知識點回顧

5.1 VT switch

通常情況下,系統控制檯模組(drivers\tty\vt\)會在suspend的過程中,重新分配一個console,並將控制檯切換到該console上。然後在resume時,切換回舊的console。這就是VT switch功能。VT switch是很耗時的,因此核心提供了一些機制,控制是否使用這個功能:

1)提供一個介面函式pm_set_vt_switch(drivers\tty\vt\vt_ioctl.c),方便其它核心模組從整體上關閉或者開啟VT switch功能。

2)VT switch全域性開關處於開啟狀態時,滿足如下的一種條件(可參考kernel\power\console.c相關的描述),即會使能VT switch

        a)有console driver呼叫pm_vt_switch_required介面,顯式的要求使能VT switch。PM core的console模組會把這些資訊記錄在一個名稱為pm_vt_switch_list的連結串列中。

       b)系統禁止在suspend的過程中suspend console(由kernel/printk.c中的console_suspend_enabled變數控制)。很有可能需要使用console檢視suspend過程,此時為了使console不混亂,有必要進行VT switch。

       c)沒有任何console driver關心是否需要VT switch,換句話說沒有任何driver呼叫pm_vt_switch_required介面要求使能或禁止VT switch功能。此時會按照舊的習慣,進行VT switch。  

 

因此,suspend過程對console的處理分為4步:

prepare console:負責在需要 VT swich 時,將當前 console 切換到 SUSPEND console

 
  1. int pm_prepare_console(void)
  2. {
  3. if(!pm_vt_switch())
  4. return0;
  5.  
  6. orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE,1);
  7. if(orig_fgconsole <0)
  8. return1;
  9.  
  10. orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
  11. return0;
  12. }

suspend console:掛起console,由kernel/printk.c實現,主要是hold住console用的互斥鎖,使他人無法使用console。  

resume console:對console解鎖。

restore console:將console恢復為初始的console。


 
  1. void pm_restore_console(void)
  2. {
  3. if(!pm_vt_switch())
  4. return;
  5.  
  6. if(orig_fgconsole >=0){
  7. vt_move_to_console(orig_fgconsole,0);
  8. vt_kmsg_redirect(orig_kmsg);
  9. }
  10. }

也許,您會問,why VT switch?先留著這個疑問吧,等到分析控制檯時再回答。

5.2 freezing of task

程序的freezing功能,是suspend、hibernate等電源管理功能的組成部分,在新版本核心中,它被獨立出來,作為一個獨立的電源管理狀態(freeze)。該功能的目的,是在電源管理的狀態切換過程中,確保所有使用者空間程序和部分核心執行緒處於一個穩定的狀態。有關該功能的具體描述,請參考wowotech後續的文章。 

5.3 PM notifier

PM notifier是基於核心blocking notifier功能實現的。blocking notifier提供了一種kernel內部的訊息通知機制,訊息接受者通過notifier註冊的方式,註冊一個回撥函式,關注訊息傳送者發出的notifier。當訊息產生時,訊息產生者通過呼叫回撥函式的形式,通知訊息接受者。這種呼叫,是可以被阻塞的,因此稱作blocking notifier。

那suspend功能為什麼使用notifier呢?原因可能有多種,這裡我舉一個例子,這是我們日常開發中可能會遇到的。

由之前的描述可知,suspend過程中,suspend device發生在程序被freeze之後,resume device發生在程序被恢復之前。那麼:

1)如果有些裝置就需要在freeze程序之前suspend怎麼辦?

2)如果有些裝置的resume動作需要較多延時,或者要等待什麼事情發生,那麼如果它的resume動作發生在程序恢復之前,豈不是要阻止所有程序的恢復?更甚者,如果該裝置要等待某個程序的資料才能resume,怎麼辦?

再來看 suspend_prepare suspend_finish 中的處理:

 
  1. staticint suspend_prepare(suspend_state_t state){
  2. error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
  3. if(error)
  4. gotoFinish;
  5.  
  6. error = suspend_freeze_processes();
  7. }
  8.  
  9. staticvoid suspend_finish(void)
  10. {
  11. suspend_thaw_processes();
  12. pm_notifier_call_chain(PM_POST_SUSPEND);
  13. pm_restore_console();
  14. }

原來PM notifier是在裝置模型的框架外,開了一個後門,那些比較特殊的driver,可以繞過裝置模型,直接接收PM傳送的suspend資訊,以便執行自身的suspend動作。特別是resume時,可以在其它程序都正好工作的時候,只讓suspend程序等待driver的resume。

感興趣的讀者,可以圍觀一下下面這個活生生的例子(順便提一下,好的設計是不應該有例外的):

drivers\video\omap2\dss\core.c  

5.4 device PM ops 和platform PM ops的呼叫時機

Linux 驅動工程師來說, device PM ops platform PM ops 就是電源管理( suspend )的全部,只要在合適的地方,實現合適的回撥函式,即可實現系統的電源管理。但現實太複雜了,以至於 kernel 提供的這兩個資料結構也很複雜,再回憶一下,如下:

 
  1. struct dev_pm_ops {
  2. int(*prepare)(struct device *dev);
  3. void(*complete)(struct device *dev);
  4. int(*suspend)(struct device *dev);
  5. int(*resume)(struct device *dev);
  6. int(*freeze)(struct device *dev);
  7. int(*thaw)(struct device *dev);
  8. int(*poweroff)(struct device *dev);
  9. int(*restore)(struct device *dev);
  10. int(*suspend_late)(struct device *dev);
  11. int(*resume_early)(struct device *dev);
  12. int(*freeze_late)(struct device *dev);
  13. int(*thaw_early)(struct device *dev);
  14. int(*poweroff_late)(struct device *dev);
  15. int(*restore_early)(struct device *dev);
  16. int(*suspend_noirq)(struct device *dev);
  17. int(*resume_noirq)(struct device *dev);
  18. int(*freeze_noirq)(struct device *dev);
  19. int(*thaw_noirq)(struct device *dev);
  20. int(*poweroff_noirq)(struct device *dev);
  21. int(*restore_noirq)(struct device *dev);
  22. int(*runtime_suspend)(struct device *dev);
  23. int(*runtime_resume)(struct device *dev);
  24. int(*runtime_idle)(struct device *dev);
  25. };
  26.  
  27. struct platform_suspend_ops {
  28. int(*valid)(suspend_state_t state);
  29. int(*begin)(suspend_state_t state);
  30. int(*prepare)(void);
  31. int(*prepare_late)(void);
  32. int(*enter)(suspend_state_t state);
  33. void(*wake)(void);
  34. void(*finish)(void);
  35. bool(*suspend_again)(void);
  36. void(*end)(void);
  37. void(*recover)(void);
  38. };

雖然核心的註釋已經相當詳細了,但我們一定會犯暈,到底該實現哪些回撥?這些回撥的應用場景又是什麼?蝸蝸以為,要熟練使用這些回撥,唯一的方法就是多coding、多理解。除此之外,我們可以總結一下在電源狀態切換時,這些回撥的呼叫時機,從側面幫助理解。如下(只介紹和suspend功能有關的,struct dev_pm_ops簡稱D,struct platform_suspend_ops簡稱P):

pm_ops_flow


5.5 suspend過程的同步和PM wakeup

最重要的事情,如果suspend的過程中,有喚醒事件產生怎麼辦?正常的流程,應該終止suspend,返回並處理事件。但由於suspend過程的特殊性,程序被freeze、關中斷等等,導致事情並沒有那麼簡單,以至於在很久的一段時間內,kernel都不能很好的處理。這也稱作suspend過程的同步問題。

在美好的舊時光裡,suspend大多用於熱關機,因此同步問題的影響並不突出(因為操作並不頻繁)。但來到新時代之後,事情變了,Android竟然用suspend作日常的待機(操作就相當頻繁了),這時問題就大了。那怎麼解決呢?得靠system wakeup framework,也就是suspend過程中所呼叫的pm_wakeup_pending介面所在的模組。我會在下一篇文章中繼續該模組的分析,這裡就不再繼續了。