Merge tag 'pm-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management updates for 3.4 from Rafael Wysocki:
 "Assorted extensions and fixes including:

  * Introduction of early/late suspend/hibernation device callbacks.
  * Generic PM domains extensions and fixes.
  * devfreq updates from Axel Lin and MyungJoo Ham.
  * Device PM QoS updates.
  * Fixes of concurrency problems with wakeup sources.
  * System suspend and hibernation fixes."

* tag 'pm-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (43 commits)
  PM / Domains: Check domain status during hibernation restore of devices
  PM / devfreq: add relation of recommended frequency.
  PM / shmobile: Make MTU2 driver use pm_genpd_dev_always_on()
  PM / shmobile: Make CMT driver use pm_genpd_dev_always_on()
  PM / shmobile: Make TMU driver use pm_genpd_dev_always_on()
  PM / Domains: Introduce "always on" device flag
  PM / Domains: Fix hibernation restore of devices, v2
  PM / Domains: Fix handling of wakeup devices during system resume
  sh_mmcif / PM: Use PM QoS latency constraint
  tmio_mmc / PM: Use PM QoS latency constraint
  PM / QoS: Make it possible to expose PM QoS latency constraints
  PM / Sleep: JBD and JBD2 missing set_freezable()
  PM / Domains: Fix include for PM_GENERIC_DOMAINS=n case
  PM / Freezer: Remove references to TIF_FREEZE in comments
  PM / Sleep: Add more wakeup source initialization routines
  PM / Hibernate: Enable usermodehelpers in hibernate() error path
  PM / Sleep: Make __pm_stay_awake() delete wakeup source timers
  PM / Sleep: Fix race conditions related to wakeup source timer function
  PM / Sleep: Fix possible infinite loop during wakeup source destruction
  PM / Hibernate: print physical addresses consistently with other parts of kernel
  ...
This commit is contained in:
Linus Torvalds
2012-03-21 10:15:51 -07:00
53 changed files with 1462 additions and 621 deletions

View File

@@ -424,7 +424,7 @@ void daemonize(const char *name, ...)
*/
exit_mm(current);
/*
* We don't want to have TIF_FREEZE set if the system-wide hibernation
* We don't want to get frozen, in case system-wide hibernation
* or suspend transition begins right now.
*/
current->flags |= (PF_NOFREEZE | PF_KTHREAD);

View File

@@ -99,9 +99,9 @@ static void fake_signal_wake_up(struct task_struct *p)
* freeze_task - send a freeze request to given task
* @p: task to send the request to
*
* If @p is freezing, the freeze request is sent by setting %TIF_FREEZE
* flag and either sending a fake signal to it or waking it up, depending
* on whether it has %PF_FREEZER_NOSIG set.
* If @p is freezing, the freeze request is sent either by sending a fake
* signal (if it's not a kernel thread) or waking it up (if it's a kernel
* thread).
*
* RETURNS:
* %false, if @p is not freezing or already frozen; %true, otherwise

View File

@@ -1546,13 +1546,13 @@ int kernel_kexec(void)
if (error)
goto Resume_console;
/* At this point, dpm_suspend_start() has been called,
* but *not* dpm_suspend_noirq(). We *must* call
* dpm_suspend_noirq() now. Otherwise, drivers for
* but *not* dpm_suspend_end(). We *must* call
* dpm_suspend_end() now. Otherwise, drivers for
* some devices (e.g. interrupt controllers) become
* desynchronized with the actual state of the
* hardware at resume time, and evil weirdness ensues.
*/
error = dpm_suspend_noirq(PMSG_FREEZE);
error = dpm_suspend_end(PMSG_FREEZE);
if (error)
goto Resume_devices;
error = disable_nonboot_cpus();
@@ -1579,7 +1579,7 @@ int kernel_kexec(void)
local_irq_enable();
Enable_cpus:
enable_nonboot_cpus();
dpm_resume_noirq(PMSG_RESTORE);
dpm_resume_start(PMSG_RESTORE);
Resume_devices:
dpm_resume_end(PMSG_RESTORE);
Resume_console:

View File

@@ -1,7 +1,8 @@
ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG
obj-$(CONFIG_PM) += main.o qos.o
obj-y += qos.o
obj-$(CONFIG_PM) += main.o
obj-$(CONFIG_VT_CONSOLE_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_SUSPEND) += suspend.o

View File

@@ -245,8 +245,8 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
* create_image - Create a hibernation image.
* @platform_mode: Whether or not to use the platform driver.
*
* Execute device drivers' .freeze_noirq() callbacks, create a hibernation image
* and execute the drivers' .thaw_noirq() callbacks.
* Execute device drivers' "late" and "noirq" freeze callbacks, create a
* hibernation image and run the drivers' "noirq" and "early" thaw callbacks.
*
* Control reappears in this routine after the subsequent restore.
*/
@@ -254,7 +254,7 @@ static int create_image(int platform_mode)
{
int error;
error = dpm_suspend_noirq(PMSG_FREEZE);
error = dpm_suspend_end(PMSG_FREEZE);
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting hibernation\n");
@@ -306,7 +306,7 @@ static int create_image(int platform_mode)
Platform_finish:
platform_finish(platform_mode);
dpm_resume_noirq(in_suspend ?
dpm_resume_start(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
return error;
@@ -343,13 +343,13 @@ int hibernation_snapshot(int platform_mode)
* successful freezer test.
*/
freezer_test_done = true;
goto Cleanup;
goto Thaw;
}
error = dpm_prepare(PMSG_FREEZE);
if (error) {
dpm_complete(PMSG_RECOVER);
goto Cleanup;
goto Thaw;
}
suspend_console();
@@ -385,6 +385,8 @@ int hibernation_snapshot(int platform_mode)
platform_end(platform_mode);
return error;
Thaw:
thaw_kernel_threads();
Cleanup:
swsusp_free();
goto Close;
@@ -394,16 +396,16 @@ int hibernation_snapshot(int platform_mode)
* resume_target_kernel - Restore system state from a hibernation image.
* @platform_mode: Whether or not to use the platform driver.
*
* Execute device drivers' .freeze_noirq() callbacks, restore the contents of
* highmem that have not been restored yet from the image and run the low-level
* code that will restore the remaining contents of memory and switch to the
* just restored target kernel.
* Execute device drivers' "noirq" and "late" freeze callbacks, restore the
* contents of highmem that have not been restored yet from the image and run
* the low-level code that will restore the remaining contents of memory and
* switch to the just restored target kernel.
*/
static int resume_target_kernel(bool platform_mode)
{
int error;
error = dpm_suspend_noirq(PMSG_QUIESCE);
error = dpm_suspend_end(PMSG_QUIESCE);
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting resume\n");
@@ -460,7 +462,7 @@ static int resume_target_kernel(bool platform_mode)
Cleanup:
platform_restore_cleanup(platform_mode);
dpm_resume_noirq(PMSG_RECOVER);
dpm_resume_start(PMSG_RECOVER);
return error;
}
@@ -518,7 +520,7 @@ int hibernation_platform_enter(void)
goto Resume_devices;
}
error = dpm_suspend_noirq(PMSG_HIBERNATE);
error = dpm_suspend_end(PMSG_HIBERNATE);
if (error)
goto Resume_devices;
@@ -549,7 +551,7 @@ int hibernation_platform_enter(void)
Platform_finish:
hibernation_ops->finish();
dpm_resume_noirq(PMSG_RESTORE);
dpm_resume_start(PMSG_RESTORE);
Resume_devices:
entering_platform_hibernation = false;
@@ -616,7 +618,7 @@ int hibernate(void)
/* Allocate memory management structures */
error = create_basic_memory_bitmaps();
if (error)
goto Exit;
goto Enable_umh;
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
@@ -624,15 +626,11 @@ int hibernate(void)
error = freeze_processes();
if (error)
goto Finish;
goto Free_bitmaps;
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
if (error)
if (error || freezer_test_done)
goto Thaw;
if (freezer_test_done) {
freezer_test_done = false;
goto Thaw;
}
if (in_suspend) {
unsigned int flags = 0;
@@ -657,8 +655,13 @@ int hibernate(void)
Thaw:
thaw_processes();
Finish:
/* Don't bother checking whether freezer_test_done is true */
freezer_test_done = false;
Free_bitmaps:
free_basic_memory_bitmaps();
Enable_umh:
usermodehelper_enable();
Exit:
pm_notifier_call_chain(PM_POST_HIBERNATION);

View File

@@ -165,16 +165,20 @@ static int suspend_stats_show(struct seq_file *s, void *unused)
last_errno %= REC_FAILED_NUM;
last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
last_step %= REC_FAILED_NUM;
seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
"%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
"%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
"success", suspend_stats.success,
"fail", suspend_stats.fail,
"failed_freeze", suspend_stats.failed_freeze,
"failed_prepare", suspend_stats.failed_prepare,
"failed_suspend", suspend_stats.failed_suspend,
"failed_suspend_late",
suspend_stats.failed_suspend_late,
"failed_suspend_noirq",
suspend_stats.failed_suspend_noirq,
"failed_resume", suspend_stats.failed_resume,
"failed_resume_early",
suspend_stats.failed_resume_early,
"failed_resume_noirq",
suspend_stats.failed_resume_noirq);
seq_printf(s, "failures:\n last_failed_dev:\t%-s\n",
@@ -287,16 +291,10 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
#ifdef CONFIG_SUSPEND
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
error = pm_suspend(state);
break;
}
if (state < PM_SUSPEND_MAX && *s) {
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else
suspend_stats.success++;
}
}
#endif

View File

@@ -177,13 +177,11 @@ extern const char *const pm_states[];
extern bool valid_state(suspend_state_t state);
extern int suspend_devices_and_enter(suspend_state_t state);
extern int enter_state(suspend_state_t state);
#else /* !CONFIG_SUSPEND */
static inline int suspend_devices_and_enter(suspend_state_t state)
{
return -ENOSYS;
}
static inline int enter_state(suspend_state_t state) { return -ENOSYS; }
static inline bool valid_state(suspend_state_t state) { return false; }
#endif /* !CONFIG_SUSPEND */
@@ -234,16 +232,14 @@ static inline int suspend_freeze_processes(void)
int error;
error = freeze_processes();
/*
* freeze_processes() automatically thaws every task if freezing
* fails. So we need not do anything extra upon error.
*/
if (error)
goto Finish;
return error;
error = freeze_kernel_threads();
/*
* freeze_kernel_threads() thaws only kernel threads upon freezing
* failure. So we have to thaw the userspace tasks ourselves.
@@ -251,7 +247,6 @@ static inline int suspend_freeze_processes(void)
if (error)
thaw_processes();
Finish:
return error;
}

View File

@@ -53,11 +53,9 @@ static int try_to_freeze_tasks(bool user_only)
* It is "frozen enough". If the task does wake
* up, it will immediately call try_to_freeze.
*
* Because freeze_task() goes through p's
* scheduler lock after setting TIF_FREEZE, it's
* guaranteed that either we see TASK_RUNNING or
* try_to_stop() after schedule() in ptrace/signal
* stop sees TIF_FREEZE.
* Because freeze_task() goes through p's scheduler lock, it's
* guaranteed that TASK_STOPPED/TRACED -> TASK_RUNNING
* transition can't race with task state testing here.
*/
if (!task_is_stopped_or_traced(p) &&
!freezer_should_skip(p))
@@ -98,13 +96,15 @@ static int try_to_freeze_tasks(bool user_only)
elapsed_csecs / 100, elapsed_csecs % 100,
todo - wq_busy, wq_busy);
read_lock(&tasklist_lock);
do_each_thread(g, p) {
if (!wakeup && !freezer_should_skip(p) &&
p != current && freezing(p) && !frozen(p))
sched_show_task(p);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
if (!wakeup) {
read_lock(&tasklist_lock);
do_each_thread(g, p) {
if (p != current && !freezer_should_skip(p)
&& freezing(p) && !frozen(p))
sched_show_task(p);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
}
} else {
printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,
elapsed_csecs % 100);

View File

@@ -469,21 +469,18 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
static int __init pm_qos_power_init(void)
{
int ret = 0;
int i;
ret = register_pm_qos_misc(&cpu_dma_pm_qos);
if (ret < 0) {
printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
return ret;
BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES);
for (i = 1; i < PM_QOS_NUM_CLASSES; i++) {
ret = register_pm_qos_misc(pm_qos_array[i]);
if (ret < 0) {
printk(KERN_ERR "pm_qos_param: %s setup failed\n",
pm_qos_array[i]->name);
return ret;
}
}
ret = register_pm_qos_misc(&network_lat_pm_qos);
if (ret < 0) {
printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
return ret;
}
ret = register_pm_qos_misc(&network_throughput_pm_qos);
if (ret < 0)
printk(KERN_ERR
"pm_qos_param: network_throughput setup failed\n");
return ret;
}

View File

@@ -711,9 +711,10 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
list_for_each_entry(region, &nosave_regions, list) {
unsigned long pfn;
pr_debug("PM: Marking nosave pages: %016lx - %016lx\n",
region->start_pfn << PAGE_SHIFT,
region->end_pfn << PAGE_SHIFT);
pr_debug("PM: Marking nosave pages: [mem %#010llx-%#010llx]\n",
(unsigned long long) region->start_pfn << PAGE_SHIFT,
((unsigned long long) region->end_pfn << PAGE_SHIFT)
- 1);
for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++)
if (pfn_valid(pfn)) {

View File

@@ -37,8 +37,8 @@ const char *const pm_states[PM_SUSPEND_MAX] = {
static const struct platform_suspend_ops *suspend_ops;
/**
* suspend_set_ops - Set the global suspend method table.
* @ops: Pointer to ops structure.
* suspend_set_ops - Set the global suspend method table.
* @ops: Suspend operations to use.
*/
void suspend_set_ops(const struct platform_suspend_ops *ops)
{
@@ -58,11 +58,11 @@ bool valid_state(suspend_state_t state)
}
/**
* suspend_valid_only_mem - generic memory-only valid callback
* 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.
* 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)
{
@@ -83,10 +83,11 @@ static int suspend_test(int level)
}
/**
* suspend_prepare - Do prep work before entering low-power state.
* suspend_prepare - Prepare for entering system sleep 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.
* Common code run for every system sleep state that can be entered (except for
* hibernation). Run suspend notifiers, allocate the "suspend" console and
* freeze processes.
*/
static int suspend_prepare(void)
{
@@ -131,9 +132,9 @@ void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
}
/**
* suspend_enter - enter the desired system sleep state.
* @state: State to enter
* @wakeup: Returns information that suspend should not be entered again.
* suspend_enter - Make the system enter the given sleep state.
* @state: System sleep state to enter.
* @wakeup: Returns information that the sleep state should not be re-entered.
*
* This function should be called after devices have been suspended.
*/
@@ -147,7 +148,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
goto Platform_finish;
}
error = dpm_suspend_noirq(PMSG_SUSPEND);
error = dpm_suspend_end(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down\n");
goto Platform_finish;
@@ -189,7 +190,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
if (suspend_ops->wake)
suspend_ops->wake();
dpm_resume_noirq(PMSG_RESUME);
dpm_resume_start(PMSG_RESUME);
Platform_finish:
if (suspend_ops->finish)
@@ -199,9 +200,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
}
/**
* suspend_devices_and_enter - suspend devices and enter the desired system
* sleep state.
* @state: state to enter
* suspend_devices_and_enter - Suspend devices and enter system sleep state.
* @state: System sleep state to enter.
*/
int suspend_devices_and_enter(suspend_state_t state)
{
@@ -251,10 +251,10 @@ int suspend_devices_and_enter(suspend_state_t state)
}
/**
* suspend_finish - Do final work before exiting suspend sequence.
* suspend_finish - Clean up before finishing the suspend sequence.
*
* Call platform code to clean up, restart processes, and free the
* console that we've allocated. This is not called for suspend-to-disk.
* Call platform code to clean up, restart processes, and free the console that
* we've allocated. This routine is not called for hibernation.
*/
static void suspend_finish(void)
{
@@ -265,16 +265,14 @@ static void suspend_finish(void)
}
/**
* enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
* enter_state - Do common work needed to enter system sleep state.
* @state: System sleep state to enter.
*
* 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).
* Make sure that no one else is trying to put the system into a sleep state.
* Fail if that's not the case. Otherwise, prepare for system suspend, make the
* system enter the given sleep state and clean up after wakeup.
*/
int enter_state(suspend_state_t state)
static int enter_state(suspend_state_t state)
{
int error;
@@ -310,24 +308,26 @@ int enter_state(suspend_state_t state)
}
/**
* pm_suspend - Externally visible function for suspending system.
* @state: Enumerated value of state to enter.
* pm_suspend - Externally visible function for suspending the system.
* @state: System sleep state to enter.
*
* Determine whether or not value is within range, get state
* structure, and enter (above).
* Check if the value of @state represents one of the supported states,
* execute enter_state() and update system suspend statistics.
*/
int pm_suspend(suspend_state_t state)
{
int ret;
if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) {
ret = enter_state(state);
if (ret) {
suspend_stats.fail++;
dpm_save_failed_errno(ret);
} else
suspend_stats.success++;
return ret;
int error;
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else {
suspend_stats.success++;
}
return -EINVAL;
return error;
}
EXPORT_SYMBOL(pm_suspend);

View File

@@ -249,16 +249,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
}
pm_restore_gfp_mask();
error = hibernation_snapshot(data->platform_support);
if (error) {
thaw_kernel_threads();
} else {
if (!error) {
error = put_user(in_suspend, (int __user *)arg);
if (!error && !freezer_test_done)
data->ready = 1;
if (freezer_test_done) {
freezer_test_done = false;
thaw_kernel_threads();
}
data->ready = !freezer_test_done && !error;
freezer_test_done = false;
}
break;