Linux suspend&resume

这一节回顾一下以前学习的Linux的电源管理模块,整个流程如下:

1:内核中的相关配置

在3.4.43内核配置中,Linux/arm 3.4.43 Kernel Configuration:

Power management options  ---> 

 [*] Suspend to RAM and standby        //STR的支持

[*] Run-time PM core functionality 

 

2:用户的接口:sysfs接口,各个kobj_attribute

相关源码位于:kernel/power/main.c

static int __init pm_init(void)
{
 int error = pm_start_workqueue();                                         //如果使能CONFIG_PM_RUNTIME,这函数将分配一个workqueue,否则函数为NULL.
 if (error)
  return error;
 hibernate_image_size_init();                                                 //以下两个函数与hibernate有关,也就是suspend to disk.
 hibernate_reserved_size_init();
 power_kobj = kobject_create_and_add("power", NULL);   //建立一个名为"power"的kobject,并注册到sysfs
 if (!power_kobj)
  return -ENOMEM;
 return sysfs_create_group(power_kobj, &attr_group);  //在"power"的目录kobject下建立属性组。
}

重点在于attr_group, 看下面代码:

static struct attribute * g[] = {
 &state_attr.attr,
#ifdef CONFIG_PM_TRACE                                                     //没有打开
 &pm_trace_attr.attr,
 &pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP             //打开
 &pm_async_attr.attr,
 &wakeup_count_attr.attr,
#ifdef CONFIG_PM_DEBUG              //可以打开
 &pm_test_attr.attr,
#endif
#endif
 NULL,
};

static struct attribute_group attr_group = {
 .attrs = g,
};

power_attr(pm_async);

power_attr(pm_test);

power_attr(state);

power_attr(wakeup_count);

在kernel/power/power.h

#define power_attr(_name) \
static struct kobj_attribute _name##_attr = { \
 .attr = {    \
  .name = __stringify(_name), \
  .mode = 0644,   \
 },     \
 .show = _name##_show,   \
 .store = _name##_store,  \
}

展开这些宏,发现声明很多kobj_attribute, kobj_attribute在sysfs中呈现为一个文件,而kobject呈现为sysfs中的目录。读kobj_attribute的文件, kobj_attribute.show()将被调用,而写kobj_attribute的文件, kobj_attribute.store()将被调用,也就是说读写/sys/power下的attribute文件,相关的sho(),store()函数将会被调用。

3:各个kobj属性文件的分析

1):如果使能了CONFIG_PM_DEBUG,哪么将定义power_attr(pm_test);,创建sys/power/pm_test属性文件。

终端下执行命令:cat /sys/power/pm_test,最终会调用函数pm_test_show(),终端上将打印出所有PM的阶段模式,并用[]表示出当前设置了的测试模式。Linux下定义了PM以下6种level:[none] core processors platform devices freezer。

执行命令:echo platform > /sys/power/pm_test最终会调用函数pm_test_store(),设置要测试的PM level为platform。pm_test_store()函数内部将会使用pm_test_level记录要测试的level。在suspend过程中,如果所处level与当前测试的相等哪么就会在终端打印出suspend debug: Waiting for 5 seconds。

2);如果使能了CONFIG_PM_SLEEP,定义了power_attr(wakeup_count);及power_attr(pm_async)。创建sys/power/wakeup_count,及sys/power/pm_async属性文件。

其中wakeup_count是用于检测当前是否有唤醒的event,如果当前有唤醒event,正在进行的suspend将会中止,使用一般是先读出当前wakeup event个数,然后重新写入。pm_async属性文件是用于device的suspend/resume是否同步,默认值为1,设置0为不同步,其他值为同步。

3):最关键的一个属性文件:power_attr(state)。

对sys/power/state读,state_show()函数将被调用,显示当前系统支持电源模式,现在主要包括以下模式:standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and 'disk' (Suspend-to-Disk),其中嵌入式系统中一般都没有'disk'模式。

对sys/power/state写操作,例如echo mem  > /sys/power/state。如果所写的字符串是上面函数打出的所支持的模式,哪么将调用pm_suspend(),进入相应的模式。

state_show()函数内调用了一个函数valid_state();这个函数是平台配置的支持省电模式的验证函数,该函数定义于文件kernel/power/suspend.c

void suspend_set_ops(const struct platform_suspend_ops *ops)
{
 mutex_lock(&pm_mutex);
 suspend_ops = ops;
 mutex_unlock(&pm_mutex);
}

bool valid_state(suspend_state_t state)
{
 return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}

实际的平台级代码将会调用suspend_set_ops()函数设置板级的struct platform_suspend_ops *ops,该结构体一般在文件arch/arm/mach-XXX/pm.c

struct platform_suspend_ops {
 int (*valid)(suspend_state_t state);
 int (*begin)(suspend_state_t state);
 int (*prepare)(void);
 int (*prepare_late)(void);
 int (*enter)(suspend_state_t state);
 void (*wake)(void);
 void (*finish)(void);
 void (*end)(void);
 void (*recover)(void);
};
从函数分析得出休眠唤醒过程将会依执行平台级所注册的struct platform_suspend_ops结构体中的函数:begin,prepare,prepare_late,enter,wake,finish,end,recover。休眠的时候代码执行停留在函数enter,唤醒就是从停留的地方继续执行end。recover只有在pm_test设置为"devices"才执行。

 

4:pm_suspend()函数分析

suspend按pm_test可分为5个阶段:freezer,devices,platform,cups,core。

suspend代码位于:kernel/power/suspend.c,以下是解读代码:

freezer阶段:

static int enter_state(suspend_state_t state)
{
 int error;

 if (!valid_state(state))
  return -ENODEV;

 if (!mutex_trylock(&pm_mutex))
  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();                         //进备进入系统休眠状态,属于第一阶段freezer,主要完成工作:准备consol,调用PM通知链状态为PM_SUSPEND_PREPARE,freeze所有进程。
 if (error)                                    //准备失败,通常为freeze进程的时候某些进程拒绝进入冻结模式。
  goto Unlock;

 if (suspend_test(TEST_FREEZER))                     //测试pm_test,如果为TEST_FREEZER,就延时5S.
  goto Finish;

 pr_debug("PM: Entering %s sleep\n", pm_states[state]);
 pm_restrict_gfp_mask();
 error = suspend_devices_and_enter(state);                                                                   //进入其他阶段。
 pm_restore_gfp_mask();

 Finish:
 pr_debug("PM: Finishing wakeup.\n");
 suspend_finish();                                                                                                                    //解决进程,发通知链。
 Unlock:
 mutex_unlock(&pm_mutex);
 return error;
}

 

device阶段:

int suspend_devices_and_enter(suspend_state_t state)
{
 int error;
 bool wakeup = false;

 if (!suspend_ops)
  return -ENOSYS;

 trace_machine_suspend(state);
 if (suspend_ops->begin) {
  error = suspend_ops->begin(state);
  if (error)
   goto Close;
 }
 suspend_console();                                                                                                 //suspend_console - suspend the console subsystem
 ftrace_stop();
 suspend_test_start();                                                                                               //记录suspend开始时间
 error = dpm_suspend_start(PMSG_SUSPEND);                                               //Prepare devices for PM transition and suspend them.休眠所有外设,device阶段关键函数。下一次分析相关的数据类型。
 if (error) {
  printk(KERN_ERR "PM: Some devices failed to suspend\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
  && suspend_ops->suspend_again && suspend_ops->suspend_again());

 Resume_devices:
 suspend_test_start();
 dpm_resume_end(PMSG_RESUME);
 suspend_test_finish("resume devices");
 ftrace_start();
 resume_console();
 Close:
 if (suspend_ops->end)
  suspend_ops->end();
 trace_machine_suspend(PWR_EVENT_EXIT);
 return error;

 Recover_platform:
 if (suspend_ops->recover)
  suspend_ops->recover();
 goto Resume_devices;
}

 

platform,cups,core阶段:

static int suspend_enter(suspend_state_t state, bool *wakeup)
{
 int error;

 if (suspend_ops->prepare) {
  error = suspend_ops->prepare();
  if (error)
   goto Platform_finish;
 }

 error = dpm_suspend_end(PMSG_SUSPEND);
 if (error) {
  printk(KERN_ERR "PM: Some devices failed to power down\n");
  goto Platform_finish;
 }

 if (suspend_ops->prepare_late) {               
  error = suspend_ops->prepare_late();            //如有,调用板级代码 platform
  if (error)
   goto Platform_wake;
 }

 if (suspend_test(TEST_PLATFORM))                    
  goto Platform_wake;

 error = disable_nonboot_cpus();                                                            //cpu
 if (error || suspend_test(TEST_CPUS))
  goto Enable_cpus;

 arch_suspend_disable_irqs();                                                        
 BUG_ON(!irqs_disabled());

 error = syscore_suspend();
 if (!error) {
  *wakeup = pm_wakeup_pending();
  if (!(suspend_test(TEST_CORE) || *wakeup)) {
   error = suspend_ops->enter(state);                                              //板级代码,进入真正的休眠,代码的运行将停在这里,直到唤醒。
   events_check_enabled = false;
  }
  syscore_resume();                                                                            //resume
 }

 arch_suspend_enable_irqs();
 BUG_ON(irqs_disabled());

 Enable_cpus:
 enable_nonboot_cpus();

 Platform_wake:
 if (suspend_ops->wake)
  suspend_ops->wake();

 dpm_resume_start(PMSG_RESUME);

 Platform_finish:
 if (suspend_ops->finish)
  suspend_ops->finish();

 return error;
}

 

5:device设备模型中电源管理部分

有两个电源管理相关的结构体dev_pm_info,dev_pm_domain,定义于kernel/include/linux/pm.h。这两个结构广泛嵌入于linux设备模型的结构体:struct device, struct device_type, struct class, struct class, struct device_driver, struct bus_type.

其中device中的struct dev_pm_info power, 将设备纳入电源管理范围,记录电源管理的相关信息,在注册设备的时候调用device_add()向sysfs添加power接口和注册电源管理系统。关键函数有dpm_sysfs_add(),device_pm_add();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值