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.mzph.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&…