分类: LINUX
2011-04-23 15:31:01
关于系统休眠的情景分析
看文件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_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;
}