Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2132310
  • 博文数量: 288
  • 博客积分: 10594
  • 博客等级: 上将
  • 技术积分: 3469
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-27 19:27
文章分类

全部博文(288)

文章存档

2012年(4)

2011年(30)

2010年(40)

2009年(32)

2008年(71)

2007年(79)

2006年(32)

分类: 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;
 }

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