linux休眠唤醒流程,及示例分析

休眠流程

应用层通过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_DEBUGif (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_suspendif (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;
}

两种阻止进入休眠

最终都是通过__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_awakepm_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;}

修改后系统可被正常唤醒

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2979201.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

ESP32-Thonny 拍摄图片到SD卡

前言&#xff1a; 代码运行在Thonny 添加main.py到单片机中&#xff1a; 可以先运行一下试试&#xff1a;会输出以下信息&#xff1a; 没有问题的话&#xff08;SD卡挂载成功&#xff0c;摄像头初始化成功&#xff09;运行一次主程序后&#xff0c;闪光灯会闪烁一下。 代码&…

js获取某月往前推一年或半年的年月数组

前言 需求&#xff1a;需要显示某月份往前推一年或者半年的费用情况&#xff0c;显示到柱形图上&#xff0c;后台接口只返回有数据的年份&#xff0c;这就需要前端拿全部月份数组去比对并显示。 开始 上代码&#xff1a; // date:选择的月份,比如:2024-04,//n:半年或者1年,…

PTA 天梯赛 L1-010 比较大小【C++】 L1-011 A-B 【C++ vector动态数组】【Python 字符串replace函数】

L1-010 比较大小 判断顺序很重要 #include<iostream> using namespace std; int main() {int a, b, c;cin >> a >> b >> c;int temp;if (a > b) {temp a;a b;b temp;}if (a > c) {temp a;a c;c temp;}if (b > c) {temp b;b c;c te…

C++ //练习 13.17 分别编写前三题中所描述的numbered和f,验证你是否正确预测了输出结果。

C Primer&#xff08;第5版&#xff09; 练习 13.17 练习 13.17 分别编写前三题中所描述的numbered和f&#xff0c;验证你是否正确预测了输出结果。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*************************…

【御控物联网平台】物联网平台常见通讯协议

随着物联网&#xff08;InternetofThings&#xff0c;IoT&#xff09;的快速发展&#xff0c;越来越多的设备和传感器连接到网络&#xff0c;使得数据的传递和交互变得更加智能化和高效化。在实现这种智能化和高效化的数据交互&#xff0c;过程中&#xff0c;各种不同的通信协议…

防火墙技术基础篇:什么是防火墙

防火墙技术基础篇&#xff1a;什么是防火墙 什么是防火墙&#xff1f; 在我们开始之前&#xff0c;让我们先想象一下一个真实的场景。你的家是你的私人领地&#xff0c;你不希望任何未经许可的人进入。为了保护你的家&#xff0c;你可能会安装一些安全设备&#xff0c;比如门…

webpack中mode、NODE_ENV、DefinePlugin、cross-env的使用

本文讲的全部知识点&#xff0c;都是和webpack相关的。如果你之前有疑问&#xff0c;那本文一定能帮你搞清楚。 问题来源一般是类似下面代码&#xff08;webpack.json中&#xff09;&#xff1a; "scripts": {"dev": "cross-env NODE_ENVdevelopmen…

Kafak详解(1)

简介 消息队列 为什么要有消息队列 图-1 消息队列的使用 消息队列 1)消息Message&#xff1a;网络中的两台计算机或者两个通讯设备之间传递的数据。例如说&#xff1a;文本、音乐、视频等内容。 2)队列Queue&#xff1a;一种特殊的线性表(数据元素首尾相接)&#xff0c;特…

DSView Windows平台编译

在Windows平台编译开源逻辑分析仪软件DSView&#xff0c;因官方没有公布DSView Windows平台源码&#xff0c;主要解决Windows平台以下问题&#xff1a; libusb_get_pollfds不支持Windows平台&#xff0c;导致无法采集数据插入设备后&#xff0c;无法自动识别设备&#xff0c;U…

全新创维EV6 Ⅱ超充车型有哪些亮点?答案将在北京车展揭晓

4月25日-5月4日&#xff0c;阔别4年的北京车展盛大回归&#xff0c;创维汽车将携全新车型创维EV6 Ⅱ超充车型及系列创新技术&#xff0c;亮相北京中国国际展览中心顺义馆W3号馆 311展位&#xff0c;全面展示其在新能源汽车补能及智能化领域的前瞻思考和技术布局。 以顶级超充革…

YOLOV5检测+追踪使用deepstream部署(c++版)

文章目录 一、Deepstream1.1 简介1.2 图架构&#xff08;Graph architecture&#xff09;1.3 应用架构&#xff08;Application Architecture&#xff09; 二、配置文件方式运行Deepstream2.1 环境准备2.2 主机运行2.3 配置文件解析2.4 docker运行 三、代码方式运行Deepstream3…

2024年电气工程与能源、动力国际学术会议(IACEEEP 2024)

2024年电气工程与能源、动力国际学术会议(IACEEEP 2024) 2024 International Conference on Electrical Engineering and Energy, Power 一、【会议简介】 2024年电气工程与能源、动力国际学术会议&#xff0c;精彩纷呈&#xff0c;不容错过&#xff01; 在这个备受瞩目的会议…

中兴5G随身wifi怎么样?中兴5G随身wifiVS格行5G随身wifi对比测评!公认最好的随身WiFi的格行随身WiFi真实测评!随身WiFi哪个品牌好?

随着各大品牌5G随身wifi的横空出世&#xff0c;其中中兴和格行5G随身wifi的呼声越来越高&#xff0c;那么性能上谁更胜一筹&#xff1f;套餐费用谁更亲民&#xff1f;售后保障谁更到位&#xff1f;今天就来一个全方位测评对比&#xff01; 一&#xff0c;首先是设备的整体外观&…

数新大数据平台迁移解决方案

随着企业的发展和数字化转型的不断深入&#xff0c;企业数据平台建设过去很多年&#xff0c;技术和架构过于落后&#xff0c;原有的大数据平台越来越难以满足业务需求。而在新的技术架构大数据平台的升级过程中&#xff0c;对数据和任务迁移的一致性、完整性有很高的要求&#…

虚拟信用卡是什么,可以用来开亚马逊店铺吗?

虚拟信用卡是什么&#xff1f; 虚拟信用卡就是一组由银行随机生成的数字的虚拟卡&#xff0c;使用起来方便快捷&#xff0c;对于个人而言保守自己的隐私&#xff0c;并且下卡快&#xff0c;即开即用 可以用来开亚马逊店铺吗&#xff1f; 可以&#xff0c;因为市场的需求很多…

揭秘APP开发者如何靠广告变现赚大钱!

在移动应用的海洋中&#xff0c;如何让我们的应用在众多竞争对手中脱颖而出&#xff0c;实现收益的最大化&#xff1f;这无疑是每一个应用开发者都关心的问题。广告变现模式为我们提供了答案。接下来&#xff0c;让我们一起探讨一下如何有效利用广告变现策略以增加应用收益。&a…

裸金属服务器和物理机有什么区别

今天&#xff0c;在我们生活的世界中&#xff0c;技术已经彻底改变了我们的生活。在开展在线业务时&#xff0c;服务器在快速高效地执行多项任务方面发挥了极其重要的作用。然而&#xff0c;很多人仍然对裡金属服务器和物理机感到很困惑。今天就给大家分析一下裡金属服务器和物…

React首次加载渲染2次的问题

在开发React项目的时候&#xff0c;发现useEffect会调用2次的情况&#xff0c;依赖数组明明没有变化&#xff0c;怎么会调用2次&#xff1f;百思不得其解&#xff0c;依赖没变化的话&#xff0c;那肯定是整个组件重渲染了。 最最简单的代码如下&#xff1a; const container …

把 WordPress 变成 BaaS 服务:API 调用指南

有了前面两篇内容的铺垫&#xff0c;我们来聊聊 WordPress 作为 CMS / BaaS 服务使用时绕不开的问题&#xff0c;API 调用。 这篇内容同样的&#xff0c;会尽量少贴代码&#xff0c;简单的讲清楚一件事&#xff0c;降低阅读负担。 写在前面 首先&#xff0c;我们需要进行清晰…

【支付宝】对接手机网站支付踩坑点记录

前言 简单记录一下对接Wap支付的问题&#xff0c;alipay和wxpay认证过程差不多&#xff0c;有个体商户或企业即可&#xff0c;前者文档不易懂后者还好&#xff0c;但是wxpay门槛高&#xff0c;个人认为pc网站支付(native支付)就是为了收300认证费&#xff01; 应用公私钥 第一…