Merge branches 'pm-sleep' and 'pm-tools'

* pm-sleep:
  PM / hibernate: Introduce test_resume mode for hibernation
  x86 / hibernate: Use hlt_play_dead() when resuming from hibernation
  PM / hibernate: Image data protection during restoration
  PM / hibernate: Add missing braces in __register_nosave_region()
  PM / hibernate: Clean up comments in snapshot.c
  PM / hibernate: Clean up function headers in snapshot.c
  PM / hibernate: Add missing braces in hibernate_setup()
  PM / hibernate: Recycle safe pages after image restoration
  PM / hibernate: Simplify mark_unsafe_pages()
  PM / hibernate: Do not free preallocated safe pages during image restore
  PM / suspend: show workqueue state in suspend flow
  PM / sleep: make PM notifiers called symmetrically
  PM / sleep: Make pm_prepare_console() return void
  PM / Hibernate: Don't let kasan instrument snapshot.c

* pm-tools:
  PM / tools: scripts: AnalyzeSuspend v4.2
  tools/turbostat: allow user to alter DESTDIR and PREFIX
This commit is contained in:
Rafael J. Wysocki 2016-07-25 13:44:32 +02:00
commit 7f234a4d8a
18 changed files with 3153 additions and 1655 deletions

View File

@ -3594,6 +3594,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
present during boot. present during boot.
nocompress Don't compress/decompress hibernation images. nocompress Don't compress/decompress hibernation images.
no Disable hibernation and resume. no Disable hibernation and resume.
protect_image Turn on image protection during restoration
(that will set all pages holding image data
during restoration read-only).
retain_initrd [RAM] Keep initrd memory after extraction retain_initrd [RAM] Keep initrd memory after extraction

View File

@ -135,6 +135,7 @@ int native_cpu_up(unsigned int cpunum, struct task_struct *tidle);
int native_cpu_disable(void); int native_cpu_disable(void);
int common_cpu_die(unsigned int cpu); int common_cpu_die(unsigned int cpu);
void native_cpu_die(unsigned int cpu); void native_cpu_die(unsigned int cpu);
void hlt_play_dead(void);
void native_play_dead(void); void native_play_dead(void);
void play_dead_common(void); void play_dead_common(void);
void wbinvd_on_cpu(int cpu); void wbinvd_on_cpu(int cpu);

View File

@ -1622,7 +1622,7 @@ static inline void mwait_play_dead(void)
} }
} }
static inline void hlt_play_dead(void) void hlt_play_dead(void)
{ {
if (__this_cpu_read(cpu_info.x86) >= 4) if (__this_cpu_read(cpu_info.x86) >= 4)
wbinvd(); wbinvd();

View File

@ -12,6 +12,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/tboot.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/proto.h> #include <asm/proto.h>
@ -266,6 +267,35 @@ void notrace restore_processor_state(void)
EXPORT_SYMBOL(restore_processor_state); EXPORT_SYMBOL(restore_processor_state);
#endif #endif
#if defined(CONFIG_HIBERNATION) && defined(CONFIG_HOTPLUG_CPU)
static void resume_play_dead(void)
{
play_dead_common();
tboot_shutdown(TB_SHUTDOWN_WFS);
hlt_play_dead();
}
int hibernate_resume_nonboot_cpu_disable(void)
{
void (*play_dead)(void) = smp_ops.play_dead;
int ret;
/*
* Ensure that MONITOR/MWAIT will not be used in the "play dead" loop
* during hibernate image restoration, because it is likely that the
* monitored address will be actually written to at that time and then
* the "dead" CPU will attempt to execute instructions again, but the
* address in its instruction pointer may not be possible to resolve
* any more at that point (the page tables used by it previously may
* have been overwritten by hibernate image data).
*/
smp_ops.play_dead = resume_play_dead;
ret = disable_nonboot_cpus();
smp_ops.play_dead = play_dead;
return ret;
}
#endif
/* /*
* When bsp_check() is called in hibernate and suspend, cpu hotplug * When bsp_check() is called in hibernate and suspend, cpu hotplug
* is disabled already. So it's unnessary to handle race condition between * is disabled already. So it's unnessary to handle race condition between

View File

@ -18,12 +18,11 @@ static inline void pm_set_vt_switch(int do_switch)
#endif #endif
#ifdef CONFIG_VT_CONSOLE_SLEEP #ifdef CONFIG_VT_CONSOLE_SLEEP
extern int pm_prepare_console(void); extern void pm_prepare_console(void);
extern void pm_restore_console(void); extern void pm_restore_console(void);
#else #else
static inline int pm_prepare_console(void) static inline void pm_prepare_console(void)
{ {
return 0;
} }
static inline void pm_restore_console(void) static inline void pm_restore_console(void)

View File

@ -1,6 +1,8 @@
ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG
KASAN_SANITIZE_snapshot.o := n
obj-y += qos.o obj-y += qos.o
obj-$(CONFIG_PM) += main.o obj-$(CONFIG_PM) += main.o
obj-$(CONFIG_VT_CONSOLE_SLEEP) += console.o obj-$(CONFIG_VT_CONSOLE_SLEEP) += console.o

View File

@ -126,17 +126,17 @@ out:
return ret; return ret;
} }
int pm_prepare_console(void) void pm_prepare_console(void)
{ {
if (!pm_vt_switch()) if (!pm_vt_switch())
return 0; return;
orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
if (orig_fgconsole < 0) if (orig_fgconsole < 0)
return 1; return;
orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE); orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
return 0; return;
} }
void pm_restore_console(void) void pm_restore_console(void)

View File

@ -52,6 +52,7 @@ enum {
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
HIBERNATION_SUSPEND, HIBERNATION_SUSPEND,
#endif #endif
HIBERNATION_TEST_RESUME,
/* keep last */ /* keep last */
__HIBERNATION_AFTER_LAST __HIBERNATION_AFTER_LAST
}; };
@ -409,6 +410,11 @@ int hibernation_snapshot(int platform_mode)
goto Close; goto Close;
} }
int __weak hibernate_resume_nonboot_cpu_disable(void)
{
return disable_nonboot_cpus();
}
/** /**
* resume_target_kernel - Restore system state from a hibernation image. * resume_target_kernel - Restore system state from a hibernation image.
* @platform_mode: Whether or not to use the platform driver. * @platform_mode: Whether or not to use the platform driver.
@ -433,7 +439,7 @@ static int resume_target_kernel(bool platform_mode)
if (error) if (error)
goto Cleanup; goto Cleanup;
error = disable_nonboot_cpus(); error = hibernate_resume_nonboot_cpu_disable();
if (error) if (error)
goto Enable_cpus; goto Enable_cpus;
@ -642,12 +648,39 @@ static void power_down(void)
cpu_relax(); cpu_relax();
} }
static int load_image_and_restore(void)
{
int error;
unsigned int flags;
pr_debug("PM: Loading hibernation image.\n");
lock_device_hotplug();
error = create_basic_memory_bitmaps();
if (error)
goto Unlock;
error = swsusp_read(&flags);
swsusp_close(FMODE_READ);
if (!error)
hibernation_restore(flags & SF_PLATFORM_MODE);
printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
swsusp_free();
free_basic_memory_bitmaps();
Unlock:
unlock_device_hotplug();
return error;
}
/** /**
* hibernate - Carry out system hibernation, including saving the image. * hibernate - Carry out system hibernation, including saving the image.
*/ */
int hibernate(void) int hibernate(void)
{ {
int error; int error, nr_calls = 0;
bool snapshot_test = false;
if (!hibernation_available()) { if (!hibernation_available()) {
pr_debug("PM: Hibernation not available.\n"); pr_debug("PM: Hibernation not available.\n");
@ -662,9 +695,11 @@ int hibernate(void)
} }
pm_prepare_console(); pm_prepare_console();
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
if (error) if (error) {
nr_calls--;
goto Exit; goto Exit;
}
printk(KERN_INFO "PM: Syncing filesystems ... "); printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync(); sys_sync();
@ -697,8 +732,12 @@ int hibernate(void)
pr_debug("PM: writing image.\n"); pr_debug("PM: writing image.\n");
error = swsusp_write(flags); error = swsusp_write(flags);
swsusp_free(); swsusp_free();
if (!error) if (!error) {
power_down(); if (hibernation_mode == HIBERNATION_TEST_RESUME)
snapshot_test = true;
else
power_down();
}
in_suspend = 0; in_suspend = 0;
pm_restore_gfp_mask(); pm_restore_gfp_mask();
} else { } else {
@ -709,12 +748,18 @@ int hibernate(void)
free_basic_memory_bitmaps(); free_basic_memory_bitmaps();
Thaw: Thaw:
unlock_device_hotplug(); unlock_device_hotplug();
if (snapshot_test) {
pr_debug("PM: Checking hibernation image\n");
error = swsusp_check();
if (!error)
error = load_image_and_restore();
}
thaw_processes(); thaw_processes();
/* Don't bother checking whether freezer_test_done is true */ /* Don't bother checking whether freezer_test_done is true */
freezer_test_done = false; freezer_test_done = false;
Exit: Exit:
pm_notifier_call_chain(PM_POST_HIBERNATION); __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
pm_restore_console(); pm_restore_console();
atomic_inc(&snapshot_device_available); atomic_inc(&snapshot_device_available);
Unlock: Unlock:
@ -740,8 +785,7 @@ int hibernate(void)
*/ */
static int software_resume(void) static int software_resume(void)
{ {
int error; int error, nr_calls = 0;
unsigned int flags;
/* /*
* If the user said "noresume".. bail out early. * If the user said "noresume".. bail out early.
@ -827,35 +871,20 @@ static int software_resume(void)
} }
pm_prepare_console(); pm_prepare_console();
error = pm_notifier_call_chain(PM_RESTORE_PREPARE); error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
if (error) if (error) {
nr_calls--;
goto Close_Finish; goto Close_Finish;
}
pr_debug("PM: Preparing processes for restore.\n"); pr_debug("PM: Preparing processes for restore.\n");
error = freeze_processes(); error = freeze_processes();
if (error) if (error)
goto Close_Finish; goto Close_Finish;
error = load_image_and_restore();
pr_debug("PM: Loading hibernation image.\n");
lock_device_hotplug();
error = create_basic_memory_bitmaps();
if (error)
goto Thaw;
error = swsusp_read(&flags);
swsusp_close(FMODE_READ);
if (!error)
hibernation_restore(flags & SF_PLATFORM_MODE);
printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
swsusp_free();
free_basic_memory_bitmaps();
Thaw:
unlock_device_hotplug();
thaw_processes(); thaw_processes();
Finish: Finish:
pm_notifier_call_chain(PM_POST_RESTORE); __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
pm_restore_console(); pm_restore_console();
atomic_inc(&snapshot_device_available); atomic_inc(&snapshot_device_available);
/* For success case, the suspend path will release the lock */ /* For success case, the suspend path will release the lock */
@ -878,6 +907,7 @@ static const char * const hibernation_modes[] = {
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
[HIBERNATION_SUSPEND] = "suspend", [HIBERNATION_SUSPEND] = "suspend",
#endif #endif
[HIBERNATION_TEST_RESUME] = "test_resume",
}; };
/* /*
@ -924,6 +954,7 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
case HIBERNATION_SUSPEND: case HIBERNATION_SUSPEND:
#endif #endif
case HIBERNATION_TEST_RESUME:
break; break;
case HIBERNATION_PLATFORM: case HIBERNATION_PLATFORM:
if (hibernation_ops) if (hibernation_ops)
@ -970,6 +1001,7 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
case HIBERNATION_SUSPEND: case HIBERNATION_SUSPEND:
#endif #endif
case HIBERNATION_TEST_RESUME:
hibernation_mode = mode; hibernation_mode = mode;
break; break;
case HIBERNATION_PLATFORM: case HIBERNATION_PLATFORM:
@ -1115,13 +1147,16 @@ static int __init resume_offset_setup(char *str)
static int __init hibernate_setup(char *str) static int __init hibernate_setup(char *str)
{ {
if (!strncmp(str, "noresume", 8)) if (!strncmp(str, "noresume", 8)) {
noresume = 1; noresume = 1;
else if (!strncmp(str, "nocompress", 10)) } else if (!strncmp(str, "nocompress", 10)) {
nocompress = 1; nocompress = 1;
else if (!strncmp(str, "no", 2)) { } else if (!strncmp(str, "no", 2)) {
noresume = 1; noresume = 1;
nohibernate = 1; nohibernate = 1;
} else if (IS_ENABLED(CONFIG_DEBUG_RODATA)
&& !strncmp(str, "protect_image", 13)) {
enable_restore_image_protection();
} }
return 1; return 1;
} }

View File

@ -38,12 +38,19 @@ int unregister_pm_notifier(struct notifier_block *nb)
} }
EXPORT_SYMBOL_GPL(unregister_pm_notifier); EXPORT_SYMBOL_GPL(unregister_pm_notifier);
int pm_notifier_call_chain(unsigned long val) int __pm_notifier_call_chain(unsigned long val, int nr_to_call, int *nr_calls)
{ {
int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL); int ret;
ret = __blocking_notifier_call_chain(&pm_chain_head, val, NULL,
nr_to_call, nr_calls);
return notifier_to_errno(ret); return notifier_to_errno(ret);
} }
int pm_notifier_call_chain(unsigned long val)
{
return __pm_notifier_call_chain(val, -1, NULL);
}
/* If set, devices may be suspended and resumed asynchronously. */ /* If set, devices may be suspended and resumed asynchronously. */
int pm_async_enabled = 1; int pm_async_enabled = 1;

View File

@ -38,6 +38,8 @@ static inline char *check_image_kernel(struct swsusp_info *info)
} }
#endif /* CONFIG_ARCH_HIBERNATION_HEADER */ #endif /* CONFIG_ARCH_HIBERNATION_HEADER */
extern int hibernate_resume_nonboot_cpu_disable(void);
/* /*
* Keep some memory free so that I/O operations can succeed without paging * Keep some memory free so that I/O operations can succeed without paging
* [Might this be more than 4 MB?] * [Might this be more than 4 MB?]
@ -59,6 +61,13 @@ extern int hibernation_snapshot(int platform_mode);
extern int hibernation_restore(int platform_mode); extern int hibernation_restore(int platform_mode);
extern int hibernation_platform_enter(void); extern int hibernation_platform_enter(void);
#ifdef CONFIG_DEBUG_RODATA
/* kernel/power/snapshot.c */
extern void enable_restore_image_protection(void);
#else
static inline void enable_restore_image_protection(void) {}
#endif /* CONFIG_DEBUG_RODATA */
#else /* !CONFIG_HIBERNATION */ #else /* !CONFIG_HIBERNATION */
static inline void hibernate_reserved_size_init(void) {} static inline void hibernate_reserved_size_init(void) {}
@ -200,6 +209,8 @@ static inline void suspend_test_finish(const char *label) {}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/* kernel/power/main.c */ /* kernel/power/main.c */
extern int __pm_notifier_call_chain(unsigned long val, int nr_to_call,
int *nr_calls);
extern int pm_notifier_call_chain(unsigned long val); extern int pm_notifier_call_chain(unsigned long val);
#endif #endif

View File

@ -89,6 +89,9 @@ static int try_to_freeze_tasks(bool user_only)
elapsed_msecs / 1000, elapsed_msecs % 1000, elapsed_msecs / 1000, elapsed_msecs % 1000,
todo - wq_busy, wq_busy); todo - wq_busy, wq_busy);
if (wq_busy)
show_workqueue_state();
if (!wakeup) { if (!wakeup) {
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
for_each_process_thread(g, p) { for_each_process_thread(g, p) {

File diff suppressed because it is too large Load Diff

View File

@ -266,16 +266,18 @@ static int suspend_test(int level)
*/ */
static int suspend_prepare(suspend_state_t state) static int suspend_prepare(suspend_state_t state)
{ {
int error; int error, nr_calls = 0;
if (!sleep_state_supported(state)) if (!sleep_state_supported(state))
return -EPERM; return -EPERM;
pm_prepare_console(); pm_prepare_console();
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
if (error) if (error) {
nr_calls--;
goto Finish; goto Finish;
}
trace_suspend_resume(TPS("freeze_processes"), 0, true); trace_suspend_resume(TPS("freeze_processes"), 0, true);
error = suspend_freeze_processes(); error = suspend_freeze_processes();
@ -286,7 +288,7 @@ static int suspend_prepare(suspend_state_t state)
suspend_stats.failed_freeze++; suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE); dpm_save_failed_step(SUSPEND_FREEZE);
Finish: Finish:
pm_notifier_call_chain(PM_POST_SUSPEND); __pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL);
pm_restore_console(); pm_restore_console();
return error; return error;
} }

View File

@ -348,6 +348,12 @@ static int swsusp_swap_check(void)
if (res < 0) if (res < 0)
blkdev_put(hib_resume_bdev, FMODE_WRITE); blkdev_put(hib_resume_bdev, FMODE_WRITE);
/*
* Update the resume device to the one actually used,
* so the test_resume mode can use it in case it is
* invoked from hibernate() to test the snapshot.
*/
swsusp_resume_device = hib_resume_bdev->bd_dev;
return res; return res;
} }

View File

@ -47,7 +47,7 @@ atomic_t snapshot_device_available = ATOMIC_INIT(1);
static int snapshot_open(struct inode *inode, struct file *filp) static int snapshot_open(struct inode *inode, struct file *filp)
{ {
struct snapshot_data *data; struct snapshot_data *data;
int error; int error, nr_calls = 0;
if (!hibernation_available()) if (!hibernation_available())
return -EPERM; return -EPERM;
@ -74,9 +74,9 @@ static int snapshot_open(struct inode *inode, struct file *filp)
swap_type_of(swsusp_resume_device, 0, NULL) : -1; swap_type_of(swsusp_resume_device, 0, NULL) : -1;
data->mode = O_RDONLY; data->mode = O_RDONLY;
data->free_bitmaps = false; data->free_bitmaps = false;
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
if (error) if (error)
pm_notifier_call_chain(PM_POST_HIBERNATION); __pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL);
} else { } else {
/* /*
* Resuming. We may need to wait for the image device to * Resuming. We may need to wait for the image device to
@ -86,13 +86,15 @@ static int snapshot_open(struct inode *inode, struct file *filp)
data->swap = -1; data->swap = -1;
data->mode = O_WRONLY; data->mode = O_WRONLY;
error = pm_notifier_call_chain(PM_RESTORE_PREPARE); error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
if (!error) { if (!error) {
error = create_basic_memory_bitmaps(); error = create_basic_memory_bitmaps();
data->free_bitmaps = !error; data->free_bitmaps = !error;
} } else
nr_calls--;
if (error) if (error)
pm_notifier_call_chain(PM_POST_RESTORE); __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
} }
if (error) if (error)
atomic_inc(&snapshot_device_available); atomic_inc(&snapshot_device_available);

View File

@ -4369,8 +4369,8 @@ static void show_pwq(struct pool_workqueue *pwq)
/** /**
* show_workqueue_state - dump workqueue state * show_workqueue_state - dump workqueue state
* *
* Called from a sysrq handler and prints out all busy workqueues and * Called from a sysrq handler or try_to_freeze_tasks() and prints out
* pools. * all busy workqueues and pools.
*/ */
void show_workqueue_state(void) void show_workqueue_state(void)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
CC = $(CROSS_COMPILE)gcc CC = $(CROSS_COMPILE)gcc
BUILD_OUTPUT := $(CURDIR) BUILD_OUTPUT := $(CURDIR)
PREFIX := /usr PREFIX ?= /usr
DESTDIR := DESTDIR ?=
ifeq ("$(origin O)", "command line") ifeq ("$(origin O)", "command line")
BUILD_OUTPUT := $(O) BUILD_OUTPUT := $(O)