📄 main.c
字号:
/* * kernel/power/main.c - PM subsystem core functionality. * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab * * This file is released under the GPLv2 * */#include <linux/module.h>#include <linux/suspend.h>#include <linux/kobject.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/console.h>#include <linux/cpu.h>#include <linux/resume-trace.h>#include <linux/freezer.h>#include <linux/vmstat.h>#include <linux/syscalls.h>#include <linux/ftrace.h>#include "power.h"DEFINE_MUTEX(pm_mutex);unsigned int pm_flags;EXPORT_SYMBOL(pm_flags);#ifdef CONFIG_PM_SLEEP/* Routines for PM-transition notifications */static BLOCKING_NOTIFIER_HEAD(pm_chain_head);int register_pm_notifier(struct notifier_block *nb){ return blocking_notifier_chain_register(&pm_chain_head, nb);}EXPORT_SYMBOL_GPL(register_pm_notifier);int unregister_pm_notifier(struct notifier_block *nb){ return blocking_notifier_chain_unregister(&pm_chain_head, nb);}EXPORT_SYMBOL_GPL(unregister_pm_notifier);int pm_notifier_call_chain(unsigned long val){ return (blocking_notifier_call_chain(&pm_chain_head, val, NULL) == NOTIFY_BAD) ? -EINVAL : 0;}#ifdef CONFIG_PM_DEBUGint pm_test_level = TEST_NONE;static int suspend_test(int level){ if (pm_test_level == level) { printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); mdelay(5000); return 1; } return 0;}static const char * const pm_tests[__TEST_AFTER_LAST] = { [TEST_NONE] = "none", [TEST_CORE] = "core", [TEST_CPUS] = "processors", [TEST_PLATFORM] = "platform", [TEST_DEVICES] = "devices", [TEST_FREEZER] = "freezer",};static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ char *s = buf; int level; for (level = TEST_FIRST; level <= TEST_MAX; level++) if (pm_tests[level]) { if (level == pm_test_level) s += sprintf(s, "[%s] ", pm_tests[level]); else s += sprintf(s, "%s ", pm_tests[level]); } if (s != buf) /* convert the last space to a newline */ *(s-1) = '\n'; return (s - buf);}static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n){ const char * const *s; int level; char *p; int len; int error = -EINVAL; p = memchr(buf, '\n', n); len = p ? p - buf : n; mutex_lock(&pm_mutex); level = TEST_FIRST; for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++) if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { pm_test_level = level; error = 0; break; } mutex_unlock(&pm_mutex); return error ? error : n;}power_attr(pm_test);#else /* !CONFIG_PM_DEBUG */static inline int suspend_test(int level) { return 0; }#endif /* !CONFIG_PM_DEBUG */#endif /* CONFIG_PM_SLEEP */#ifdef CONFIG_SUSPEND#ifdef CONFIG_PM_TEST_SUSPEND/* * We test the system suspend code by setting an RTC wakealarm a short * time in the future, then suspending. Suspending the devices won't * normally take long ... some systems only need a few milliseconds. * * The time it takes is system-specific though, so when we test this * during system bootup we allow a LOT of time. */#define TEST_SUSPEND_SECONDS 5static unsigned long suspend_test_start_time;static void suspend_test_start(void){ /* FIXME Use better timebase than "jiffies", ideally a clocksource. * What we want is a hardware counter that will work correctly even * during the irqs-are-off stages of the suspend/resume cycle... */ suspend_test_start_time = jiffies;}static void suspend_test_finish(const char *label){ long nj = jiffies - suspend_test_start_time; unsigned msec; msec = jiffies_to_msecs(abs(nj)); pr_info("PM: %s took %d.%03d seconds\n", label, msec / 1000, msec % 1000); /* Warning on suspend means the RTC alarm period needs to be * larger -- the system was sooo slooowwww to suspend that the * alarm (should have) fired before the system went to sleep! * * Warning on either suspend or resume also means the system * has some performance issues. The stack dump of a WARN_ON * is more likely to get the right attention than a printk... */ WARN_ON(msec > (TEST_SUSPEND_SECONDS * 1000));}#elsestatic void suspend_test_start(void){}static void suspend_test_finish(const char *label){}#endif/* This is just an arbitrary number */#define FREE_PAGE_NUMBER (100)static struct platform_suspend_ops *suspend_ops;/** * suspend_set_ops - Set the global suspend method table. * @ops: Pointer to ops structure. */void suspend_set_ops(struct platform_suspend_ops *ops){ mutex_lock(&pm_mutex); suspend_ops = ops; mutex_unlock(&pm_mutex);}/** * suspend_valid_only_mem - generic memory-only valid callback * * Platform drivers that implement mem suspend only and only need * to check for that in their .valid callback can use this instead * of rolling their own .valid callback. */int suspend_valid_only_mem(suspend_state_t state){ return state == PM_SUSPEND_MEM;}/** * suspend_prepare - Do prep work before entering low-power state. * * This is common code that is called for each state that we're entering. * Run suspend notifiers, allocate a console and stop all 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; 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(); Finish: pm_notifier_call_chain(PM_POST_SUSPEND); pm_restore_console(); return error;}/* default implementation */void __attribute__ ((weak)) arch_suspend_disable_irqs(void){ local_irq_disable();}/* default implementation */void __attribute__ ((weak)) arch_suspend_enable_irqs(void){ local_irq_enable();}/** * suspend_enter - enter the desired system sleep state. * @state: state to enter * * This function should be called after devices have been suspended. */static int suspend_enter(suspend_state_t state){ int error = 0; device_pm_lock(); 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; } if (!suspend_test(TEST_CORE)) error = suspend_ops->enter(state); device_power_up(PMSG_RESUME); Done: arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); device_pm_unlock(); return error;}/** * suspend_devices_and_enter - suspend devices and enter the desired system * sleep state. * @state: state to enter */int suspend_devices_and_enter(suspend_state_t state){ int error, ftrace_save; if (!suspend_ops) return -ENOSYS; if (suspend_ops->begin) { error = suspend_ops->begin(state); if (error) goto Close; } suspend_console(); ftrace_save = __ftrace_enabled_save(); 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"); __ftrace_enabled_restore(ftrace_save); resume_console(); Close: if (suspend_ops->end) suspend_ops->end(); return error;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -