linux新旧电源管理

晨曦之光 发布于 2012/04/10 15:04
阅读 260
收藏 0

2.4内核的电源管理:
asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void * arg)
{
         char buffer[256];
         /* We only trust the superuser with rebooting the system. */
         if (!capable(CAP_SYS_BOOT))
                 return -EPERM;
         /* For safety, we require "magic" arguments. */
         if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A &&
                         magic2 != LINUX_REBOOT_MAGIC2B))
                 return -EINVAL;
         lock_kernel();
         switch (cmd) {
         case LINUX_REBOOT_CMD_RESTART:
                 notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);//直接在这里调用回调
                 printk(KERN_EMERG "Restarting system./n");
                 machine_restart(NULL);
                 break;
       …
         }
         unlock_kernel();
         return 0;
}
void ctrl_alt_del(void)
{
         if (C_A_D) {
                 notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
                machine_restart(NULL);
         } else
                kill_proc(1, SIGINT, 1);
}
void machine_restart(char * __unused)
{
#if CONFIG_SMP
         smp_send_stop();
         disable_IO_APIC();
#endif
         if(!reboot_thru_bios) {
                 /* rebooting needs to touch the page at absolute addr 0 */
                 *((unsigned short *)__va(0x472)) = reboot_mode;
                 for (;;) {
                         int i;
                         for (i=0; i<100; i++) {
                                 kb_wait();
                                 udelay(50);
                                 outb(0xfe,0x64);         /* pulse reset low */
                                 udelay(50);
                         }
                         /* That didn't work - force a triple fault.. */
                         __asm__ __volatile__("lidt %0": :"m" (no_idt));
                         __asm__ __volatile__("int3");
                 }
         }
         machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
}
就这就完了,而2.6内核就比较完善了:
asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg)
{
         char buffer[256];
         /* We only trust the superuser with rebooting the system. */
         if (!capable(CAP_SYS_BOOT))
                 return -EPERM;
         /* For safety, we require "magic" arguments. */
         if (magic1 != LINUX_REBOOT_MAGIC1 ||
             (magic2 != LINUX_REBOOT_MAGIC2 &&
                         magic2 != LINUX_REBOOT_MAGIC2A &&
                         magic2 != LINUX_REBOOT_MAGIC2B &&
                         magic2 != LINUX_REBOOT_MAGIC2C))
                 return -EINVAL;
         if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
                 cmd = LINUX_REBOOT_CMD_HALT;
         lock_kernel();
         switch (cmd) {
         case LINUX_REBOOT_CMD_RESTART:
                 kernel_restart(NULL);
                 break;
        …//以下类似

         }
         unlock_kernel();
         return 0;
}
void kernel_restart(char *cmd)
{
         kernel_restart_prepare(cmd);
         if (!cmd) {
                 printk(KERN_EMERG "Restarting system./n");
         } else {
                 printk(KERN_EMERG "Restarting system with command '%s'./n", cmd);
         }
         printk("./n");
         machine_restart(cmd);
}
void kernel_restart_prepare(char *cmd)
{
         blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
         system_state = SYSTEM_RESTART;
         device_shutdown();
}
void machine_shutdown(void)
{
#ifdef CONFIG_SMP
         int reboot_cpu_id;
         reboot_cpu_id = 0;
         if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) &&
                 cpu_isset(reboot_cpu, cpu_online_map)) {
                 reboot_cpu_id = reboot_cpu;
         }
         if (!cpu_isset(reboot_cpu_id, cpu_online_map)) {
                 reboot_cpu_id = smp_processor_id();
         }
         set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id));
         smp_send_stop();
#endif /* CONFIG_SMP */
         lapic_shutdown();
#ifdef CONFIG_X86_IO_APIC
         disable_IO_APIC();
#endif
}
void machine_restart(char * __unused)
{
         machine_shutdown();
         machine_emergency_restart();
}
void machine_emergency_restart(void)
{
         if (!reboot_thru_bios) {
                 if (efi_enabled) {
                         efi.reset_system(EFI_RESET_COLD, EFI_SUCCESS, 0, NULL);
                         load_idt(&no_idt);
                         __asm__ __volatile__("int3");
                 }
                 *((unsigned short *)__va(0x472)) = reboot_mode;
                 for (;;) {
                         mach_reboot_fixups(); /* for board specific fixups */
                         mach_reboot();
                         /* That didn't work - force a triple fault.. */
                         load_idt(&no_idt);
                         __asm__ __volatile__("int3");
                 }
         }
         if (efi_enabled)
                 efi.reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL);
         machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
}
以上可以看得出新内核中添加了很多操作,而且将很多调用互换了位置,封装性增强了。再一个就是新内核将apm.c中的send_event取消了,因为有了统一的设备模型,当然send_event封装的pm_send_all还是存在的,我想可能有些地方还要用吧,还可能是因为兼容。
int pm_send_all(pm_request_t rqst, void *data)
{
         struct list_head *entry = pm_devs.next;
         while (entry != ±_devs) {
                 struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
                 if (dev->callback) {
                         int status = pm_send(dev, rqst, data);
                         if (status) {
                                 if (rqst == PM_SUSPEND)
                                         pm_undo_all(dev);
                                 return status;
                         }
                 }
                 entry = entry->next;
         }
         return 0;
}
看一下新内核的suspend函数
static int suspend(int vetoable)
{
         int             err;
         struct apm_user *as;
         if (pm_send_all(PM_SUSPEND, (void *)3)) {
                 if (vetoable) {
                         if (apm_info.connection_version > 0x100)
                                 set_system_power_state(APM_STATE_REJECT);
                         err = -EBUSY;
                         ignore_sys_suspend = 0;
                         printk(KERN_WARNING "apm: suspend was vetoed./n");
                         goto out;
                 }

         }
         device_suspend(PMSG_SUSPEND);
         local_irq_disable();
         device_power_down(PMSG_SUSPEND);
         /* serialize with the timer interrupt */
         write_seqlock(&xtime_lock);
         /* protect against access to timer chip registers */
         spin_lock(&i8253_lock);
         get_time_diff();
         spin_unlock(&i8253_lock);
         write_sequnlock(&xtime_lock);
         local_irq_enable();
         save_processor_state();
         err = set_system_power_state(APM_STATE_SUSPEND);
         ignore_normal_resume = 1;
         restore_processor_state();
         local_irq_disable();
         write_seqlock(&xtime_lock);
         spin_lock(&i8253_lock);
         reinit_timer();
         set_time();
         spin_unlock(&i8253_lock);
         write_sequnlock(&xtime_lock);
         if (err == APM_NO_ERROR)
                 err = APM_SUCCESS;
         if (err != APM_SUCCESS)
                 apm_error("suspend", err);
         err = (err == APM_SUCCESS) ? 0 : -EIO;
         device_power_up();
         local_irq_enable();
         device_resume();
         pm_send_all(PM_RESUME, (void *)0);
         queue_event(APM_NORMAL_RESUME, NULL);
  out:

}
体现统一设备模型好处的就是电源管理,以下是上面函数中的调用的一个函数的代码:
int device_power_down(pm_message_t state)
{
         int error = 0;
         struct device * dev;
         list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
                 if ((error = suspend_device(dev, state)))
                         break;
         }
         if (error)
                 goto Error;
         if ((error = sysdev_suspend(state)))
                 goto Error;

}
int sysdev_suspend(pm_message_t state)
{
         struct sysdev_class * cls;
         struct sys_device *sysdev, *err_dev;
         struct sysdev_driver *drv, *err_drv;
         int ret;
         list_for_each_entry_reverse(cls, &system_subsys.kset.list,
                                     kset.kobj.entry) {
                 list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
                         list_for_each_entry(drv, &sysdev_drivers, entry) {
                                 if (drv->suspend) {
                                         ret = drv->suspend(sysdev, state);
                                         if (ret)
                                                 goto gbl_driver;
                                 }
                         }
                         /* Call auxillary drivers next. */
                         list_for_each_entry(drv, &cls->drivers, entry) {
                                 if (drv->suspend) {
                                         ret = drv->suspend(sysdev, state);
                                         if (ret)
                                                 goto aux_driver;
                                 }
                         }
                         /* Now call the generic one */
                         if (cls->suspend) {
                                 ret = cls->suspend(sysdev, state);
                                 if (ret)
                                         goto cls_driver;
                         }
                 }
         }
         return 0;
         /* resume current sysdev */
cls_driver:
         drv = NULL;
         printk(KERN_ERR "Class suspend failed for %s/n",
                 kobject_name(&sysdev->kobj));
aux_driver:
         list_for_each_entry(err_drv, &cls->drivers, entry) {
                 if (err_drv == drv)
                         break;
                 if (err_drv->resume)
                         err_drv->resume(sysdev);
         }
         drv = NULL;
gbl_driver:
        list_for_each_entry(err_drv, &sysdev_drivers, entry) {
                 if (err_drv == drv)
                         break;
                 if (err_drv->resume)
                        err_drv->resume(sysdev);
         }
         list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
                 if (err_dev == sysdev)
                         break;
                __sysdev_resume(err_dev);
         }
        list_for_each_entry_continue(cls, &system_subsys.kset.list, kset.kobj.entry) {
                 list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
                            __sysdev_resume(err_dev);
                 }
         }
        return ret;
}
内核中有一个el3_suspend函数,但是它在2.4和2.6中的调用路径是不一样的,2.4中是这样的:
int __init el3_probe(struct net_device *dev, int card_idx)
{
    struct el3_private *lp;
    short lrs_state = 0xff, i;
    dev->watchdog_timeo = TX_TIMEOUT;
    dev->do_ioctl = netdev_ioctl;
#ifdef CONFIG_PM
    lp->pmdev = pm_register(PM_ISA_DEV, card_idx, el3_pm_callback);
    if (lp->pmdev) {
        struct pm_dev *p;
        p = lp->pmdev;
        p->data = (struct net_device *)dev;
    }
    ......
}
static int el3_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data)
{
    switch (rqst) {
        case PM_SUSPEND:
            return el3_suspend(pdev);
        case PM_RESUME:
            return el3_resume(pdev);
    }
    return 0;
}
static int el3_suspend(struct pm_dev *pdev)
{
    unsigned long flags;
    struct net_device *dev;
    struct el3_private *lp;
    if (!pdev && !pdev->data)
        return -EINVAL;   
    dev = (struct net_device *)pdev->data;
    lp = (struct el3_private *)dev->priv;
    spin_lock_irqsave(&lp->lock, flags);
    if (netif_running(dev))
        netif_device_detach(dev);
    el3_down(dev);
    spin_unlock_irqrestore(&lp->lock, flags);
    return 0;
}
然后在pm_send_all当中遍历链表就可以调用到,那么在2.6内核当中就不是在pm_send_all中被调用了,pm_send_all,的职能已经单一化了。2.6内核中是在arch/i386/kernel/apm.c中的suspend函数中的device_suspend中被调用的
int device_suspend(pm_message_t state)
{
         int error = 0;
          down(&dpm_sem);
         down(&dpm_list_sem);
         while (!list_empty(&dpm_active) && error == 0) {
                 struct list_head * entry = dpm_active.prev;
                 struct device * dev = to_device(entry);
                  get_device(dev);
                 up(&dpm_list_sem);
                  error = suspend_device(dev, state);
                 down(&dpm_list_sem);
                 if (!list_empty(&dev->power.entry)) {
                         if (!error) {
                                 list_del(&dev->power.entry);
                                 list_add(&dev->power.entry, &dpm_off);
                         } else if (error == -EAGAIN) {
                                 list_del(&dev->power.entry);
                                 list_add(&dev->power.entry, &dpm_off_irq);
                                 error = 0;
                         }
                 }
         … 

                 put_device(dev);
         }
         up(&dpm_list_sem);
         if (error) {
                 while (!list_empty(&dpm_off_irq)) {
                         struct list_head * entry = dpm_off_irq.next;
                         list_del(entry);
                         list_add(entry, &dpm_off);
                 }
                 dpm_resume();
         }
         up(&dpm_sem);
         return error;
}
其中suspend_device被调用:
int suspend_device(struct device * dev, pm_message_t state)
{
         int error = 0;
          down(&dev->sem);
         if (dev->power.power_state.event) {
                 dev_dbg(dev, "PM: suspend %d-->%d/n",
                         dev->power.power_state.event, state.event);
         }
        if (dev->power.pm_parent&& dev->power.pm_parent->power.power_state.event) {
       …        

         }
          dev->power.prev_state = dev->power.power_state;
          if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
                 dev_dbg(dev, "suspending/n");
                 error = dev->bus->suspend(dev, state);
                 suspend_report_result(dev->bus->suspend, error);
         }
         up(&dev->sem);
         return error;
}
从dev->bus->suspend(dev, state)可以看出,实际调用的是bus_type的suspend方法,那么就得得到el3的bus_type了,仍然分析/drivers/net/3c509.c文件:
static struct mca_driver el3_mca_driver = {
   .id_table = el3_mca_adapter_ids,
   .driver = {
              .name = "3c529",
              .bus = &eisa_bus_type,//原来不是这个,这里往下的代码来自一个补丁
              .probe = el3_mca_probe,
              .remove = __devexit_p(el3_device_remove),
              .suspend = el3_suspend,
              .resume  = el3_resume,
             },
};
struct bus_type eisa_bus_type = {
    .name = "eisa",
    .match = eisa_bus_match,
    + .suspend = eisa_bus_suspend,//补丁中添加
    + .resume = eisa_bus_resume,  //补丁中添加
};
static int eisa_bus_suspend(struct device * dev, pm_message_t state)
{
    int ret = 0;
    if (dev->driver && dev->driver->suspend)
        ret = dev->driver->suspend(dev, state);
        return ret;
}
上面函数的dev->driver->suspend实际就是el3_suspend。
就这样,在新内核当中,device_suspend被单独作为一个功能封装成了一个函数,而在老的内核中却被统一纳入了apm的管理范围。最后看一下apm的管理前提,就是将设备注册进apm可以管理的链表
struct pm_dev *pm_register(pm_dev_t type, unsigned long id,  pm_callback callback)
{
         struct pm_dev *dev = kzalloc(sizeof(struct pm_dev), GFP_KERNEL);
         if (dev) {
                 dev->type = type;
                 dev->id = id;
                 dev->callback = callback;
                 mutex_lock(±_devs_lock);
                 list_add(&dev->entry, ±_devs);
                 mutex_unlock(±_devs_lock);
         }
         return dev;
}
这个函数很简单,仅仅在链表中创建并且添加了一个项,但是怎么将之和具体设备联系起来呢,那就是调用pm_register的函数下面要做的事情,看个例子:
static int ali_ircc_open(int i, chipio_t *info)
{
         struct net_device *dev;
         struct ali_ircc_cb *self;
         struct pm_dev *pmdev;
         int dongle_id;
         int err;
         dev = alloc_irdadev(sizeof(*self));
    ......
         self = dev->priv;
         self->netdev = dev;
         dev_self[i] = self;
         self->index = i;
    ......
         if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) {
    .....
         dev->hard_start_xmit = ali_ircc_sir_hard_xmit;
         dev->open            = ali_ircc_net_open;
         dev->stop            = ali_ircc_net_close;
         dev->do_ioctl        = ali_ircc_net_ioctl;
         dev->get_stats       = ali_ircc_net_get_stats;
    ......
         err = register_netdev(dev);    
    ......
         pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc);
         if (pmdev)
                 pmdev->data = self;
         ......
}
注意pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc);
         if (pmdev)
                 pmdev->data = self;
这两行,这样就可以在需要apm管理的地方找到原来的设备了。


原文链接:http://blog.csdn.net/dog250/article/details/5302900
加载中
返回顶部
顶部