diff options
| -rw-r--r-- | arch/x86/include/asm/smp.h | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/smpboot.c | 2 | ||||
| -rw-r--r-- | arch/x86/power/cpu.c | 30 | ||||
| -rw-r--r-- | kernel/power/hibernate.c | 7 | ||||
| -rw-r--r-- | kernel/power/power.h | 2 |
5 files changed, 40 insertions, 2 deletions
diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h index 66b057306f40..7427ca895a27 100644 --- a/arch/x86/include/asm/smp.h +++ b/arch/x86/include/asm/smp.h | |||
| @@ -135,6 +135,7 @@ int native_cpu_up(unsigned int cpunum, struct task_struct *tidle); | |||
| 135 | int native_cpu_disable(void); | 135 | int native_cpu_disable(void); |
| 136 | int common_cpu_die(unsigned int cpu); | 136 | int common_cpu_die(unsigned int cpu); |
| 137 | void native_cpu_die(unsigned int cpu); | 137 | void native_cpu_die(unsigned int cpu); |
| 138 | void hlt_play_dead(void); | ||
| 138 | void native_play_dead(void); | 139 | void native_play_dead(void); |
| 139 | void play_dead_common(void); | 140 | void play_dead_common(void); |
| 140 | void wbinvd_on_cpu(int cpu); | 141 | void wbinvd_on_cpu(int cpu); |
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index fafe8b923cac..8264dfad9cf8 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c | |||
| @@ -1622,7 +1622,7 @@ static inline void mwait_play_dead(void) | |||
| 1622 | } | 1622 | } |
| 1623 | } | 1623 | } |
| 1624 | 1624 | ||
| 1625 | static inline void hlt_play_dead(void) | 1625 | void hlt_play_dead(void) |
| 1626 | { | 1626 | { |
| 1627 | if (__this_cpu_read(cpu_info.x86) >= 4) | 1627 | if (__this_cpu_read(cpu_info.x86) >= 4) |
| 1628 | wbinvd(); | 1628 | wbinvd(); |
diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c index d5f64996394a..b12c26e2e309 100644 --- a/arch/x86/power/cpu.c +++ b/arch/x86/power/cpu.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <linux/export.h> | 12 | #include <linux/export.h> |
| 13 | #include <linux/smp.h> | 13 | #include <linux/smp.h> |
| 14 | #include <linux/perf_event.h> | 14 | #include <linux/perf_event.h> |
| 15 | #include <linux/tboot.h> | ||
| 15 | 16 | ||
| 16 | #include <asm/pgtable.h> | 17 | #include <asm/pgtable.h> |
| 17 | #include <asm/proto.h> | 18 | #include <asm/proto.h> |
| @@ -266,6 +267,35 @@ void notrace restore_processor_state(void) | |||
| 266 | EXPORT_SYMBOL(restore_processor_state); | 267 | EXPORT_SYMBOL(restore_processor_state); |
| 267 | #endif | 268 | #endif |
| 268 | 269 | ||
| 270 | #if defined(CONFIG_HIBERNATION) && defined(CONFIG_HOTPLUG_CPU) | ||
| 271 | static void resume_play_dead(void) | ||
| 272 | { | ||
| 273 | play_dead_common(); | ||
| 274 | tboot_shutdown(TB_SHUTDOWN_WFS); | ||
| 275 | hlt_play_dead(); | ||
| 276 | } | ||
| 277 | |||
| 278 | int hibernate_resume_nonboot_cpu_disable(void) | ||
| 279 | { | ||
| 280 | void (*play_dead)(void) = smp_ops.play_dead; | ||
| 281 | int ret; | ||
| 282 | |||
| 283 | /* | ||
| 284 | * Ensure that MONITOR/MWAIT will not be used in the "play dead" loop | ||
| 285 | * during hibernate image restoration, because it is likely that the | ||
| 286 | * monitored address will be actually written to at that time and then | ||
| 287 | * the "dead" CPU will attempt to execute instructions again, but the | ||
| 288 | * address in its instruction pointer may not be possible to resolve | ||
| 289 | * any more at that point (the page tables used by it previously may | ||
| 290 | * have been overwritten by hibernate image data). | ||
| 291 | */ | ||
| 292 | smp_ops.play_dead = resume_play_dead; | ||
| 293 | ret = disable_nonboot_cpus(); | ||
| 294 | smp_ops.play_dead = play_dead; | ||
| 295 | return ret; | ||
| 296 | } | ||
| 297 | #endif | ||
| 298 | |||
| 269 | /* | 299 | /* |
| 270 | * When bsp_check() is called in hibernate and suspend, cpu hotplug | 300 | * When bsp_check() is called in hibernate and suspend, cpu hotplug |
| 271 | * is disabled already. So it's unnessary to handle race condition between | 301 | * is disabled already. So it's unnessary to handle race condition between |
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 51441d87f0b6..5f3523e18e46 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c | |||
| @@ -409,6 +409,11 @@ int hibernation_snapshot(int platform_mode) | |||
| 409 | goto Close; | 409 | goto Close; |
| 410 | } | 410 | } |
| 411 | 411 | ||
| 412 | int __weak hibernate_resume_nonboot_cpu_disable(void) | ||
| 413 | { | ||
| 414 | return disable_nonboot_cpus(); | ||
| 415 | } | ||
| 416 | |||
| 412 | /** | 417 | /** |
| 413 | * resume_target_kernel - Restore system state from a hibernation image. | 418 | * resume_target_kernel - Restore system state from a hibernation image. |
| 414 | * @platform_mode: Whether or not to use the platform driver. | 419 | * @platform_mode: Whether or not to use the platform driver. |
| @@ -433,7 +438,7 @@ static int resume_target_kernel(bool platform_mode) | |||
| 433 | if (error) | 438 | if (error) |
| 434 | goto Cleanup; | 439 | goto Cleanup; |
| 435 | 440 | ||
| 436 | error = disable_nonboot_cpus(); | 441 | error = hibernate_resume_nonboot_cpu_disable(); |
| 437 | if (error) | 442 | if (error) |
| 438 | goto Enable_cpus; | 443 | goto Enable_cpus; |
| 439 | 444 | ||
diff --git a/kernel/power/power.h b/kernel/power/power.h index 064963e89194..242d8b827dd5 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
| @@ -38,6 +38,8 @@ static inline char *check_image_kernel(struct swsusp_info *info) | |||
| 38 | } | 38 | } |
| 39 | #endif /* CONFIG_ARCH_HIBERNATION_HEADER */ | 39 | #endif /* CONFIG_ARCH_HIBERNATION_HEADER */ |
| 40 | 40 | ||
| 41 | extern int hibernate_resume_nonboot_cpu_disable(void); | ||
| 42 | |||
| 41 | /* | 43 | /* |
| 42 | * Keep some memory free so that I/O operations can succeed without paging | 44 | * Keep some memory free so that I/O operations can succeed without paging |
| 43 | * [Might this be more than 4 MB?] | 45 | * [Might this be more than 4 MB?] |
