linux休眠喚醒流程

1、框架

2、休眠流程
應用層通過echo mem > /sys/power/state寫入休眠狀態,給一張大概流程圖

這個操作對應在kernel/power/main.c的state這個attr的store操作

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
? ? ? ? ? ? ? ? ? ? ? ? ? ?const char *buf, size_t n)
{
? ? ? ? suspend_state_t state;
? ? ? ? int error;
?
? ? ? ? error = pm_autosleep_lock();
? ? ? ? if (error)
? ? ? ? ? ? ? ? return error;
?
? ? ? ? if (pm_autosleep_state() > PM_SUSPEND_ON) {
? ? ? ? ? ? ? ? error = -EBUSY;
? ? ? ? ? ? ? ? goto out;
? ? ? ? }
?
? ? ? ? state = decode_state(buf, n);
? ? ? ? if (state < PM_SUSPEND_MAX) {
? ? ? ? ? ? ? ? if (state == PM_SUSPEND_MEM)
? ? ? ? ? ? ? ? ? ? ? ? state = mem_sleep_current;
?
? ? ? ? ? ? ? ? error = pm_suspend(state);
? ? ? ? } else if (state == PM_SUSPEND_MAX) {
? ? ? ? ? ? ? ? error = hibernate();
? ? ? ? } else {
? ? ? ? ? ? ? ? error = -EINVAL;
? ? ? ? }
?
?out:
? ? ? ? pm_autosleep_unlock();
? ? ? ? return error ? error : n;
}

應用層通過/sys/power/state寫入休眠狀態;或者使能autosleep都會調用這個

int pm_suspend(suspend_state_t state)
{
?? ?int error;
?
?? ?if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
?? ??? ?return -EINVAL;
?
?? ?pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
?? ?error = enter_state(state);
?? ?if (error) {
?? ??? ?suspend_stats.fail++;
?? ??? ?dpm_save_failed_errno(error);
?? ?} else {
?? ??? ?suspend_stats.success++;
?? ?}
?? ?pr_info("suspend exit\n");
?? ?return error;
}

不同state,進入不同休眠狀態

static int enter_state(suspend_state_t state)
{
?? ?int error;
?
?? ?trace_suspend_resume(TPS("suspend_enter"), state, true);
?? ?if (state == PM_SUSPEND_TO_IDLE) {
#ifdef CONFIG_PM_DEBUG
?? ??? ?if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
?? ??? ??? ?pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
?? ??? ??? ?return -EAGAIN;
?? ??? ?}
#endif
?? ?} else if (!valid_state(state)) {
?? ??? ?return -EINVAL;
?? ?}
?? ?if (!mutex_trylock(&system_transition_mutex))
?? ??? ?return -EBUSY;
?
?? ?if (state == PM_SUSPEND_TO_IDLE)
?? ??? ?s2idle_begin();
?
?? ?if (sync_on_suspend_enabled) {
?? ??? ?trace_suspend_resume(TPS("sync_filesystems"), 0, true);
?? ??? ?ksys_sync_helper();
?? ??? ?trace_suspend_resume(TPS("sync_filesystems"), 0, false);
?? ?}
?
?? ?pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
?? ?pm_suspend_clear_flags();
?? ?error = suspend_prepare(state);
?? ?if (error)
?? ??? ?goto Unlock;
?
?? ?if (suspend_test(TEST_FREEZER))
?? ??? ?goto Finish;
?
?? ?trace_suspend_resume(TPS("suspend_enter"), state, false);
?? ?pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);
?? ?pm_restrict_gfp_mask();
?? ?error = suspend_devices_and_enter(state);
?? ?pm_restore_gfp_mask();
?
?Finish:
?? ?events_check_enabled = false;
?? ?pm_pr_dbg("Finishing wakeup.\n");
?? ?suspend_finish();
?Unlock:
?? ?mutex_unlock(&system_transition_mutex);
?? ?return error;
}

設備進入休眠,被喚醒或者休眠失敗,就會走對應的喚醒流程;

掛起console,比如串口,終端等;

掛起devfreq,cpufreq;執行device_suspend

int suspend_devices_and_enter(suspend_state_t state)
{
?? ?int error;
?? ?bool wakeup = false;
?
?? ?if (!sleep_state_supported(state))
?? ??? ?return -ENOSYS;
?
?? ?pm_suspend_target_state = state;
?
?? ?if (state == PM_SUSPEND_TO_IDLE)
?? ??? ?pm_set_suspend_no_platform();
?
?? ?error = platform_suspend_begin(state);
?? ?if (error)
?? ??? ?goto Close;
?
?? ?suspend_console();//掛起console,比如串口,終端等
?? ?suspend_test_start();
?? ?error = dpm_suspend_start(PMSG_SUSPEND);//掛起devfreq,cpufreq;執行device_suspend
?? ?if (error) {
?? ??? ?pr_err("Some devices failed to suspend, or early wake event detected\n");
?? ??? ?goto Recover_platform;
?? ?}
?? ?suspend_test_finish("suspend devices");
?? ?if (suspend_test(TEST_DEVICES))
?? ??? ?goto Recover_platform;
?
?? ?do {
?? ??? ?error = suspend_enter(state, &wakeup);//平臺休眠
?? ?} while (!error && !wakeup && platform_suspend_again(state));
?
?Resume_devices:
?? ?suspend_test_start();
?? ?dpm_resume_end(PMSG_RESUME);
?? ?suspend_test_finish("resume devices");
?? ?trace_suspend_resume(TPS("resume_console"), state, true);
?? ?resume_console();
?? ?trace_suspend_resume(TPS("resume_console"), state, false);
?
?Close:
?? ?platform_resume_end(state);
?? ?pm_suspend_target_state = PM_SUSPEND_ON;
?? ?return error;
?
?Recover_platform:
?? ?platform_recover(state);
?? ?goto Resume_devices;
}

平臺進入休眠;被喚醒或者休眠失敗,就會走對應的喚醒流程;

檢查pendind標記,檢查休眠鎖標記,來進入平臺實現的enter函數;

平臺休眠的最后,會開啟中斷,用與響應外部中斷,來喚醒系統并繼續執行接下來的代碼喚醒系統

static int suspend_enter(suspend_state_t state, bool *wakeup)
{
?? ?int error;
?
?? ?error = platform_suspend_prepare(state);
?? ?if (error)
?? ??? ?goto Platform_finish;
?
?? ?error = dpm_suspend_late(PMSG_SUSPEND);
?? ?if (error) {
?? ??? ?pr_err("late suspend of devices failed\n");
?? ??? ?goto Platform_finish;
?? ?}
?? ?error = platform_suspend_prepare_late(state);
?? ?if (error)
?? ??? ?goto Devices_early_resume;
?
?? ?error = dpm_suspend_noirq(PMSG_SUSPEND);
?? ?if (error) {
?? ??? ?pr_err("noirq suspend of devices failed\n");
?? ??? ?goto Platform_early_resume;
?? ?}
?? ?error = platform_suspend_prepare_noirq(state);
?? ?if (error)
?? ??? ?goto Platform_wake;
?
?? ?if (suspend_test(TEST_PLATFORM))
?? ??? ?goto Platform_wake;
?
?? ?if (state == PM_SUSPEND_TO_IDLE) {
?? ??? ?s2idle_loop();
?? ??? ?goto Platform_wake;
?? ?}
?
?? ?error = pm_sleep_disable_secondary_cpus();
?? ?if (error || suspend_test(TEST_CPUS))
?? ??? ?goto Enable_cpus;
?
?? ?arch_suspend_disable_irqs();
?? ?BUG_ON(!irqs_disabled());
?
?? ?system_state = SYSTEM_SUSPEND;
?
?? ?error = syscore_suspend();
?? ?if (!error) {
?? ??? ?*wakeup = pm_wakeup_pending();//檢查能否進休眠
?? ??? ?if (!(suspend_test(TEST_CORE) || *wakeup)) {
?? ??? ??? ?trace_suspend_resume(TPS("machine_suspend"),
?? ??? ??? ??? ?state, true);
?? ??? ??? ?error = suspend_ops->enter(state);
?? ??? ??? ?trace_suspend_resume(TPS("machine_suspend"),
?? ??? ??? ??? ?state, false);
?? ??? ?} else if (*wakeup) {
?? ??? ??? ?error = -EBUSY;
?? ??? ?}
?? ??? ?syscore_resume();
?? ?}
?
?? ?system_state = SYSTEM_RUNNING;
?
?? ?arch_suspend_enable_irqs();//平臺休眠,但是開啟中斷,用與響應中斷,喚醒系統并繼續執行接下來的代碼喚醒系統
?? ?BUG_ON(irqs_disabled());
?
?Enable_cpus:
?? ?pm_sleep_enable_secondary_cpus();
?
?Platform_wake:
?? ?platform_resume_noirq(state);
?? ?dpm_resume_noirq(PMSG_RESUME);
?
?Platform_early_resume:
?? ?platform_resume_early(state);
?
?Devices_early_resume:
?? ?dpm_resume_early(PMSG_RESUME);
?
?Platform_finish:
?? ?platform_resume_finish(state);
?? ?return error;
}

3、兩種阻止進入休眠
最終都是通過__pm_stay_awake

應用層
echo abc > /sys/power/wake_lock 來申請一個休眠鎖;

使用cat /sys/kernel/debug/wakeup_sources看什么在持有休眠鎖;?

echo abc > /sys/power/wake_unlock來接觸休眠鎖

內核層
應用請求休眠,系統進入休眠流程,此時如果設備觸發了中斷,中斷處理程序中首先關閉中斷,然后調度內核線程去處理work,但假如這個時候此work還未被調度到,系統就進入休眠了,那么這個設備就被永久關閉中斷了,再也不能喚醒系統。pm_stay_awake()和pm_relax()的設計就是用來解決這個問題。

pm_stay_awake
? ? ? ? pm_wake_lock
? ? ? ? ? ? ? ? __pm_stay_awake
恢復
? ? ? ? pm_wake_unlock
? ? ? ? ? ? ? ? __pm_relax


休眠檢查
pm_wakeup_pending


示例:休眠后,無法喚醒?


開啟打印信息
休眠后系統卡住,組織串口來休眠,并開啟相關打印;在Linux內核睡眠過程中,會先調用suspend_console()函數使串口進入睡眠狀態,這樣會導致后續設備驅動的睡眠過程不可見。可以在boot啟動參數中增加no_console_suspend參數,顯示設備驅動睡眠日志

remove_cmdline_param(cmdline, "no_console_suspend");
sprintf(cmdline + strlen(cmdline), " no_console_suspend=%d", 1);
修改串口日志打印等級,顯示更多調試信息

echo 8 > /proc/sys/kernel/printk
設置pm_print_times參數,可以顯示設備驅動睡眠喚醒時間,方便調試時查看哪個函數處理占用時間過長

echo 1 > /sys/power/pm_print_times
設置pm_debug_messages,打印來自系統的調試消息的暫停/休眠內核日志的基礎結構

echo 1 > /sys/power/pm_debug_messages
打印信息
PM: pm_system_irq_wakeup: 20 triggered PMIC
pxa2xx-i2c pxa2xx-i2c.2: calling i2c_pxa_suspend_noirq+0x1/0x24 @ 6223, parent: d4000000.apb
i2c: <pxa_i2c-i2c> ICR is modified!
pxa2xx-i2c pxa2xx-i2c.2: i2c_pxa_suspend_noirq+0x1/0x24 returned 0 after 0 usecs
i2c: reset controller!
Workqueue: events chargeic_update_state_work_func
pcie-falcon d4220000.pcie: calling pcie_resume_noirq+0x1/0x1c @ 6223, parent: d4200000.axi
PCIe Host: No link negotiated
pcie-falcon d4220000.pcie: pcie_resume_noirq+0x1/0x1c returned 0 after 114202 usecs
pci 0001:00:00.0: calling pci_pm_resume_noirq+0x1/0xd4 @ 6165, parent: pci0001:00
pxa2xx-i2c pxa2xx-i2c.0: calling i2c_pxa_resume_noirq+0x1/0x38 @ 6223, parent: d4000000.apb
解決辦法
i2c還在工作----而且打印了正在工作的函數;確認是充電ic休眠函數沒去暫停工作隊列;實現PM函數即可修復

#ifdef CONFIG_PM
static int charger_suspend(struct device *dev)
{
? ? ? ? cancel_delayed_work_sync(g_info->chg_state_update_work);
? ? ? ? return 0;
}
?
static int charger_resume(struct device *dev)
{
? ? ? ? mod_delayed_work(system_wq, g_info->chg_state_update_work,msecs_to_jiffies(1000));
? ? ? ? return 0;
}
?
static const struct dev_pm_ops pm_ops = {
? ? ? ? .suspend = charger_suspend,
? ? ? ? .resume = charger_resume,
};
?
#endif

找不到pcie設備----確認供電;如無異常,確認cpu和ddr頻率是否恢復;如無異常,確認現象是否跟復位腳異常有關

--- a/drivers/pci/controller/pcie-host.c
+++ b/drivers/pci/controller/pcie-host.c
@@ -646,6 +646,7 @@ static int __maybe_unused pcie_suspend_noirq(struct device *dev)
? ? ? ? phy_exit(port->phy);
? ? ? ? pm_qos_update_request(&pcie->qos_idle,
? ? ? ? ? ? ? ? ? ? ? ? PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
+ ? ? ? gpio_set_value(port->gpio_reset,0);
? ? ? ? return 0;
?}
?
@@ -653,10 +654,10 @@ static int __maybe_unused pcie_resume_noirq(struct device *dev)
?{
? ? ? ? struct pcie *pcie = dev_get_drvdata(dev);
? ? ? ? struct pcie_port *port = pcie->port;
-
+ ? ? ? gpio_set_value(port->gpio_reset,1);
+ ? ? ? mdelay(200);
? ? ? ? pm_qos_update_request(&pcie->qos_idle, port->lpm_qos);
? ? ? ? pcie_enable_port(port);
? ? ? ? return 0;
?}

修改后系統可被正常喚醒

4、echo mem > /sys/power/state

做如上操作后,整個函數調用流程如下:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901835.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901835.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901835.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mysql--基礎知識點--93--兩階段提交

1 兩階段提交 以update語句的具體執行過程為例&#xff1a; 具體更新一條記錄 UPDATE t_user SET name ‘xiaolin’ WHERE id 1;的流程如下&#xff1a; 1.執行器負責具體執行&#xff0c;會調用存儲引擎的接口&#xff0c;通過主鍵索引樹搜索獲取 id 1 這一行記錄&#…

Windows 環境下 Apache 配置 WebSocket 支持

目錄 前言1. 基本知識2. 實戰前言 ?? 找工作,來萬碼優才:?? #小程序://萬碼優才/r6rqmzDaXpYkJZF 爬蟲神器,無代碼爬取,就來:bright.cn 原先寫過apache的http配置:Apache httpd-vhosts.conf 配置詳解(附Demo) 1. 基本知識 ?? WebSocket 是 HTTP 的升級協議 客戶…

UMAEA論文閱讀

Preliminaries MMKG為一個五元組G{E, R, A, V, T}&#xff0c;其中E、R、A和V分別表示實體集、關系集、屬性集和圖像集。 T?ERE是關系三元組集。 給定兩個MMKG G1 {E1, R1, A1, V1, T1} 和 G2 {E2, R2, A2, V2, T2}&#xff0c; MMEA旨在識別每個實體對&#xff08;e1…

AIGC-十款知識付費類智能體完整指令直接用(DeepSeek,豆包,千問,Kimi,GPT)

Unity3D特效百例案例項目實戰源碼Android-Unity實戰問題匯總游戲腳本-輔助自動化Android控件全解手冊再戰Android系列Scratch編程案例軟考全系列Unity3D學習專欄藍橋系列AIGC(GPT、DeepSeek、豆包、千問、Kimi)??關于作者 專注于Android/Unity和各種游戲開發技巧,以及各種資…

Qt界面卡住變慢的解決方法

本質原因: 當Qt界面出現卡頓或無響應時&#xff0c;通常是因為主線程&#xff08;GUI線程&#xff09;被耗時操作阻塞。 完全忘了。。。 Qt Creater解決方法 1. 定位耗時操作 目標&#xff1a;找到阻塞主線程的代碼段。 方法&#xff1a; 使用QElapsedTimer測量代碼執行時間…

【LangChain4j快速入門】5分鐘用Java玩轉GPT-4o-mini,Spring Boot整合實戰!| 附源碼

【LangChain4j快速入門】5分鐘用Java玩轉GPT-4o-mini&#xff0c;Spring Boot整合實戰&#xff01; 前言&#xff1a;當Java遇上大模型 在AI浪潮席卷全球的今天&#xff0c;Java開發者如何快速擁抱大語言模型&#xff1f;LangChain4j作為專為Java打造的AI開發框架&#xff0c…

Vue 3 reactive 和 ref 區別及 失去響應性問題

在 Vue 3 中&#xff0c;reactive 和 ref 是實現響應式數據的兩個核心 API&#xff0c;它們的設計目標和使用場景有所不同。以下是兩者的詳細對比&#xff1a; 1. 基本定義與核心功能 特性reactiveref作用創建對象類型的響應式代理&#xff08;對象、數組、Map 等&#xff09…

第一節:Vben Admin 最新 v5.0初體驗

系列文章目錄 基礎篇 第一節&#xff1a;Vben Admin介紹和初次運行 第二節&#xff1a;Vben Admin 登錄邏輯梳理和對接后端準備 第三節&#xff1a;Vben Admin登錄對接后端login接口 第四節&#xff1a;Vben Admin登錄對接后端getUserInfo接口 第五節&#xff1a;Vben Admin權…

Nginx部署spa單頁面的小bug

沒部署過&#xff0c;都是給后端干的&#xff0c;自己嘗試部署了一個下午終于成功了 我遇到的最大的bug是進入后只有首頁正常顯示 其他頁面全是404&#xff0c;于是問問問才知道&#xff0c;需要這個 location / { try_files $uri $uri/ /index.html; } 讓…

面試算法高頻08-動態規劃-01

動態規劃 遞歸知識要點 遞歸代碼模板&#xff1a;提供遞歸代碼的標準形式public void recur(int level, int param) &#xff0c;包含終止條件&#xff08;if (level> MAX_LEVEL)&#xff09;、當前層邏輯處理&#xff08;process(level, param)&#xff09;、向下一層遞歸…

若依框架前后端分離版部署全流程詳解(本地+服務器+高級配置)

若依框架前后端分離版部署全流程詳解&#xff08;本地服務器高級配置&#xff09; 若依&#xff08;RuoYi&#xff09;作為一款基于SpringBoot和Vue的權限管理系統&#xff0c;憑借其模塊化設計和開箱即用的特性廣受開發者歡迎。本文將從本地部署、服務器部署、高級配置三個維…

醫療設備預測性維護合規架構:從法規遵循到技術實現的深度解析

在醫療行業數字化轉型加速推進的當下&#xff0c;醫療設備預測性維護已成為提升設備可用性、保障醫療安全的核心技術。然而&#xff0c;該技術的有效落地必須建立在嚴格的合規框架之上。醫療設備直接關乎患者生命健康&#xff0c;其維護過程涉及醫療法規、數據安全、質量管控等…

LLMs基礎學習(七)DeepSeek專題(4)

LLMs基礎學習&#xff08;七&#xff09;DeepSeek專題&#xff08;4&#xff09; 文章目錄 LLMs基礎學習&#xff08;七&#xff09;DeepSeek專題&#xff08;4&#xff09;DeepSeek-R1 訓練過程的四個階段具體流程小結 “規則化獎勵”具體原因小結 “自我認知”&#xff08;se…

SQL 速查手冊

前言&#xff1a;SQL&#xff08;Structured Query Language&#xff09;是用于管理關系型數據庫的標準語言&#xff0c;廣泛應用于數據查詢、更新、定義和管理等操作。本文將為你提供一份詳細的 SQL 速查手冊&#xff0c;涵蓋從基礎到高級的各種 SQL 操作&#xff0c;幫助你快…

IDEA 中 Scala 項目遠程連接虛擬機 Spark 環境

IDEA 中 Scala 項目遠程連接虛擬機 Spark 環境 1. 環境準備 確保虛擬機 Spark 環境正常運行 虛擬機中已安裝并啟動 Spark記錄虛擬機的 IP 地址和 Spark 端口&#xff08;默認 7077&#xff09;確保虛擬機防火墻允許相關端口訪問 本地 IDEA 環境配置 安裝 Scala 插件安裝 Spar…

.net core 項目快速接入Coze智能體-開箱即用-全局說明

目錄 一、Coze智能體的核心價值 二、開箱即用-效果如下 三 流程與交互設計 為什么要分析意圖&#xff0c;而不是全部交由AI處理。 四 接入前的準備工作 五&#xff1a;代碼實現----字節Coze 簽署 JWT和獲取Token .net core 項目快速接入Coze智能體-開箱即用 .net core快…

網店運營精細化突破新路徑

內容概要 電商戰場越來越卷&#xff0c;單純靠低價和流量轟炸已經玩不轉了。今天想要站穩腳跟&#xff0c;精細化運營才是破局密碼——從商品怎么選、用戶怎么留&#xff0c;到供應鏈怎么跑得更快&#xff0c;每個環節都得摳細節。比如用數據給選品“開天眼”&#xff0c;把用…

數據結構學習筆記 :線性表的鏈式存儲詳解

目錄 單鏈表 1.1 無頭單鏈表 1.2 有頭單鏈表單向循環鏈表雙鏈表 3.1 雙鏈表 3.2 雙向循環鏈表總結與對比 一、單鏈表 1. 無頭單鏈表&#xff08;Headless Singly Linked List&#xff09; 定義&#xff1a;鏈表無頭結點&#xff0c;直接由頭指針指向第一個數據節點。 特點&…

數據庫10(代碼相關語句)

while循環 declare avgprice numeric(10,2) set avgprice(select avg(price)from titles) //自定義參數 while avgprice<10 //循環條件 begin update titles set priceprice*1.1 end //循環語句操作&#xff0c;當avgprice<10,所有price都加0.1 case語句 查詢authors表…

Redis 下載與安裝(Windows版)

一、下載 1、redis官網&#xff1a; https://redis.io/downloads/ 2、Github下載地址&#xff1a; https://github.com/MicrosoftArchive/redis/releases 二、安裝 1、打開一個命令窗口&#xff0c;通過 cd 命令進入到你解壓的目錄 2、輸入命令 &#xff0c;啟動 Redis&…