Chinaunix首页 | 论坛 | 博客
  • 博客访问: 793871
  • 博文数量: 172
  • 博客积分: 3836
  • 博客等级: 中校
  • 技术积分: 1988
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-10 14:59
文章分类

全部博文(172)

文章存档

2014年(2)

2013年(1)

2012年(28)

2011年(141)

分类: LINUX

2011-11-08 10:19:15

在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。

int pm_suspend(suspend_state_t state)

{

if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)

return enter_state(state);

return -EINVAL;

}

 

在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。

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();

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;

}

 

在 suspend_prepare()函数中,先通过pm_prepare_console();给suspend分配一个虚拟终端来输出信息,再广播一个 系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,最后会尝试释 放一些内存。

static int suspend_prepare(void)

{

int error;

unsigned int free_pages;

 

if (!suspend_ops || !suspend_ops->enter)

return -EPERM;

 

pm_prepare_console();

 

error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);

if (error)

goto Finish;

 

error = usermodehelper_disable();

if (error)

goto Finish;

 

if (suspend_freeze_processes()) {

error = -EAGAIN;

goto Thaw;

}

 

free_pages = global_page_state(NR_FREE_PAGES);

if (free_pages < FREE_PAGE_NUMBER) {

pr_debug("PM: free some memory\n");

shrink_all_memory(FREE_PAGE_NUMBER - free_pages);

if (nr_free_pages() < FREE_PAGE_NUMBER) {

error = -ENOMEM;

printk(KERN_ERR "PM: No enough memory\n");

}

}

if (!error)

return 0;

 

 Thaw:

suspend_thaw_processes();

usermodehelper_enable();

 Finish:

pm_notifier_call_chain(PM_POST_SUSPEND);

pm_restore_console();

return error;

}

 

在 suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes() 函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败, 函数会放弃冻结。

static int try_to_freeze_tasks(bool sig_only)

{

struct task_struct *g, *p;

unsigned long end_time;

unsigned int todo;

struct timeval start, end;

u64 elapsed_csecs64;

unsigned int elapsed_csecs;

unsigned int wakeup = 0;

 

do_gettimeofday(&start);

 

end_time = jiffies + TIMEOUT;

do {

todo = 0;

read_lock(&tasklist_lock);

do_each_thread(g, p) {

if (frozen(p) || !freezeable(p))

continue;

 

if (!freeze_task(p, sig_only))

continue;

 

/*

* Now that we've done set_freeze_flag, don't

* perturb a task in TASK_STOPPED or TASK_TRACED.

* It is "frozen enough".  If the task does wake

* up, it will immediately call try_to_freeze.

*/

if (!task_is_stopped_or_traced(p) &&

   !freezer_should_skip(p))

todo++;

} while_each_thread(g, p);

read_unlock(&tasklist_lock);

yield();/* Yield is okay here */

if (todo && has_wake_lock(WAKE_LOCK_SUSPEND)) {

wakeup = 1;

break;

}

if (time_after(jiffies, end_time))

break;

} while (todo);

 

do_gettimeofday(&end);

elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);

do_div(elapsed_csecs64, NSEC_PER_SEC / 100);

elapsed_csecs = elapsed_csecs64;

 

if (todo) {

/* This does not unfreeze processes that are already frozen

* (we have slightly ugly calling convention in that respect,

* and caller must call thaw_processes() if something fails),

* but it cleans up leftover PF_FREEZE requests.

*/

if(wakeup) {

printk("\n");

printk(KERN_ERR "Freezing of %s aborted\n",

sig_only ? "user space " : "tasks ");

}

else {

printk("\n");

printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "

"(%d tasks refusing to freeze):\n",

elapsed_csecs / 100, elapsed_csecs % 100, todo);

show_state();

}

read_lock(&tasklist_lock);

do_each_thread(g, p) {

task_lock(p);

if (freezing(p) && !freezer_should_skip(p))

printk(KERN_ERR " %s\n", p->comm);

cancel_freezing(p);

task_unlock(p);

} while_each_thread(g, p);

read_unlock(&tasklist_lock);

} else {

printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,

elapsed_csecs % 100);

}

return todo ? -EBUSY : 0;

}

 

到 现在,所有的进程(也包括workqueue/kthread) 都已经停止了,内核态进程有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁, 所以在外设suspend()函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程 中,有一些log是无法输出的,所以一旦出现问题,非常难调试。

 

回到enter_state()函数中,再冻结进程完成 后,调用suspend_devices_and_enter()函数让外设进入休眠。该函数中,首先休眠串口(之后不能再显示log,解决方法为在 kernel配置选项的cmd_line中,添加”no_console_suspend”选项),再通过device_suspend()函数调用各驱 动的suspend函数。

当外设进入休眠后,suspend_ops->prepare()被调用,suspend_ops是板级的 PM操作(本文中粉红色的函数,依赖于具体的平台),以s3c6410为例,其注册在linux_source/arch/arm/plat- s3c64xx/pm.c中,只定义了suspend_ops->enter()函数。

static struct platform_suspend_ops s3c6410_pm_ops = {

.enter= s3c6410_pm_enter,

.valid= suspend_valid_only_mem,

};

接下来,多CPU中的非启动CPU被关闭。

int suspend_devices_and_enter(suspend_state_t state)

{

int error;

 

if (!suspend_ops)

return -ENOSYS;

 

if (suspend_ops->begin) {

error = suspend_ops->begin(state);

if (error)

goto Close;

}

suspend_console();

suspend_test_start();

error = device_suspend(PMSG_SUSPEND);

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;

 

if (suspend_ops->prepare) {

error = suspend_ops->prepare();

if (error)

goto Resume_devices;

}

 

if (suspend_test(TEST_PLATFORM))

goto Finish;

 

error = disable_nonboot_cpus();

if (!error && !suspend_test(TEST_CPUS))

suspend_enter(state);

 

enable_nonboot_cpus();

 Finish:

if (suspend_ops->finish)

suspend_ops->finish();

 Resume_devices:

suspend_test_start();

device_resume(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;

}

 

接 下来suspend_enter()被调用,该函数首先关闭IRQ,然后调用device_power_down(), 它会调用suspend_late()函数, 这个函数是系统真正进入休眠最后调用的函数, 通常会在这个函数中作最后的检查,接下来休眠所有的系统设备和总线。最后调用 suspend_pos->enter() 来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。

static int suspend_enter(suspend_state_t state)

{

int error = 0;

 

device_pm_lock();

#ifdef CONFIG_CPU_FREQ

cpufreq_get_cpufreq_name(0);

strcpy(governor_name, cpufreq_governor_name);

if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0, "performance");

}

#endif /* CONFIG_CPU_FREQ */

arch_suspend_disable_irqs();

BUG_ON(!irqs_disabled());

 

if ((error = device_power_down(PMSG_SUSPEND))) {

printk(KERN_ERR "PM: Some devices failed to power down\n");

goto Done;

}

 

error = sysdev_suspend(PMSG_SUSPEND);

if (!error) {

if (!suspend_test(TEST_CORE))

error = suspend_ops->enter(state);

sysdev_resume();

}

 

device_power_up(PMSG_RESUME);

 Done:

arch_suspend_enable_irqs();

#ifdef CONFIG_CPU_FREQ

if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0, governor_name);

}

#endif /* CONFIG_CPU_FREQ */

BUG_ON(irqs_disabled());

device_pm_unlock();

return error;

}

 

在suspend_pos->enter()所对应的函数中,代码最终停止在pm_cpu_sleep();处。

static int s3c6410_pm_enter(suspend_state_t state)

{

 ……

s3c6410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save));

s3c6410_pm_do_save(irq_save, ARRAY_SIZE(irq_save));

s3c6410_pm_do_save(core_save, ARRAY_SIZE(core_save));

s3c6410_pm_do_save(sromc_save, ARRAY_SIZE(sromc_save));

 

/* Clear WAKEUP_STAT register for next wakeup -jc.lee */

/* If this register do not be cleared, Wakeup will be failed */

__raw_writel(__raw_readl(S3C_WAKEUP_STAT), S3C_WAKEUP_STAT);

 

 

#ifdef CONFIG_MACH_SMDK6410

/* ALL sub block "ON" before enterring sleep mode - EVT0 bug*/

__raw_writel(0xffffff00, S3C_NORMAL_CFG);

 

/* Open all clock gate to enter sleep mode - EVT0 bug*/

__raw_writel(0xffffffff, S3C_HCLK_GATE);

__raw_writel(0xffffffff, S3C_PCLK_GATE);

__raw_writel(0xffffffff, S3C_SCLK_GATE);

        ……

/* s3c6410_cpu_save will also act as our return point from when

* we resume as it saves its own register state, so use the return

* code to differentiate return from save and return from sleep */

 

if (s3c6410_cpu_save(regs_save) == 0) {

flush_cache_all();

pm_cpu_sleep();

}

 

/* restore the cpu state */

cpu_init();

 

__raw_writel(s3c_eint_mask_val, S3C_EINT_MASK);

 

/* restore the system state */

s3c6410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));

s3c6410_pm_do_restore(sromc_save, ARRAY_SIZE(sromc_save));

……

}

 

Android 唤醒过程如下:

如 果在休眠中系统被中断或者其他事件唤醒,接下来的代码就从suspend完成的地方开始执行,以s3c6410为例,即pm.c中的 s3c6410_pm_enter()中的cpu_init(),然后执行suspend_enter()的sysdev_resume()函数,唤醒系 统设备和总线,使能系统中断。

static int suspend_enter(suspend_state_t state)

{

int error = 0;

 

device_pm_lock();

#ifdef CONFIG_CPU_FREQ

cpufreq_get_cpufreq_name(0);

strcpy(governor_name, cpufreq_governor_name);

if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0, "performance");

}

#endif /* CONFIG_CPU_FREQ */

arch_suspend_disable_irqs();

BUG_ON(!irqs_disabled());

 

if ((error = device_power_down(PMSG_SUSPEND))) {

printk(KERN_ERR "PM: Some devices failed to power down\n");

goto Done;

}

 

error = sysdev_suspend(PMSG_SUSPEND);

if (!error) {

if (!suspend_test(TEST_CORE))

error = suspend_ops->enter(state);  //suspend过程完成处

sysdev_resume();

}

 

device_power_up(PMSG_RESUME);

 Done:

arch_suspend_enable_irqs();

#ifdef CONFIG_CPU_FREQ

if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0, governor_name);

}

#endif /* CONFIG_CPU_FREQ */

BUG_ON(irqs_disabled());

device_pm_unlock();

return error;

}

 

然后回到suspend_devices_and_enter()函数中,使能休眠时候停止掉的非启动CPU,继续唤醒每个设备,使能终端。

int suspend_devices_and_enter(suspend_state_t state)

{

int error;

 

if (!suspend_ops)

return -ENOSYS;

 

if (suspend_ops->begin) {

error = suspend_ops->begin(state);

if (error)

goto Close;

}

suspend_console();

suspend_test_start();

error = device_suspend(PMSG_SUSPEND);

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;

 

if (suspend_ops->prepare) {

error = suspend_ops->prepare();

if (error)

goto Resume_devices;

}

 

if (suspend_test(TEST_PLATFORM))

goto Finish;

 

error = disable_nonboot_cpus();

if (!error && !suspend_test(TEST_CPUS))

suspend_enter(state);  //suspend过程完成处

 

enable_nonboot_cpus();

 Finish:

if (suspend_ops->finish)

suspend_ops->finish();

 Resume_devices:

suspend_test_start();

device_resume(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;

}

 

当suspend_devices_and_enter()执行完成后,系统外设已经唤醒,但进程依然是冻结的状态,返回到enter_state函数中,调用suspend_finish()函数。

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();

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);  //suspend过程完成处

 

 Finish:

pr_debug("PM: Finishing wakeup.\n");

suspend_finish();

 Unlock:

mutex_unlock(&pm_mutex);

return error;

}

 

在suspend_finish()函数中,解冻进程和任务,使能用户空间helper进程,广播一个系统从suspend状态退出的notify,唤醒终端。

static void suspend_finish(void)

{

suspend_thaw_processes();

usermodehelper_enable();

pm_notifier_call_chain(PM_POST_SUSPEND);

pm_restore_console();

}

 

当所有的唤醒已经结束以后,用户进程都已经开始运行了,但没点亮屏幕,唤醒通常会是以下的几种原因:

如 果是来电,那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用 PowerManagerService来写”on”到 /sys/power/state 来调用late resume(),执行点亮屏幕等操作。

用 户按键事件会送到WindowManager中,WindowManager会处理这些按键事件,按键分为几种情况,如果按键不是唤醒键,那么 WindowManager会主动放弃wakeLock来使系统进入再次休眠;如果按键是唤醒键,那么WindowManger就会调用 PowerManagerService中的接口来执行late Resume。

 

当”on”被写入到/sys/power /state之后,同early_suspend过程,request_suspend_state()被调用,只是执行的工作队列变为 late_resume_work。在late_resume函数中,唤醒调用了early_suspend的设备。

static DECLARE_WORK(late_resume_work, late_resume);

static void late_resume(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);

if (state == SUSPENDED)

state &= ~SUSPENDED;

else

abort = 1;

spin_unlock_irqrestore(&state_lock, irqflags);

 

if (abort) {

if (debug_mask & DEBUG_SUSPEND)

pr_info("late_resume: abort, state %d\n", state);

goto abort;

}

if (debug_mask & DEBUG_SUSPEND)

pr_info("late_resume: call handlers\n");

list_for_each_entry_reverse(pos, &early_suspend_handlers, link)

if (pos->resume != NULL)

pos->resume(pos);

if (debug_mask & DEBUG_SUSPEND)

pr_info("late_resume: done\n");

abort:

mutex_unlock(&early_suspend_lock);

}

 

 

 

关于wake_lock

在上文中,已经介绍了wakelock机制,下面从代码的角度进行介绍。

wakelock有3种类型,常用为WAKE_LOCK_SUSPEND,作用是防止系统进入睡眠。其他类型不是很清楚。

enum {

WAKE_LOCK_SUSPEND, /* Prevent suspend */

WAKE_LOCK_IDLE,    /* Prevent low power idle */

WAKE_LOCK_TYPE_COUNT

};

 

Wakelock有加锁和解锁2种操作,加锁有2种方式,第一种是永久加锁(wake_lock),这种锁必须手动的解锁;另一种是超时锁(wake_lock_timeout),这种锁在过去指定时间后,会自动解锁。

void wake_lock(struct wake_lock *lock)

{

wake_lock_internal(lock, 0, 0);

}

 

void wake_lock_timeout(struct wake_lock *lock, long timeout)

{

wake_lock_internal(lock, timeout, 1);

}

 

对于wakelock,timeout = has_timeout = 0;直接加锁后,然后退出;

static void wake_lock_internal(

struct wake_lock *lock, long timeout, int has_timeout)

{

int type;

unsigned long irqflags;

long expire_in;

 

spin_lock_irqsave(&list_lock, irqflags);

type = lock->flags & WAKE_LOCK_TYPE_MASK;

BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);

BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));

#ifdef CONFIG_WAKELOCK_STAT

if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {

if (debug_mask & DEBUG_WAKEUP)

pr_info("wakeup wake lock: %s\n", lock->name);

wait_for_wakeup = 0;

lock->stat.wakeup_count++;

}

if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&

   (long)(lock->expires - jiffies) <= 0) {

wake_unlock_stat_locked(lock, 0);

lock->stat.last_time = ktime_get();

}

#endif

if (!(lock->flags & WAKE_LOCK_ACTIVE)) {

lock->flags |= WAKE_LOCK_ACTIVE;

#ifdef CONFIG_WAKELOCK_STAT

lock->stat.last_time = ktime_get();

#endif

}

list_del(&lock->link);

if (has_timeout) {

if (debug_mask & DEBUG_WAKE_LOCK)

pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",

lock->name, type, timeout / HZ,

(timeout % HZ) * MSEC_PER_SEC / HZ);

lock->expires = jiffies + timeout;

lock->flags |= WAKE_LOCK_AUTO_EXPIRE;

list_add_tail(&lock->link, &active_wake_locks[type]);

} else {

if (debug_mask & DEBUG_WAKE_LOCK)

pr_info("wake_lock: %s, type %d\n", lock->name, type);

lock->expires = LONG_MAX;

lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;

list_add(&lock->link, &active_wake_locks[type]);

}

if (type == WAKE_LOCK_SUSPEND) {

current_event_num++;

#ifdef CONFIG_WAKELOCK_STAT

if (lock == &main_wake_lock)

update_sleep_wait_stats_locked(1);

else if (!wake_lock_active(&main_wake_lock))

update_sleep_wait_stats_locked(0);

#endif

if (has_timeout)

expire_in = has_wake_lock_locked(type);

else

expire_in = -1;

if (expire_in > 0) {

if (debug_mask & DEBUG_EXPIRE)

pr_info("wake_lock: %s, start expire timer, "

"%ld\n", lock->name, expire_in);

mod_timer(&expire_timer, jiffies + expire_in);

} else {

if (del_timer(&expire_timer))

if (debug_mask & DEBUG_EXPIRE)

pr_info("wake_lock: %s, stop expire timer\n",

lock->name);

if (expire_in == 0)

queue_work(suspend_work_queue, &suspend_work);

}

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

而对于wake_lock_timeout,在经过timeout时间后,才加锁。再判断当前持有wakelock时,启动另一个定时器,在expire_timer的回调函数中再次判断是否持有wakelock。

static void expire_wake_locks(unsigned long data)

{

long has_lock;

unsigned long irqflags;

if (debug_mask & DEBUG_EXPIRE)

pr_info("expire_wake_locks: start\n");

spin_lock_irqsave(&list_lock, irqflags);

if (debug_mask & DEBUG_SUSPEND)

print_active_locks(WAKE_LOCK_SUSPEND);

has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);

if (debug_mask & DEBUG_EXPIRE)

pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);

if (has_lock == 0)

queue_work(suspend_work_queue, &suspend_work);

spin_unlock_irqrestore(&list_lock, irqflags);

}

 

static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);

 

在wakelock中,有2个地方可以让系统从early_suspend进入suspend状态。分别是:

l在wake_unlock中,解锁之后,若没有其他的wakelock,则进入suspend。

l在超时锁的定时器超时后,定时器的回调函数,会判断有没有其他的wakelock,若没有,则进入suspend。

阅读(1498) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~