/* * Compression/decompression algorithm to be used while saving/loading * image to/from disk. This would later be used in 'kernel/power/swap.c' * to allocate comp streams.
*/ char hib_comp_algo[CRYPTO_MAX_ALG_NAME];
#ifdef CONFIG_SUSPEND /** * pm_hibernation_mode_is_suspend - Check if hibernation has been set to suspend
*/ bool pm_hibernation_mode_is_suspend(void)
{ return hibernation_mode == HIBERNATION_SUSPEND;
}
EXPORT_SYMBOL_GPL(pm_hibernation_mode_is_suspend); #endif
/** * hibernation_set_ops - Set the global hibernate operations. * @ops: Hibernation operations to use in subsequent hibernation transitions.
*/ void hibernation_set_ops(conststruct platform_hibernation_ops *ops)
{ unsignedint sleep_flags;
/** * platform_begin - Call platform to start hibernation. * @platform_mode: Whether or not to use the platform driver.
*/ staticint platform_begin(int platform_mode)
{ return (platform_mode && hibernation_ops) ?
hibernation_ops->begin(PMSG_FREEZE) : 0;
}
/** * platform_end - Call platform to finish transition to the working state. * @platform_mode: Whether or not to use the platform driver.
*/ staticvoid platform_end(int platform_mode)
{ if (platform_mode && hibernation_ops)
hibernation_ops->end();
}
/** * platform_pre_snapshot - Call platform to prepare the machine for hibernation. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to prepare the system for creating a hibernate image, * if so configured, and return an error code if that fails.
*/
/** * platform_leave - Call platform to prepare a transition to the working state. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver prepare to prepare the machine for switching to the * normal mode of operation. * * This routine is called on one CPU with interrupts disabled.
*/ staticvoid platform_leave(int platform_mode)
{ if (platform_mode && hibernation_ops)
hibernation_ops->leave();
}
/** * platform_finish - Call platform to switch the system to the working state. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to switch the machine to the normal mode of * operation. * * This routine must be called after platform_prepare().
*/ staticvoid platform_finish(int platform_mode)
{ if (platform_mode && hibernation_ops)
hibernation_ops->finish();
}
/** * platform_pre_restore - Prepare for hibernate image restoration. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to prepare the system for resume from a hibernation * image. * * If the restore fails after this function has been called, * platform_restore_cleanup() must be called.
*/ staticint platform_pre_restore(int platform_mode)
{ return (platform_mode && hibernation_ops) ?
hibernation_ops->pre_restore() : 0;
}
/** * platform_restore_cleanup - Switch to the working state after failing restore. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to switch the system to the normal mode of operation * after a failing restore. * * If platform_pre_restore() has been called before the failing restore, this * function must be called too, regardless of the result of * platform_pre_restore().
*/ staticvoid platform_restore_cleanup(int platform_mode)
{ if (platform_mode && hibernation_ops)
hibernation_ops->restore_cleanup();
}
/** * platform_recover - Recover from a failure to suspend devices. * @platform_mode: Whether or not to use the platform driver.
*/ staticvoid platform_recover(int platform_mode)
{ if (platform_mode && hibernation_ops && hibernation_ops->recover)
hibernation_ops->recover();
}
/** * swsusp_show_speed - Print time elapsed between two events during hibernation. * @start: Starting event. * @stop: Final event. * @nr_pages: Number of memory pages processed between @start and @stop. * @msg: Additional diagnostic message to print.
*/ void swsusp_show_speed(ktime_t start, ktime_t stop, unsigned nr_pages, char *msg)
{
ktime_t diff;
u64 elapsed_centisecs64; unsignedint centisecs; unsignedint k; unsignedint kps;
/** * 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.
*/ staticint create_image(int platform_mode)
{ int error;
error = dpm_suspend_end(PMSG_FREEZE); if (error) {
pr_err("Some devices failed to power down, aborting\n"); return error;
}
error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Platform_finish;
error = pm_sleep_disable_secondary_cpus(); if (error || hibernation_test(TEST_CPUS)) goto Enable_cpus;
local_irq_disable();
system_state = SYSTEM_SUSPEND;
error = syscore_suspend(); if (error) {
pr_err("Some system devices failed to power down, aborting\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 image\n", error);
if (!in_suspend) {
events_check_enabled = false;
clear_or_poison_free_pages();
}
si_meminfo(&info);
nr_shmem_pages = info.sharedram; /* current page count used for shmem */ /* * The intent is to reclaim all shmem pages. Though shrink_all_memory() can * only reclaim about half of them, it's enough for creating the hibernation * image.
*/
nr_freed_pages = shrink_all_memory(nr_shmem_pages);
pr_debug("requested to reclaim %lu shmem pages, actually freed %lu pages\n",
nr_shmem_pages, nr_freed_pages);
}
/** * hibernation_snapshot - Quiesce devices and create a hibernation image. * @platform_mode: If set, use platform driver to prepare for the transition. * * This routine must be called with system_transition_mutex held.
*/ int hibernation_snapshot(int platform_mode)
{
pm_message_t msg; int error;
pm_suspend_clear_flags();
error = platform_begin(platform_mode); if (error) goto Close;
/* Preallocate image memory before shutting down devices. */
error = hibernate_preallocate_memory(); if (error) goto Close;
error = freeze_kernel_threads(); if (error) goto Cleanup;
if (hibernation_test(TEST_FREEZER)) {
/* * Indicate to the caller that we are returning due to a * successful freezer test.
*/
freezer_test_done = true; goto Thaw;
}
error = dpm_prepare(PMSG_FREEZE); if (error) {
dpm_complete(PMSG_RECOVER); goto Thaw;
}
/* * Device drivers may move lots of data to shmem in dpm_prepare(). The shmem * pages will use lots of system memory, causing hibernation image creation * fail due to insufficient free memory. * This call is to force flush the shmem pages to swap disk and reclaim * the system memory so that image creation can succeed.
*/
shrink_shmem_memory();
console_suspend_all();
pm_restrict_gfp_mask();
error = dpm_suspend(PMSG_FREEZE);
if (error || hibernation_test(TEST_DEVICES))
platform_recover(platform_mode); else
error = create_image(platform_mode);
/* * In the case that we call create_image() above, the control * returns here (1) after the image has been created or the * image creation has failed and (2) after a successful restore.
*/
/* We may need to release the preallocated image pages here. */ if (error || !in_suspend)
swsusp_free();
int __weak hibernate_resume_nonboot_cpu_disable(void)
{ return suspend_disable_secondary_cpus();
}
/** * resume_target_kernel - Restore system state from a hibernation image. * @platform_mode: Whether or not to use the platform driver. * * Execute device drivers' "noirq" and "late" freeze callbacks, restore the * contents of highmem that have not been restored yet from the image and run * the low-level code that will restore the remaining contents of memory and * switch to the just restored target kernel.
*/ staticint resume_target_kernel(bool platform_mode)
{ int error;
error = dpm_suspend_end(PMSG_QUIESCE); if (error) {
pr_err("Some devices failed to power down, aborting resume\n"); return error;
}
error = platform_pre_restore(platform_mode); if (error) goto Cleanup;
cpuidle_pause();
error = hibernate_resume_nonboot_cpu_disable(); if (error) goto Enable_cpus;
error = syscore_suspend(); if (error) goto Enable_irqs;
save_processor_state();
error = restore_highmem(); if (!error) {
error = swsusp_arch_resume(); /* * The code below is only ever reached in case of a failure. * Otherwise, execution continues at the place where * swsusp_arch_suspend() was called.
*/
BUG_ON(!error); /* * This call to restore_highmem() reverts the changes made by * the previous one.
*/
restore_highmem();
} /* * The only reason why swsusp_arch_resume() can fail is memory being * very tight, so we have to free it as soon as we can to avoid * subsequent failures.
*/
swsusp_free();
restore_processor_state();
touch_softlockup_watchdog();
/** * hibernation_restore - Quiesce devices and restore from a hibernation image. * @platform_mode: If set, use platform driver to prepare for the transition. * * This routine must be called with system_transition_mutex held. If it is * successful, control reappears in the restored target kernel in * hibernation_snapshot().
*/ int hibernation_restore(int platform_mode)
{ int error;
pm_prepare_console();
console_suspend_all();
error = dpm_suspend_start(PMSG_QUIESCE); if (!error) {
error = resume_target_kernel(platform_mode); /* * The above should either succeed and jump to the new kernel, * or return with an error. Otherwise things are just * undefined, so let's be paranoid.
*/
BUG_ON(!error);
}
dpm_resume_end(PMSG_RECOVER);
console_resume_all();
pm_restore_console(); return error;
}
/** * hibernation_platform_enter - Power off the system using the platform driver.
*/ int hibernation_platform_enter(void)
{ int error;
if (!hibernation_ops) return -ENOSYS;
/* * We have cancelled the power transition by running * hibernation_ops->finish() before saving the image, so we should let * the firmware know that we're going to enter the sleep state after all
*/
error = hibernation_ops->begin(PMSG_HIBERNATE); if (error) goto Close;
entering_platform_hibernation = true;
console_suspend_all();
error = dpm_suspend_start(PMSG_HIBERNATE); if (error) { if (hibernation_ops->recover)
hibernation_ops->recover(); goto Resume_devices;
}
error = dpm_suspend_end(PMSG_HIBERNATE); if (error) goto Resume_devices;
error = hibernation_ops->prepare(); if (error) goto Platform_finish;
error = pm_sleep_disable_secondary_cpus(); if (error) goto Enable_cpus;
/** * power_down - Shut the machine down for hibernation. * * Use the platform driver, if configured, to put the system into the sleep * state corresponding to hibernation, or try to power it off or reboot, * depending on the value of hibernation_mode.
*/ staticvoid power_down(void)
{ int error;
#ifdef CONFIG_SUSPEND if (hibernation_mode == HIBERNATION_SUSPEND) {
error = suspend_devices_and_enter(mem_sleep_current); if (!error) gotoexit;
switch (hibernation_mode) { case HIBERNATION_REBOOT:
kernel_restart(NULL); break; case HIBERNATION_PLATFORM:
error = hibernation_platform_enter(); if (error == -EAGAIN || error == -EBUSY) {
events_check_enabled = false;
pr_info("Wakeup event detected during hibernation, rolling back.\n"); gotoexit;
}
fallthrough; case HIBERNATION_SHUTDOWN: if (kernel_can_power_off()) {
entering_platform_hibernation = true;
kernel_power_off();
entering_platform_hibernation = false;
} break;
}
kernel_halt(); /* * Valid image is on the disk, if we continue we risk serious data * corruption after resume.
*/
pr_crit("Power down manually\n"); while (1)
cpu_relax();
exit: /* Restore swap signature. */
error = swsusp_unmark(); if (error)
pr_err("Swap will be unusable! Try swapon -a.\n");
}
staticint load_image_and_restore(void)
{ int error; unsignedint flags;
/** * hibernate - Carry out system hibernation, including saving the image.
*/ int hibernate(void)
{ bool snapshot_test = false; unsignedint sleep_flags; int error;
if (!hibernation_available()) {
pm_pr_dbg("Hibernation not available.\n"); return -EPERM;
}
/* * Query for the compression algorithm support if compression is enabled.
*/ if (!nocompress) {
strscpy(hib_comp_algo, hibernate_compressor); if (!crypto_has_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC)) {
pr_err("%s compression is not available\n", hib_comp_algo); return -EOPNOTSUPP;
}
}
sleep_flags = lock_system_sleep(); /* The snapshot device should not be opened while we're running */ if (!hibernate_acquire()) {
error = -EBUSY; goto Unlock;
}
if (hibernation_mode == HIBERNATION_PLATFORM)
flags |= SF_PLATFORM_MODE; if (nocompress) {
flags |= SF_NOCOMPRESS_MODE;
} else {
flags |= SF_CRC32_MODE;
/* * By default, LZO compression is enabled. Use SF_COMPRESSION_ALG_LZ4 * to override this behaviour and use LZ4. * * Refer kernel/power/power.h for more details
*/
/** * hibernate_quiet_exec - Execute a function with all devices frozen. * @func: Function to execute. * @data: Data pointer to pass to @func. * * Return the @func return value or an error code if it cannot be executed.
*/ int hibernate_quiet_exec(int (*func)(void *data), void *data)
{ unsignedint sleep_flags; int error;
sleep_flags = lock_system_sleep();
if (!hibernate_acquire()) {
error = -EBUSY; goto unlock;
}
pm_prepare_console();
error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION); if (error) goto restore;
if (filesystem_freeze_enabled)
filesystems_freeze();
error = freeze_processes(); if (error) gotoexit;
lock_device_hotplug();
pm_suspend_clear_flags();
error = platform_begin(true); if (error) goto thaw;
error = freeze_kernel_threads(); if (error) goto thaw;
error = dpm_prepare(PMSG_FREEZE); if (error) goto dpm_complete;
console_suspend_all();
error = dpm_suspend(PMSG_FREEZE); if (error) goto dpm_resume;
error = dpm_suspend_end(PMSG_FREEZE); if (error) goto dpm_resume;
error = platform_pre_snapshot(true); if (error) goto skip;
if (resume_delay) {
pr_info("Waiting %dsec before reading resume device ...\n",
resume_delay);
ssleep(resume_delay);
}
/* Check if the device is there */ if (!early_lookup_bdev(resume_file, &swsusp_resume_device)) return 0;
/* * Some device discovery might still be in progress; we need to wait for * this to finish.
*/
wait_for_device_probe(); if (resume_wait) { while (early_lookup_bdev(resume_file, &swsusp_resume_device))
msleep(10);
async_synchronize_full();
}
mutex_lock(&system_transition_mutex);
error = swsusp_check(true); if (error) goto Unlock;
/* * Check if the hibernation image is compressed. If so, query for * the algorithm support.
*/ if (!(swsusp_header_flags & SF_NOCOMPRESS_MODE)) { if (swsusp_header_flags & SF_COMPRESSION_ALG_LZ4)
strscpy(hib_comp_algo, COMPRESSION_ALGO_LZ4); else
strscpy(hib_comp_algo, COMPRESSION_ALGO_LZO); if (!crypto_has_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC)) {
pr_err("%s compression is not available\n", hib_comp_algo);
error = -EOPNOTSUPP; goto Unlock;
}
}
/* The snapshot device should not be opened while we're running */ if (!hibernate_acquire()) {
error = -EBUSY;
swsusp_close(); goto Unlock;
}
pr_info("resume from hibernation\n");
pm_prepare_console();
error = pm_notifier_call_chain_robust(PM_RESTORE_PREPARE, PM_POST_RESTORE); if (error) goto Restore;
if (filesystem_freeze_enabled)
filesystems_freeze();
pm_pr_dbg("Preparing processes for hibernation restore.\n");
error = freeze_processes(); if (error) {
filesystems_thaw(); goto Close_Finish;
}
error = load_image_and_restore();
thaw_processes();
filesystems_thaw();
Finish:
pm_notifier_call_chain(PM_POST_RESTORE);
Restore:
pm_restore_console();
pr_info("resume failed (%d)\n", error);
hibernate_release(); /* For success case, the suspend path will release the lock */
Unlock:
mutex_unlock(&system_transition_mutex);
pm_pr_dbg("Hibernation image not present or could not be loaded.\n"); return error;
Close_Finish:
swsusp_close(); goto Finish;
}
/** * software_resume_initcall - Resume from a saved hibernation image. * * This routine is called as a late initcall, when all devices have been * discovered and initialized already. * * The image reading code is called to see if there is a hibernation image * available for reading. If that is the case, devices are quiesced and the * contents of memory is restored from the saved image. * * If this is successful, control reappears in the restored target kernel in * hibernation_snapshot() which returns to hibernate(). Otherwise, the routine * attempts to recover gracefully and make the kernel return to the normal mode * of operation.
*/ staticint __init software_resume_initcall(void)
{ /* * If the user said "noresume".. bail out early.
*/ if (noresume || !hibernation_available()) return 0;
if (!swsusp_resume_device) { int error = find_resume_device();
/* * /sys/power/disk - Control hibernation mode. * * Hibernation can be handled in several ways. There are a few different ways * to put the system into the sleep state: using the platform driver (e.g. ACPI * or other hibernation_ops), powering it off or rebooting it (for testing * mostly). * * The sysfs file /sys/power/disk provides an interface for selecting the * hibernation mode to use. Reading from this file causes the available modes * to be printed. There are 3 modes that can be supported: * * 'platform' * 'shutdown' * 'reboot' * * If a platform hibernation driver is in use, 'platform' will be supported * and will be used by default. Otherwise, 'shutdown' will be used by default. * The selected option (i.e. the one corresponding to the current value of * hibernation_mode) is enclosed by a square bracket. * * To select a given hibernation mode it is necessary to write the mode's * string representation (as returned by reading from /sys/power/disk) back * into /sys/power/disk.
*/
if (!hibernation_available()) return sysfs_emit(buf, "[disabled]\n");
for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { if (!hibernation_modes[i]) continue; switch (i) { case HIBERNATION_SHUTDOWN: case HIBERNATION_REBOOT: #ifdef CONFIG_SUSPEND case HIBERNATION_SUSPEND: #endif case HIBERNATION_TEST_RESUME: break; case HIBERNATION_PLATFORM: if (hibernation_ops) break; /* not a valid mode, continue with loop */ continue;
} if (i == hibernation_mode)
count += sysfs_emit_at(buf, count, "[%s] ", hibernation_modes[i]); else
count += sysfs_emit_at(buf, count, "%s ", hibernation_modes[i]);
}
/* Convert the last space to a newline if needed. */ if (count > 0)
buf[count - 1] = '\n';
return count;
}
static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, constchar *buf, size_t n)
{ int mode = HIBERNATION_INVALID; unsignedint sleep_flags; int error = 0; int len; char *p; int i;
if (!hibernation_available()) return -EPERM;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
sleep_flags = lock_system_sleep(); for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { if (len == strlen(hibernation_modes[i])
&& !strncmp(buf, hibernation_modes[i], len)) {
mode = i; break;
}
} if (mode != HIBERNATION_INVALID) { switch (mode) { case HIBERNATION_SHUTDOWN: case HIBERNATION_REBOOT: #ifdef CONFIG_SUSPEND case HIBERNATION_SUSPEND: #endif case HIBERNATION_TEST_RESUME:
hibernation_mode = mode; break; case HIBERNATION_PLATFORM: if (hibernation_ops)
hibernation_mode = mode; else
error = -EINVAL;
}
} else
error = -EINVAL;
if (!error)
pm_pr_dbg("Hibernation mode set to '%s'\n",
hibernation_modes[mode]);
unlock_system_sleep(sleep_flags); return error ? error : n;
}
staticint hibernate_compressor_param_set(constchar *compressor, conststruct kernel_param *kp)
{ int index, ret;
if (!mutex_trylock(&system_transition_mutex)) return -EBUSY;
index = sysfs_match_string(comp_alg_enabled, compressor); if (index >= 0) {
ret = param_set_copystring(comp_alg_enabled[index], kp); if (!ret)
strscpy(hib_comp_algo, comp_alg_enabled[index]);
} else {
ret = index;
}
mutex_unlock(&system_transition_mutex);
if (ret)
pr_debug("Cannot set specified compressor %s\n",
compressor);
module_param_cb(compressor, &hibernate_compressor_param_ops,
&hibernate_compressor_param_string, 0644);
MODULE_PARM_DESC(compressor, "Compression algorithm to be used with hibernation");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.