summaryrefslogtreecommitdiffstats
path: root/kernel/kexec.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/kexec.c')
-rw-r--r--kernel/kexec.c39
1 files changed, 39 insertions, 0 deletions
diff --git a/kernel/kexec.c b/kernel/kexec.c
index a0d920915b3..c8a4370e2a3 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -26,6 +26,10 @@
#include <linux/numa.h>
#include <linux/suspend.h>
#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/pm.h>
+#include <linux/cpu.h>
+#include <linux/console.h>
#include <asm/page.h>
#include <asm/uaccess.h>
@@ -1441,7 +1445,31 @@ int kernel_kexec(void)
if (kexec_image->preserve_context) {
#ifdef CONFIG_KEXEC_JUMP
+ mutex_lock(&pm_mutex);
+ pm_prepare_console();
+ error = freeze_processes();
+ if (error) {
+ error = -EBUSY;
+ goto Restore_console;
+ }
+ suspend_console();
+ error = device_suspend(PMSG_FREEZE);
+ if (error)
+ goto Resume_console;
+ error = disable_nonboot_cpus();
+ if (error)
+ goto Resume_devices;
local_irq_disable();
+ /* At this point, device_suspend() has been called,
+ * but *not* device_power_down(). We *must*
+ * device_power_down() 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 = device_power_down(PMSG_FREEZE);
+ if (error)
+ goto Enable_irqs;
save_processor_state();
#endif
} else {
@@ -1459,7 +1487,18 @@ int kernel_kexec(void)
if (kexec_image->preserve_context) {
#ifdef CONFIG_KEXEC_JUMP
restore_processor_state();
+ device_power_up(PMSG_RESTORE);
+ Enable_irqs:
local_irq_enable();
+ enable_nonboot_cpus();
+ Resume_devices:
+ device_resume(PMSG_RESTORE);
+ Resume_console:
+ resume_console();
+ thaw_processes();
+ Restore_console:
+ pm_restore_console();
+ mutex_unlock(&pm_mutex);
#endif
}