int hibernation_snapshot(int platform_mode) { int error; /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) return error; error = platform_begin(platform_mode); if (error) goto Close; suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) goto Recover_platform; if (hibernation_test(TEST_DEVICES)) goto Recover_platform; error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Finish; error = disable_nonboot_cpus(); if (!error) { if (hibernation_test(TEST_CPUS)) goto Enable_cpus; if (hibernation_testmode(HIBERNATION_TEST)) goto Enable_cpus; error = create_image(platform_mode); /* Control returns here after successful restore */ } Enable_cpus: enable_nonboot_cpus(); Finish: platform_finish(platform_mode); Resume_devices: device_resume(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); resume_console(); Close: platform_end(platform_mode); return error; Recover_platform: platform_recover(platform_mode); goto Resume_devices; }
int hibernation_snapshot(int platform_mode) { int error; /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) return error; error = platform_start(platform_mode); if (error) return error; suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) goto Resume_console; error = platform_pre_snapshot(platform_mode); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (!error) { if (hibernation_mode != HIBERNATION_TEST) { in_suspend = 1; error = create_image(platform_mode); /* Control returns here after successful restore */ } else { printk("swsusp debug: Waiting for 5 seconds.\n"); mdelay(5000); } } enable_nonboot_cpus(); Resume_devices: platform_finish(platform_mode); device_resume(); Resume_console: resume_console(); return error; }
/** * create_image - Create a hibernation image. * @platform_mode: Whether or not to use the platform driver. * * 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. */ static int create_image(int platform_mode) { int error; error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); return error; } error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Platform_finish; error = disable_nonboot_cpus(); if (error || hibernation_test(TEST_CPUS)) goto Enable_cpus; local_irq_disable(); error = syscore_suspend(); if (error) { pr_err("Some system devices failed to power down, aborting hibernation\n"); goto Enable_irqs; } if (hibernation_test(TEST_CORE) || pm_wakeup_pending()) goto Power_up; in_suspend = 1; save_processor_state(); trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true); error = swsusp_arch_suspend(); /* Restore control flow magically appears here */ restore_processor_state(); trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false); if (error) pr_err("Error %d creating hibernation image\n", error); if (!in_suspend) { events_check_enabled = false; clear_free_pages(); } platform_leave(platform_mode); Power_up: syscore_resume(); Enable_irqs: local_irq_enable(); Enable_cpus: enable_nonboot_cpus(); Platform_finish: platform_finish(platform_mode); dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); return error; }
static int create_image(int platform_mode) { int error; error = arch_prepare_suspend(); if (error) return error; /* At this point, dpm_suspend_start() has been called, but *not* * dpm_suspend_noirq(). We *must* call dpm_suspend_noirq() 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); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting hibernation\n"); return error; } error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Platform_finish; error = disable_nonboot_cpus(); if (error || hibernation_test(TEST_CPUS) || hibernation_testmode(HIBERNATION_TEST)) goto Enable_cpus; local_irq_disable(); error = sysdev_suspend(PMSG_FREEZE); if (error) { printk(KERN_ERR "PM: Some system devices failed to power down, " "aborting hibernation\n"); goto Enable_irqs; } if (hibernation_test(TEST_CORE) || !pm_check_wakeup_events()) goto Power_up; in_suspend = 1; save_processor_state(); error = swsusp_arch_suspend(); if (error) printk(KERN_ERR "PM: Error %d creating hibernation image\n", error); /* Restore control flow magically appears here */ restore_processor_state(); if (!in_suspend) { events_check_enabled = false; platform_leave(platform_mode); } Power_up: sysdev_resume(); /* NOTE: dpm_resume_noirq() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ Enable_irqs: local_irq_enable(); Enable_cpus: enable_nonboot_cpus(); Platform_finish: platform_finish(platform_mode); dpm_resume_noirq(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); return error; }
/** * 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. * * Control reappears in this routine after the subsequent restore. */ static int create_image(int platform_mode) { int error; error = dpm_suspend_noirq(PMSG_FREEZE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting hibernation\n"); return error; } error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Platform_finish; error = disable_nonboot_cpus(); if (error || hibernation_test(TEST_CPUS) || hibernation_testmode(HIBERNATION_TEST)) goto Enable_cpus; local_irq_disable(); error = syscore_suspend(); if (error) { printk(KERN_ERR "PM: Some system devices failed to power down, " "aborting hibernation\n"); goto Enable_irqs; } if (hibernation_test(TEST_CORE) || pm_wakeup_pending()) goto Power_up; in_suspend = 1; save_processor_state(); error = swsusp_arch_suspend(); if (error) printk(KERN_ERR "PM: Error %d creating hibernation image\n", error); /* Restore control flow magically appears here */ restore_processor_state(); if (!in_suspend) { events_check_enabled = false; platform_leave(platform_mode); } Power_up: syscore_resume(); Enable_irqs: local_irq_enable(); Enable_cpus: enable_nonboot_cpus(); Platform_finish: platform_finish(platform_mode); dpm_resume_noirq(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); return error; }
/** * toi_go_atomic - do the actual atomic copy/restore * @state: The state to use for dpm_suspend_start & power_down calls. * @suspend_time: Whether we're suspending or resuming. **/ int toi_go_atomic(pm_message_t state, int suspend_time) { if (suspend_time) { if (platform_begin(1)) { set_abort_result(TOI_PLATFORM_PREP_FAILED); toi_end_atomic(ATOMIC_STEP_PLATFORM_END, suspend_time, 3); return 1; } if (dpm_prepare(PMSG_FREEZE)) { set_abort_result(TOI_DPM_PREPARE_FAILED); dpm_complete(PMSG_RECOVER); toi_end_atomic(ATOMIC_STEP_PLATFORM_END, suspend_time, 3); return 1; } } suspend_console(); ftrace_stop(); pm_restrict_gfp_mask(); if (suspend_time) { if (dpm_suspend(state)) { set_abort_result(TOI_DPM_SUSPEND_FAILED); toi_end_atomic(ATOMIC_STEP_DEVICE_RESUME, suspend_time, 3); return 1; } } else { if (dpm_suspend_start(state)) { set_abort_result(TOI_DPM_SUSPEND_FAILED); toi_end_atomic(ATOMIC_STEP_DEVICE_RESUME, suspend_time, 3); return 1; } } /* At this point, dpm_suspend_start() has been called, but *not* * dpm_suspend_noirq(). We *must* dpm_suspend_noirq() 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. */ if (dpm_suspend_end(state)) { set_abort_result(TOI_DEVICE_REFUSED); toi_end_atomic(ATOMIC_STEP_DEVICE_RESUME, suspend_time, 1); return 1; } if (suspend_time) { if (platform_pre_snapshot(1)) set_abort_result(TOI_PRE_SNAPSHOT_FAILED); } else { if (platform_pre_restore(1)) set_abort_result(TOI_PRE_RESTORE_FAILED); } if (test_result_state(TOI_ABORTED)) { toi_end_atomic(ATOMIC_STEP_PLATFORM_FINISH, suspend_time, 1); return 1; } if (test_action_state(TOI_LATE_CPU_HOTPLUG)) { if (disable_nonboot_cpus()) { set_abort_result(TOI_CPU_HOTPLUG_FAILED); toi_end_atomic(ATOMIC_STEP_CPU_HOTPLUG, suspend_time, 1); return 1; } } local_irq_disable(); if (syscore_suspend()) { set_abort_result(TOI_SYSCORE_REFUSED); toi_end_atomic(ATOMIC_STEP_IRQS, suspend_time, 1); return 1; } if (suspend_time && pm_wakeup_pending()) { set_abort_result(TOI_WAKEUP_EVENT); toi_end_atomic(ATOMIC_STEP_SYSCORE_RESUME, suspend_time, 1); return 1; } return 0; }
/** * toi_go_atomic - do the actual atomic copy/restore * @state: The state to use for dpm_suspend_start & power_down calls. * @suspend_time: Whether we're suspending or resuming. **/ int toi_go_atomic(pm_message_t state, int suspend_time) { if (suspend_time) { if (platform_begin(1)) { set_abort_result(TOI_PLATFORM_PREP_FAILED); toi_end_atomic(ATOMIC_STEP_PLATFORM_END, suspend_time, 3); hib_log("FAILED @line:%d suspend(%d) pm_state(%d)\n", __LINE__, suspend_time, state.event); return 1; } if (dpm_prepare(PMSG_FREEZE)) { set_abort_result(TOI_DPM_PREPARE_FAILED); dpm_complete(PMSG_RECOVER); toi_end_atomic(ATOMIC_STEP_PLATFORM_END, suspend_time, 3); hib_log("FAILED @line:%d suspend(%d) pm_state(%d)\n", __LINE__, suspend_time, state.event); return 1; } } suspend_console(); ftrace_stop(); pm_restrict_gfp_mask(); if (suspend_time) { #if 0 /* FIXME: jonathan.jmchen: trick code here to let dpm_suspend succeeded, NEED to find out the root cause!! */ if (events_check_enabled) { hib_log("play trick here set events_check_enabled(%d) = false!!\n", events_check_enabled); events_check_enabled = false; } #endif if (dpm_suspend(state)) { set_abort_result(TOI_DPM_SUSPEND_FAILED); toi_end_atomic(ATOMIC_STEP_DEVICE_RESUME, suspend_time, 3); hib_log("FAILED @line:%d suspend(%d) pm_state(%d) toi_result(0x%#lx)\n", __LINE__, suspend_time, state.event, toi_result); return 1; } } else { if (dpm_suspend_start(state)) { set_abort_result(TOI_DPM_SUSPEND_FAILED); toi_end_atomic(ATOMIC_STEP_DEVICE_RESUME, suspend_time, 3); hib_log("FAILED @line:%d suspend(%d) pm_state(%d) toi_result(0x%#lx)\n", __LINE__, suspend_time, state.event, toi_result); return 1; } } /* At this point, dpm_suspend_start() has been called, but *not* * dpm_suspend_noirq(). We *must* dpm_suspend_noirq() 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. */ if (dpm_suspend_end(state)) { set_abort_result(TOI_DEVICE_REFUSED); toi_end_atomic(ATOMIC_STEP_DEVICE_RESUME, suspend_time, 1); hib_log("FAILED @line:%d suspend(%d) pm_state(%d) toi_result(0x%#lx)\n", __LINE__, suspend_time, state.event, toi_result); return 1; } if (suspend_time) { if (platform_pre_snapshot(1)) set_abort_result(TOI_PRE_SNAPSHOT_FAILED); } else { if (platform_pre_restore(1)) set_abort_result(TOI_PRE_RESTORE_FAILED); } if (test_result_state(TOI_ABORTED)) { toi_end_atomic(ATOMIC_STEP_PLATFORM_FINISH, suspend_time, 1); hib_log("FAILED @line:%d suspend(%d) pm_state(%d) toi_result(0x%#lx)\n", __LINE__, suspend_time, state.event, toi_result); return 1; } if (test_action_state(TOI_LATE_CPU_HOTPLUG)) { if (disable_nonboot_cpus()) { set_abort_result(TOI_CPU_HOTPLUG_FAILED); toi_end_atomic(ATOMIC_STEP_CPU_HOTPLUG, suspend_time, 1); hib_log("FAILED @line:%d suspend(%d) pm_state(%d) toi_result(0x%#lx)\n", __LINE__, suspend_time, state.event, toi_result); return 1; } } local_irq_disable(); if (syscore_suspend()) { set_abort_result(TOI_SYSCORE_REFUSED); toi_end_atomic(ATOMIC_STEP_IRQS, suspend_time, 1); hib_log("FAILED @line:%d suspend(%d) pm_state(%d) toi_result(0x%#lx)\n", __LINE__, suspend_time, state.event, toi_result); return 1; } if (suspend_time && pm_wakeup_pending()) { set_abort_result(TOI_WAKEUP_EVENT); toi_end_atomic(ATOMIC_STEP_SYSCORE_RESUME, suspend_time, 1); hib_log("FAILED @line:%d suspend(%d) pm_state(%d) toi_result(0x%#lx)\n", __LINE__, suspend_time, state.event, toi_result); return 1; } hib_log("SUCCEEDED @line:%d suspend(%d) pm_state(%d)\n", __LINE__, suspend_time, state.event); return 0; }