五、suspend和resume代码走读
下面对suspend分的几个阶段都是按照pm test的5中模式来划分的:freezer、devices、platform、processors、core。
suspend第一阶段:freezer
int enter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if (!mutex_trylock(&pm_mutex)) // def in kernel/kernel/power/main.c
return -EBUSY;
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n"); // 同步文件系统
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
error = suspend_prepare();
// suspend前准备工作:Run suspend notifiers, allocate a console and stop all processes
if (error) // 如果一些准备工作失败,通常为冻结进程的时候某些进程拒绝进入冻结模式
goto Unlock; // 释放锁,然后退出
if (suspend_test(TEST_FREEZER))
// 检查上层下达的命令是否是:
// echo freezer > /sys/power/pm_test
// echo mem > /sys/power/state
// 是的话,延时5s后,然后做一些解冻进程等工作就返回
goto Finish;
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
error = suspend_devices_and_enter(state); // 休眠外设
Finish:
pr_debug("PM: Finishing wakeup.\n");
suspend_finish(); // 解冻进程,发广播通知等
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
static int suspend_prepare(void)
{
int error;
if (!suspend_ops || !suspend_ops->enter) // mtk_pm_enter() in mtkpm.c
return -EPERM;
pm_prepare_console(); //给suspend分配一个虚拟终端来输出信息
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
// 广播一个通知给通知链pm_chain_head,该通知链上都是用函数register_pm_notifier()注册的pm通知项(也可以用宏pm_notifier定义和注册),这里按照注册时候的定义的优先级来调用该通知链上注册的回调函数。
// PM_SUSPEND_PREPARE是事件值,表示将要进入suspend
if (error)
goto Finish;
error = usermodehelper_disable(); // 关闭用户态的helper进程
if (error)
goto Finish;
error = suspend_freeze_processes();
// 冻结所有的进程, 这里会保存所有进程当前的状态。
if (!error)
return 0;
// 也许有一些进程会拒绝进入冻结状态, 当有这样的进程存在的时候, 会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程.
suspend_thaw_processes(); // 进程解冻
usermodehelper_enable(); // enable helper process
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND); // 广播退出suspend的通知
pm_restore_console();
return error;
}
// 如果不支持pm debug的话,该函数直接返回0
static int suspend_test(int level)
{
#ifdef CONFIG_PM_DEBUG
if (pm_test_level == level) {
printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
mdelay(5000);
return 1;
}
#endif /* !CONFIG_PM_DEBUG */
return 0;
}
static void suspend_finish(void)
{
suspend_thaw_processes();
usermodehelper_enable();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
同步文件系统函数sys_sync()调用后,将会执行函数suspend_prepare()来做一些进入suspend之前的准备工作:Run suspend notifiers, allocate a console and stop all processes,disable user mode hleper process。之后将会调用suspend_test(TEST_FREEZER)来判断上层是否有下这样的命令下来:echo freezer > /sys/power/pm_test(如果pm debug支持),如果没有下这个命令或者系统根本就不支持pm debug,那么直接返回0。如果该测试函数返回了1,那么就不会继续往下走suspend的流程了,而是调用函数suspend_finish()来结束freezer模式的pm test。返回0,将会进入第二阶段继续suspend的流程。
suspend第二阶段:devices
后续所有对suspend划分的阶段都包含在函数suspend_devices_and_enter(state)之中,所以这个函数是关键所在。
int suspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
// 一个重要的函数指针结构体,特定平台的不一样,
// kernel/arch/arm/mach-mt6516/mtkpm.c
return -ENOSYS;
if (suspend_ops->begin) {
error = suspend_ops->begin(state);
// 调用特定平台实现的suspend_begin函数,
// 这里没做什么实际的工作,打印了点字符串
if (error)
goto Close; // 如果有错,执行特定平台的suspend_end函数
}
suspend_console(); // suspend console subsystem
suspend_test_start(); // suspend devices超时警告测试
error = dpm_suspend_start(PMSG_SUSPEND); // suspend all devices, 外设休眠
// 关键函数之一, kernel/drivers/base/power/main.c
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend\n");
goto Recover_platform; // 如果外设休眠过程出现错误,将会终止suspend过程,直接跳到某标签出resume外设
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES)) // suspend第二阶段以此为界
goto Recover_platform;
suspend_enter(state); // 关键函数之一, pm test的后三种模式都在该函数中进行测试:platform、cpus、core
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume devices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover(); // 该函数目前的平台没有实现
goto Resume_devices;
}
@kernel/drivers/base/power/main.c
int dpm_suspend_start(pm_message_t state)
{
int error;
might_sleep();
error = dpm_prepare(state);
if (!error)
error = dpm_suspend(state); // 这两个函数的架构有一些类似
return error;
这两个函数如果在执行某一个device的->prepare()和->suspend回调函数的时候出错,都将会直接跳出返回错误码,不理会后续devices。
}
static int dpm_prepare(pm_message_t state)
{
struct list_head list;
int error = 0;
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx); // 锁住dpm_list链表
transition_started = true; // device_pm_add()中使用
while (!list_empty(&dpm_list)) { // dpm_list上挂着所有的devices
// 关于device中的这部分内容,参考文档:
// 新版linux系统设备架构中关于电源管理方式的变更.txt
struct device *dev = to_device(dpm_list.next); // 取得对应的device结构体
get_device(dev);
dev->power.status = DPM_PREPARING;
// 将该设备的电源状态设置成DPM_PREPARING,表示该设备正准备着进入相应的省电模式,这之后就会调用和该设备有关的所以的-->prepare函数
mutex_unlock(&dpm_list_mtx);
pm_runtime_get_noresume(dev);
// 电源管理新方式中比较复杂的用法,这里没有使用到,所以直接跳过
if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
/* Wake-up requested during system sleep transition. */
pm_runtime_put_noidle(dev);
error = -EBUSY;
} else {
error = device_prepare(dev, state);
// 这个才是真正执行-->prepare回调函数的地方
}
mutex_lock(&dpm_list_mtx);
if (error) {
dev->power.status = DPM_ON;
if (error == -EAGAIN) { // try again
put_device(dev);
error = 0;
continue;
}
printk(KERN_ERR "PM: Failed to prepare device %s "
"for power transition: error %d\n",
kobject_name(&dev->kobj), error);
put_device(dev);
break;
}
dev->power.status = DPM_SUSPENDING;
// 这之后就不能以它为父设备再注册设备了,可以从函数device_pm_add
// 中看出来
if (!list_empty(&dev->power.entry))
list_move_tail(&dev->power.entry, &list);
// 为了该函数中循环链表之用, list_empty(&dpm_list)
put_device(dev);
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
return error;
}
从这个函数我们可以发现,每一个device的dev->power.status状态都会有如下轨迹的变化:DPM_ON(device刚刚注册完的初始状态) --> DPM_PREPARING --> DPM_SUSPENDING --> 未完待续...
static int device_prepare(struct device *dev, pm_message_t state)
{
int error = 0;
down(&dev->sem);
// dev->bus绝大多数都存在,dev->bus->pm这个就不一定了,不过platform bus一定存在,而i2c bus就没有,dev->bus->pm->prepare这个函数如果存在就会被后面给调用到,以platform bus为例,该函数的实现完全回去调用device对应的driver->pm->prepare()函数(如果存在的话),当然driver->pm->prepare()不存在就什么也不做了 。
if (dev->bus && dev->bus->pm && dev->bus->pm->prepare) {
pm_dev_dbg(dev, state, "preparing ");
error = dev->bus->pm->prepare(dev);
suspend_report_result(dev->bus->pm->prepare, error);
if (error)
goto End;
}
// dev->type这个很多设备都有,但是dev->type->pm这个却很少有了
if (dev->type && dev->type->pm && dev->type->pm->prepare) {
pm_dev_dbg(dev, state, "preparing type ");
error = dev->type->pm->prepare(dev);
suspend_report_result(dev->type->pm->prepare, error);
if (error)
goto End;
}
// dev->class很少有设备有
if (dev->class && dev->class->pm && dev->class->pm->prepare) {
pm_dev_dbg(dev, state, "preparing class ");
error = dev->class->pm
分享到:
相关推荐
综上所述,《Linux内核分析》一书从Linux内核的历史背景、开发模式、结构特征、硬件基础、中断机制以及进程管理等方面,提供了深入浅出的讲解,对于想要深入了解Linux内核工作原理的读者来说,是一本不可多得的技术...
特色平台前端linu移植项目可行性分析报告.pdf
1. **数字电路与模拟电路**:理解数字电路的基本原理,掌握模拟电路的工作机制。 2. **计算机组成原理**:熟悉计算机系统的各个组成部分及其工作原理。 3. **微机原理与接口技术**:掌握微型计算机的基本原理及接口...
Linu系统安全配置标准V.pdf
Linux内核QoS(服务质量)实现机制是网络管理和优化的关键组成部分,它确保了不同类型的网络服务得到合理且优先级适中的资源分配。QoS的主要目标是通过控制带宽、延迟、丢包率和抖动来提升网络性能和用户体验。在...
这份"Linu系统安全配置标准.pdf"文档提供了一系列针对Linux服务器的安全加固技术要求,以增强系统的稳定性和防止未授权访问。以下是根据文档内容提炼出的关键知识点: 1. **补丁管理**: - **系统补丁**:建议定期...
1. **掌纹识别(Palm Recognition)**:这是一种生物特征识别技术,通过分析和比较个人的掌纹特征来进行身份验证。掌纹具有独特的纹理、线形结构和细节,使其成为生物识别系统中的一个可靠选项。在实现时,通常需要...
### Linu系统配置DNS服务器知识点详解 #### 一、实验环境 - **操作系统**: CentOS 6.4 - **DNS服务器软件**: BIND (Berkeley Internet Name Domain) #### 二、DNS服务器配置概述 DNS服务器(Domain Name System)...
总的来说,这个"linu下cadence的patch包"是一个用于提升Linux环境下Cadence工具性能和稳定性的更新,对于那些依赖Cadence进行复杂电路设计的专业人士来说,定期检查和应用这些补丁是保持工作效率和数据安全的重要...
支持qt和linu通信的qssh库
解决方案:通过分析系统负载和进程状态,找出 CPU 使用率过高的原因。如果是由于高负载应用导致的,可以优化应用代码或增加服务器资源。 * 问题 2:内存泄漏 解决方案:通过内存检测工具找出内存泄漏的原因,并进行...
"奔跑吧"可能代表了作者希望读者能积极主动地深入学习和理解Linux内核,而“基于Linux 4.x内核源代码问题分析”则表明该书将详细探讨Linux 4.x版本的内核源代码,帮助读者理解内核的工作原理和解决实际问题。...
"Windows客户端访问Linu服务器NFS" 本文档主要讲解Windows客户端如何访问Linux服务器的NFS(Network File System),以解决异构环境中的文件共享问题。文中介绍了跨平台通讯工具SFU(Services For Unix),它能够...
Elasticsearch是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。 Logstash 主要是...
#### 三、支持的语言标准 GCC对于各种编程语言的支持不仅限于语法层面,还包括遵循不同的语言标准。 - **C语言**:GCC支持从C90到C11的多个版本的标准。 - **C++语言**:GCC支持从C++98到C++20的多个版本的标准。 - ...
myself linu x(2)myself linu x(2)myself linu x(2)myself linu x(2)myself linu x(2)
《Linux0.01内核分析与操作系统设计》介绍LINUX0.01的内核,0.01是拥有比较少的代码,对于初学者来说容易理解。(共4个文件,第1个文件扣分,其余不扣。4/4)
linu下安装websphere手册,一目了然