kexec jump: save/restore device state
[linux-2.6.git] / kernel / kexec.c
index a0d9209..c8a4370 100644 (file)
 #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
        }