关于s5pc110系统休眠的情景分析(1)
2011-11-04 11:59
288 查看
原文地址:http://hi.baidu.com/flydownup/blog/item/490848d1b3da7514a08bb739.html
关于系统休眠的情景分析
看文件kernel/power/main.c
此文件中定义了几个宏
power_attr(state);
power_attr(pm_trace);
power_attr(wake_lock);
power_attr(wake_unlock);
power_attr(pm_test);
这个宏定义在头文件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, \
}
由此可见我们声明了五个结构体,struct kobj_attribute state_attr, pm_trace_attr, wake_lock_attr, wake_unlock_attr, pm_test_attr
我们看系统在初始化时会调用
static int __init pm_init(void)
{
int error = pm_start_workqueue(); //创建一个全局的工作队列struct workqueue_struct *pm_wq;
if (error)
return error;
power_kobj = kobject_create_and_add("power", NULL); //动态的创建一个struct kobject,并注册它在sysfs下,会在/sys/目录下生成 //一个power的目录
if (!power_kobj)
return -ENOMEM;
return sysfs_create_group(power_kobj, &attr_group);//创建属性组在power_kobj目录下,
}
我们看看attr_group的定义
struct attribute_group {
const char *name;
mode_t (*is_visible)(struct kobject *,
struct attribute *, int);
struct attribute **attrs;
};
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
#endif
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
&pm_test_attr.attr,
#endif
#ifdef CONFIG_USER_WAKELOCK
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
NULL,
};
static struct attribute_group attr_group = {
.attrs = g,
};
可见至少我们会在sys/power目录下生成一个名字为state的state_attr
根据不同的配置,还有可能生成名字为pm_trace, pm_test, wake_lock, wake_unlock的属性文件
读写这些attribute的话就会调用相应attribute的show和store函数
call trace is like following:
sys_write -> sysfs_write_file -> flush_write_buffer -> sysfs_ops.store
(sysfs_ops在open一个sysfs文件的时候会设置,这里是kobj_sysfs_ops)
-> kobj_sysfs_ops.store (kobj_attr_store) -> state_store
可见按下power键,触发机器休眠时,上层会通过某种方式打开/sys/power/目录的属性文件,写相应的状态到文件,从而触发相应attribute的show和store函数
这里我们重点分析下state属性的show和store函数
回到文件/kernel/power/main.c中
首先通过cat /sys/power/state 可以调用state_show函数
下面我们看state_store函数
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
printk("++ %s\n", __func__);
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state = PM_SUSPEND_ON;
#else
suspend_state_t state = PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
printk("state:%d, n:%d ,buf:%s\n", state, n, buf);
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
/* First, check if we are requested to hibernate */
if (len == 4 && !strncmp(buf, "disk", len)) {
error = hibernate(); //如果向文件/sys/power/state写 disk 字串, 设备将进入hibernate模式,这种情况我们暂且不分析
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
printk("suspend , *s(%p):%s, buf:%s, len:%d\n", *s, *s?*s:"null", buf, len);
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break;
}
*********************************************************************************
遍历全局指针数组pm_states,定义如下
const char *const pm_states[PM_SUSPEND_MAX] = {
#ifdef CONFIG_EARLYSUSPEND
[PM_SUSPEND_ON] = "on",
#endif
[PM_SUSPEND_STANDBY] = "standby",
[PM_SUSPEND_MEM] = "mem",
};
从数组找到与上层传递过来的buf字串相匹配的项,假如写mem字串,那么此时,s指针会指向pm_states数组的字串mem,state值为相应字串对 应的宏PM_SUSPEND_MEM
**********************************************************************************
if (state < PM_SUSPEND_MAX && *s)
#ifdef CONFIG_EARLYSUSPEND
printk("earlysuspend, state:%d\n", state);
/*******************************************************
检测state值确定是否调用request_suspend_state
如果state为PM_SUSPEND_ON,即写on字串到属性文件时,必然调用request_suspend_state
如果写了其他的字串,比如standby或者mem,或其他字串,则需要进一步判断,通过函数bool valid_state
此函数定义在文件/kernel/power/suspend.c中
bool valid_state(suspend_state_t state)
{
/*
* All states need lowlevel support and need to be valid to the lowlevel
* implementation, no valid callback implies that none are valid.
*/
return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}
这个函数会检测静态的结构体static struct platform_suspend_ops *suspend_ops是否初始化,并且其成员valid是否
存在,然后调用相应的检测函数去检测state状态
这里我们以我们的s5pc110为例说明一下
suspend_ops将会被初始化指向s3c_pm_ops,这个结构体定义在文件/arch/arm/plat-samsung/pm.c
我们看看s3c_pm_ops的定义
static struct platform_suspend_ops s3c_pm_ops = {
#ifdef CONFIG_REGULATOR
.begin = s3c_pm_begin,
#endif
.enter = s3c_pm_enter,
.prepare = s3c_pm_prepare,
.finish = s3c_pm_finish,
.valid = suspend_valid_only_mem,
};
这个设置过程是系统在初始化时通过调用s3c_pm_init()函数是设置的
int __init s3c_pm_init(void)
{
printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");
#ifdef USE_DMA_ALLOC
regs_save = dma_alloc_coherent(NULL, 4096, &phy_regs_save, GFP_KERNEL);
if (regs_save == NULL) {
printk(KERN_ERR "DMA alloc error\n");
return -1;
}
#endif /* USE_DMA_ALLOC */
suspend_set_ops(&s3c_pm_ops); //此处决定了suspend_ops的初始化值
return 0;
}
注意到s3c_pm_ops的成员函数valid指向suspend_valid_only_mem,因此检测时会调用此函数
这个函数定义在文件/power/suspend.c中
int suspend_valid_only_mem(suspend_state_t state)
{
return state == PM_SUSPEND_MEM;
}
说明平台驱动只实现mem suspend,
只有在state为PM_SUSPEND_ON或者PM_SUSPEND_MEM的时候才会调用request_suspend_state
********************************************************/
if (state == PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
/*********************************************************************
我们进入函数request_suspend_state
void request_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
int old_sleep;
spin_lock_irqsave(&state_lock, irqflags);
old_sleep = state & SUSPEND_REQUESTED; //state记录suspend状态
if (debug_mask & DEBUG_USER_STATE) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
pr_info("request_suspend_state: %s (%d->%d) at %lld "
"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
requested_suspend_state, new_state,
ktime_to_ns(ktime_get()),
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}
if (!old_sleep && new_state != PM_SUSPEND_ON) {
state |= SUSPEND_REQUESTED;
queue_work(suspend_work_queue, &early_suspend_work);
} else if (old_sleep && new_
b30a
state == PM_SUSPEND_ON) {
state &= ~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
queue_work(suspend_work_queue, &late_resume_work);
}
requested_suspend_state = new_state; //requested_suspend_state记录请求suspend的状态
spin_unlock_irqrestore(&state_lock, irqflags);
}
根据old_sleep和new_state的取值,产生了两种情况,我们先分析第一种情况:
这里如果new_state是PM_SUSPEND_MEM,那么更新state,表明已经有suspend请求,suspend_work_queue是一个全局的struct workqueue_struct *suspend_work_queue; 将early_suspend_work插入工作队列,我们看看early_suspend_work的定义
kernel/power/earlysuspend.c
static DECLARE_WORK(early_suspend_work, early_suspend);
include/linux/workqueue.h
#define DECLARE_WORK(n, f) \
struct work_struct n = __WORK_INITIALIZER(n, f)
#define __WORK_INITIALIZER(n, f) { \
.data = WORK_DATA_INIT(), \
.entry = { &(n).entry, &(n).entry }, \
.func = (f), \
__WORK_INIT_LOCKDEP_MAP(#n, &(n)) \
}
这里early_suspend是类型是void (*)(struct work_struct *work),将这个函数地址赋值给early_suspend_work结构的func成员
再将early_suspend_work插入到suspend_work_queue中
执行early_suspend函数
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/power/earlysuspend.c
static void early_suspend(struct work_struct *work)
{
struct early_suspend *pos;
unsigned long irqflags;
int abort = 0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock, irqflags); //保护全局变量state的自旋锁,屏蔽中断,禁止抢占
if (state == SUSPEND_REQUESTED)
state |= SUSPENDED; //如果有SUSPEND_REQUESTED,那么改变state
else
abort = 1;
spin_unlock_irqrestore(&state_lock, irqflags);
if (abort) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: abort, state %d\n", state);
mutex_unlock(&early_suspend_lock);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: call handlers\n");
list_for_each_entry(pos, &early_suspend_handlers, link) { //遍历early_suspend_handlers中结构体struct //early_suspend的成员
if (pos->suspend != NULL)
pos->suspend(pos);
}
mutex_unlock(&early_suspend_lock);
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: sync\n");
sys_sync();
#ifdef CONFIG_S5P_LPAUDIO
if (has_audio_wake_lock()) {
printk("******************* Enter LP-Audio mode\n");
#ifdef CONFIG_CPU_FREQ_S5PV210
s5pv210_set_cpufreq_level(MAXIMUM_TABLE);
#endif /* CONFIG_CPU_FREQ_S5PV210 */
s5p_setup_lpaudio(LPAUDIO_MODE);
previous_idle_mode = LPAUDIO_MODE;
}
#endif /* CONFIG_S5P_LPAUDIO */
abort:
spin_lock_irqsave(&state_lock, irqflags);
if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);
spin_unlock_irqrestore(&state_lock, irqflags);
}
这个函数主要的任务是遍历链表early_suspend_handlers,访问结构体struct early_suspend的suspend成员函数,这个链表通过
register_early_suspend函数注册
我们的平台中分别调用了tp中的early_suspend.suspend函数hj_touch_early_suspend
console_early_suspend,
s5p_tv_early_suspend,
s3cfb_early_suspend
sys_sync系统调用用来将缓存中的数据写入块设备,sys_sync系统调用将buffer、inode和super在缓存中的数据写入设备
关于系统调用,我们需要看头文件syscalls.h
#define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(void)
在看文件fs/sync.c
SYSCALL_DEFINE0(sync)
{
wakeup_flusher_threads(0);
sync_filesystems(0);
sync_filesystems(1);
if (unlikely(laptop_mode))
laptop_sync_completion();
return 0;
}
展开宏SYSCALL_DEFINE0(sync)
得到asmlinkage long sys_sync(void)函数的定义
这个系统调用的英文注释为sync everything. Start out by waking pdflush, because that writes back
all queues in parallel.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
**********************************************************************/
#else
error = enter_state(state);
#endif
#endif
printk("-- %s\n", __func__);
Exit:
return error ? error : n;
}
退出state_store之后,系统休眠仍未真正完成休眠,如果插着USB到这里就结束了。
android利用wakelock机制实现了系统的休眠管理,我们暂且不讨论PowerManagerService的机制。
只是知道系统PowerManagerService机制会判断实现系统的真正休眠,
这时候就要调用power/wakelock.c中的static void suspend(struct work_struct *work)函数了
我们看这个函数
static void suspend(struct work_struct *work)
{
int ret;
int entry_event_num;
if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: abort suspend\n");
// printk("suspend: abort suspend\n");
return;
}
entry_event_num = current_event_num;
sys_sync(); //唤醒pdflush ,同步文件系统,将缓存中的数据写入块设备
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: enter suspend\n");
// printk("xxsuspend: enter suspend\n");
ret = pm_suspend(requested_suspend_state); //根据requested_suspend_state调用enter_state
******************************************************************************************
/**
* enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
*
* Make sure we're the only ones trying to enter a sleep state. Fail
* if someone has beat us to it, since we don't want anything weird to
* happen when we wake up.
* Then, do the setup for suspend, enter the state, and cleaup (after
* we've woken up).
*/
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();
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
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;
}
相关文章推荐
- 关于s5pc110系统休眠的情景分析(2)
- 关于s5pc110系统休眠的情景分析(PMIC为MAX8698)
- 关于s5pc110系统休眠的情景分析(2)
- 关于php-fpm占用系统资源分析
- SQL语句练习实例之五 WMS系统中的关于LIFO或FIFO的问题分析
- 关于需求分析与系统设计的文章
- 关于新闻系统的分析
- 关于嵌入式Linux系统的启动(很好的嵌入式linux启动过程分析)
- 关于Linux操作系统根文件系统的启动分析
- Android系统--Binder系统具体框架分析(二)Binder驱动情景分析
- 关于外部设备存储空间的地址映射--摘自情景分析
- linux中关于tmpfs文件系统资料的整理与分析
- 关于php-fpm占用系统资源分析
- 关于待机,休眠的经典问答正文分析错误
- 关于 Atmega128L 中系统参数存储在EEPROM中的位置分析
- linux 2.6源代码情景分析笔记之系统启动2
- 关于Linux系统指令 top 之 %si 占用高,分析实例一
- 关于嵌入式下系统自动kill进程的简单分析
- 关于Linux操作系统根文件系统的启动分析