当前位置:   article > 正文

linux内核中的电源管理_linux内核电源管理

linux内核电源管理

一、介绍

linux中为了解决非必要功耗的消耗,提供了多种电源管理方式,诸如休眠(suspend)、关机(power off和shutdown)、复位(reboot和reset)。为了解决运行时不必要的功耗消耗,linux提供了runtime pm、cpu/device dvfs、cpu hotplug、cpu idle、clock gate、power gate、reset等电源管理的机制。为了解决运行时电源管理对性能的影响,linux提供了pm qos的功能,用于平衡性能与功耗,这样既能降低功耗,又不影响性能。

二、电源管理操作原理实现和流程

1.suspend

suspend一般是我们所说的s3状态,也就是关闭到mem,省电等级低于关机和s4。这个状态就是将系统所有进程全部冻结,只保留first cpu进行运行(等待用户的唤醒中断),但是硬件设备不断电(最直观的感受可以通过键盘鼠标是可以唤醒系统,键盘灯或者鼠标等显示电源还在线)。

susepend的大致流程如下图

详细流程如下图

详细代码实现

step1:linux系统提供了一个/sys/power/state文件接口,一旦文件被修改,将调用state_store()函数处理电源管理需求。suspend一般是我们常说的s3休眠,即echo mem > /sys/power/state后,将执行pm_suspend(mem_sleep_current)。

  1. static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
  2. const char *buf, size_t n)
  3. {
  4. suspend_state_t state;
  5. int error;
  6. error = pm_autosleep_lock();
  7. if (error)
  8. return error;
  9. if (pm_autosleep_state() > PM_SUSPEND_ON) {
  10. error = -EBUSY;
  11. goto out;
  12. }
  13. state = decode_state(buf, n);
  14. if (state < PM_SUSPEND_MAX) {
  15. if (state == PM_SUSPEND_MEM)
  16. state = mem_sleep_current;
  17. /*****写入的state值合法且非'disk',执行pm_suspend(state)进入suspend console****/
  18. error = pm_suspend(state);
  19. } else if (state == PM_SUSPEND_MAX) {
  20. error = hibernate();
  21. } else {
  22. error = -EINVAL;
  23. }
  24. out:
  25. pm_autosleep_unlock();
  26. return error ? error : n;
  27. }
  28. power_attr(state);

step2:如果state值是合法的,将打印suspend entry (mem_sleep_lables[state])并调用enter_state(state);开始进入将要suspend的状态。

  1. /**
  2. * pm_suspend - Externally visible function for suspending the system.
  3. * @state: System sleep state to enter.
  4. *
  5. * Check if the value of @state represents one of the supported states,
  6. * execute enter_state() and update system suspend statistics.
  7. */
  8. int pm_suspend(suspend_state_t state)
  9. {
  10. int error;
  11. if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
  12. return -EINVAL;
  13. /****打印suspend entry提示信息****/
  14. pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
  15. /****开始进入suspend请求的状态****/
  16. error = enter_state(state);
  17. if (error) {
  18. suspend_stats.fail++;
  19. dpm_save_failed_errno(error);
  20. } else {
  21. suspend_stats.success++;
  22. }
  23. pr_info("suspend exit\n");
  24. return error;
  25. }
  26. EXPORT_SYMBOL(pm_suspend);

step3:enter_state函数主要调用两个函数,分别是suspend_prepare(state)和suspend_devices_and_enter(state)

  1. /**
  2. * enter_state - Do common work needed to enter system sleep state.
  3. * @state: System sleep state to enter.
  4. *
  5. * Make sure that no one else is trying to put the system into a sleep state.
  6. * Fail if that's not the case. Otherwise, prepare for system suspend, make the
  7. * system enter the given sleep state and clean up after wakeup.
  8. */
  9. static int enter_state(suspend_state_t state)
  10. {
  11. int error;
  12. trace_suspend_resume(TPS("suspend_enter"), state, true);
  13. if (state == PM_SUSPEND_TO_IDLE) {
  14. #ifdef CONFIG_PM_DEBUG
  15. if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
  16. pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
  17. return -EAGAIN;
  18. }
  19. #endif
  20. } else if (!valid_state(state)) {
  21. return -EINVAL;
  22. }
  23. if (!mutex_trylock(&system_transition_mutex))
  24. return -EBUSY;
  25. if (state == PM_SUSPEND_TO_IDLE)
  26. s2idle_begin();
  27. if (sync_on_suspend_enabled) {
  28. trace_suspend_resume(TPS("sync_filesystems"), 0, true);
  29. ksys_sync_helper();
  30. trace_suspend_resume(TPS("sync_filesystems"), 0, false);
  31. }
  32. pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
  33. pm_suspend_clear_flags();
  34. /****准备进入休眠状态****/
  35. error = suspend_prepare(state);
  36. if (error)
  37. goto Unlock;
  38. if (suspend_test(TEST_FREEZER))
  39. goto Finish;
  40. trace_suspend_resume(TPS("suspend_enter"), state, false);
  41. pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);
  42. pm_restrict_gfp_mask();
  43. /****devices进入suspend状态****/
  44. error = suspend_devices_and_enter(state);
  45. pm_restore_gfp_mask();
  46. Finish:
  47. events_check_enabled = false;
  48. pm_pr_dbg("Finishing wakeup.\n");
  49. suspend_finish();
  50. Unlock:
  51. mutex_unlock(&system_transition_mutex);
  52. return error;
  53. }

step4:suspend_prepare()函数开始进入"suspend" console和冻结用户进程

  1. /**
  2. * suspend_prepare - Prepare for entering system sleep state.
  3. * @state: Target system sleep state.
  4. *
  5. * Common code run for every system sleep state that can be entered (except for
  6. * hibernation). Run suspend notifiers, allocate the "suspend" console and
  7. * freeze processes.
  8. */
  9. static int suspend_prepare(suspend_state_t state)
  10. {
  11. int error;
  12. if (!sleep_state_supported(state))
  13. return -EPERM;
  14. /****准备进入suspend console****/
  15. pm_prepare_console();
  16. error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND);
  17. if (error)
  18. goto Restore;
  19. trace_suspend_resume(TPS("freeze_processes"), 0, true);
  20. /****冻结用户进程****/
  21. error = suspend_freeze_processes();
  22. trace_suspend_resume(TPS("freeze_processes"), 0, false);
  23. if (!error)
  24. return 0;
  25. suspend_stats.failed_freeze++;
  26. dpm_save_failed_step(SUSPEND_FREEZE);
  27. pm_notifier_call_chain(PM_POST_SUSPEND);
  28. Restore:
  29. pm_restore_console();
  30. return error;
  31. }

step5:

  1. /**
  2. * suspend_prepare - Prepare for entering system sleep state.
  3. * @state: Target system sleep state.
  4. *
  5. * Common code run for every system sleep state that can be entered (except for
  6. * hibernation). Run suspend notifiers, allocate the "suspend" console and
  7. * freeze processes.
  8. */
  9. static int suspend_prepare(suspend_state_t state)
  10. {
  11. int error;
  12. if (!sleep_state_supported(state))
  13. return -EPERM;
  14. /****准备进入suspend console****/
  15. pm_prepare_console();
  16. error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND);
  17. if (error)
  18. goto Restore;
  19. trace_suspend_resume(TPS("freeze_processes"), 0, true);
  20. /****冻结用户进程****/
  21. error = suspend_freeze_processes();
  22. trace_suspend_resume(TPS("freeze_processes"), 0, false);
  23. if (!error)
  24. return 0;
  25. suspend_stats.failed_freeze++;
  26. dpm_save_failed_step(SUSPEND_FREEZE);
  27. pm_notifier_call_chain(PM_POST_SUSPEND);
  28. Restore:
  29. pm_restore_console();
  30. return error;
  31. }

2.autosleep

autosleep也是从android wakelocks补丁集中演化而来的,用于取代wakelock中的自动休眠功能。它基于wakeup source实现。根据使用场景,低功耗状态可以是Freeze, Standby, Suspend to RAM和suspend to disk中的任意一种。它依赖wakeup events framework判断系统有没有事情正在做,只要系统没有正在处理和新增的wakeup events, 就尝试suspend, 如果suspend过程中有events产生,就resume。

autosleep的实现位于kernel/power/autosleep.c中,基于wakeup count & hibernate功能, 并通过PM core的main模块在开启CONFIG_PM_AUTOSLEEP配置时,向用户空间提供sysfs文件(sys/power/autosleep)。代码实现参考:

首先,在开启CONFIG_PM_AUTOSLEEP配置时,向用户空间提供sysfs文件(sys/power/autosleep)。主要通过pm_autosleep_set_state接口,进行pm的autosleep状态设置。

  1. #ifdef CONFIG_PM_AUTOSLEEP
  2. //文件读时函数入口
  3. static ssize_t autosleep_show(struct kobject *kobj,
  4. struct kobj_attribute *attr,
  5. char *buf)
  6. {
  7. suspend_state_t state = pm_autosleep_state();
  8. if (state == PM_SUSPEND_ON)
  9. return sprintf(buf, "off\n");
  10. #ifdef CONFIG_SUSPEND
  11. if (state < PM_SUSPEND_MAX)
  12. return sprintf(buf, "%s\n", pm_states[state] ?
  13. pm_states[state] : "error");
  14. #endif
  15. #ifdef CONFIG_HIBERNATION
  16. return sprintf(buf, "disk\n");
  17. #else
  18. return sprintf(buf, "error");
  19. #endif
  20. }
  21. //文件写时函数入口
  22. static ssize_t autosleep_store(struct kobject *kobj,
  23. struct kobj_attribute *attr,
  24. const char *buf, size_t n)
  25. {
  26. suspend_state_t state = decode_state(buf, n);
  27. int error;
  28. if (state == PM_SUSPEND_ON
  29. && strcmp(buf, "off") && strcmp(buf, "off\n"))
  30. return -EINVAL;
  31. if (state == PM_SUSPEND_MEM)
  32. state = mem_sleep_current;
  33. //pm处理进入autosleep状态接口
  34. error = pm_autosleep_set_state(state);
  35. return error ? error : n;
  36. }
  37. //定义一个0644权限的sysfs,在这里是/sys/power/autosleep文件
  38. power_attr(autosleep);
  39. #endif /* CONFIG_PM_AUTOSLEEP */

在kernel PM初始化时(/kernel/power/main.c:pm_init),调用pm_autosleep_init初始化autosleep所需的两个全局参数autosleep_ws和autosleep_wq:

  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * kernel/power/autosleep.c
  4. *
  5. * Opportunistic sleep support.
  6. *
  7. * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
  8. */
  9. #include <linux/device.h>
  10. #include <linux/mutex.h>
  11. #include <linux/pm_wakeup.h>
  12. #include "power.h"
  13. static suspend_state_t autosleep_state;
  14. static struct workqueue_struct *autosleep_wq;
  15. /*
  16. * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
  17. * is active, otherwise a deadlock with try_to_suspend() is possible.
  18. * Alternatively mutex_lock_interruptible() can be used. This will then fail
  19. * if an auto_sleep cycle tries to freeze processes.
  20. */
  21. static DEFINE_MUTEX(autosleep_lock);
  22. static struct wakeup_source *autosleep_ws;

1)autosleep_ws:在autosleep执行关键操作时, 阻止系统休眠

2)autosleep_wq:一个workqueue, 用于触发实际的休眠动作(休眠应由进程或者线程触发)

  1. int __init pm_autosleep_init(void)
  2. {
  3. autosleep_ws = wakeup_source_register(NULL, "autosleep");
  4. if (!autosleep_ws)
  5. return -ENOMEM;
  6. autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
  7. if (autosleep_wq)
  8. return 0;
  9. wakeup_source_unregister(autosleep_ws);
  10. return -ENOMEM;
  11. }

前面说过,在开启CONFIG_PM_AUTOSLEEP配置时,向用户空间提供sysfs文件(sys/power/autosleep)。主要通过pm_autosleep_set_state接口,进行pm的autosleep状态设置。pm_autosleep_set_state负责设置autosleep的状态, autosleep状态有freeze, standby, STR, STD等状态(具体依赖于系统支持的电源管理状态)。

  1. int pm_autosleep_set_state(suspend_state_t state)
  2. {
  3. #ifndef CONFIG_HIBERNATION
  4. if (state >= PM_SUSPEND_MAX)
  5. return -EINVAL;
  6. #endif
  7. __pm_stay_awake(autosleep_ws);
  8. mutex_lock(&autosleep_lock);
  9. autosleep_state = state;
  10. __pm_relax(autosleep_ws);
  11. if (state > PM_SUSPEND_ON) { /***如果设置的autosleep要进入的状态高于就绪状态(s1,s2,s3,s4等高于s0的状态),就执行autosleep***/
  12. pm_wakep_autosleep_enabled(true);
  13. queue_up_suspend_work();
  14. } else { /***否则不执行autosleep***/
  15. pm_wakep_autosleep_enabled(false);
  16. }
  17. mutex_unlock(&autosleep_lock);
  18. return 0;
  19. }

一旦autosleep开启,将使能pm_wakep_autosleep_enabled和执行queue_up_suspend_work功能。

1)pm_wakep_autosleep_enabled主要用于更新wakeup source中和auto sleep有关的信息,代码和执行逻辑如下:

  1. #ifdef CONFIG_PM_AUTOSLEEP
  2. /**
  3. * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources.
  4. * @set: Whether to set or to clear the autosleep_enabled flags.
  5. */
  6. void pm_wakep_autosleep_enabled(bool set)
  7. {
  8. struct wakeup_source *ws;
  9. ktime_t now = ktime_get();
  10. int srcuidx;
  11. srcuidx = srcu_read_lock(&wakeup_srcu);
  12. list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) {
  13. spin_lock_irq(&ws->lock);
  14. if (ws->autosleep_enabled != set) {
  15. ws->autosleep_enabled = set;
  16. if (ws->active) {
  17. if (set)
  18. ws->start_prevent_time = now;
  19. else
  20. update_prevent_sleep_time(ws, now);
  21. }
  22. }
  23. spin_unlock_irq(&ws->lock);
  24. }
  25. srcu_read_unlock(&wakeup_srcu, srcuidx);
  26. }
  27. #endif /* CONFIG_PM_AUTOSLEEP */

2)queue_up_suspend_work调用queue_work设置pm状态,而queue_work是try_to_suspend的别名,try_to_suspend会调用hibernate或者pm_suspend(相关实现参考suspend)接口完成pm状态的设置。代码实现如下:

  1. static void try_to_suspend(struct work_struct *work)
  2. {
  3. unsigned int initial_count, final_count;
  4. if (!pm_get_wakeup_count(&initial_count, true))
  5. goto out;
  6. mutex_lock(&autosleep_lock);
  7. if (!pm_save_wakeup_count(initial_count) ||
  8. system_state != SYSTEM_RUNNING) {
  9. mutex_unlock(&autosleep_lock);
  10. goto out;
  11. }
  12. if (autosleep_state == PM_SUSPEND_ON) {
  13. mutex_unlock(&autosleep_lock);
  14. return;
  15. }
  16. if (autosleep_state >= PM_SUSPEND_MAX)
  17. hibernate();
  18. else
  19. pm_suspend(autosleep_state);
  20. mutex_unlock(&autosleep_lock);
  21. if (!pm_get_wakeup_count(&final_count, false))
  22. goto out;
  23. /*
  24. * If the wakeup occurred for an unknown reason, wait to prevent the
  25. * system from trying to suspend and waking up in a tight loop.
  26. */
  27. if (final_count == initial_count)
  28. schedule_timeout_uninterruptible(HZ / 2);
  29. out:
  30. queue_up_suspend_work();
  31. }
  32. static DECLARE_WORK(suspend_work, try_to_suspend);
  33. void queue_up_suspend_work(void)
  34. {
  35. if (autosleep_state > PM_SUSPEND_ON)
  36. queue_work(autosleep_wq, &suspend_work);
  37. }

3.poweroff

poweroff是通过sysrq处理程序(sysrq相关介绍参考linux内核中的sysrq-CSDN博客)来实现正常关闭机器电源的操作的。它的流程如下:

其代码如下:

  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * poweroff.c - sysrq handler to gracefully power down machine.
  4. */
  5. #include <linux/kernel.h>
  6. #include <linux/sysrq.h>
  7. #include <linux/init.h>
  8. #include <linux/pm.h>
  9. #include <linux/workqueue.h>
  10. #include <linux/reboot.h>
  11. #include <linux/cpumask.h>
  12. /*
  13. * When the user hits Sys-Rq o to power down the machine this is the
  14. * callback we use.
  15. */
  16. static void do_poweroff(struct work_struct *dummy)
  17. {
  18. kernel_power_off();
  19. }
  20. static DECLARE_WORK(poweroff_work, do_poweroff);
  21. static void handle_poweroff(int key)
  22. {
  23. /* run sysrq poweroff on boot cpu */
  24. schedule_work_on(cpumask_first(cpu_online_mask), &poweroff_work);
  25. }
  26. static const struct sysrq_key_op sysrq_poweroff_op = {
  27. .handler = handle_poweroff,
  28. .help_msg = "poweroff(o)",
  29. .action_msg = "Power Off",
  30. .enable_mask = SYSRQ_ENABLE_BOOT,
  31. };
  32. static int __init pm_sysrq_init(void)
  33. {
  34. register_sysrq_key('o', &sysrq_poweroff_op);
  35. return 0;
  36. }
  37. subsys_initcall(pm_sysrq_init);

当发出poweroff操作时,将向/proc/sysrq-trigger写入'o'操作,以此触发handle_poweroff处理函数,并最终执行kernel_power_off()函数关闭系统电源。

  1. /*
  2. * kernel/reboot.c
  3. */
  4. /**
  5. * kernel_power_off - power_off the system
  6. *
  7. * Shutdown everything and perform a clean system power_off.
  8. */
  9. void kernel_power_off(void)
  10. {
  11. kernel_shutdown_prepare(SYSTEM_POWER_OFF);
  12. do_kernel_power_off_prepare();
  13. migrate_to_reboot_cpu();
  14. syscore_shutdown();
  15. pr_emerg("Power down\n");
  16. kmsg_dump(KMSG_DUMP_SHUTDOWN);
  17. machine_power_off();
  18. }
  19. EXPORT_SYMBOL_GPL(kernel_power_off);

在kernel_power_off()函数中以此执行内核shutdown准备 -> 停用cpu -> syscore shutdown -> 打印关机日志 -> 机器关机。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/817960
推荐阅读
相关标签
  

闽ICP备14008679号