Re: [PATCH] Preserve hibenate-system-image on startup

From: Nigel Cunningham
Date: Tue Jul 19 2005 - 06:33:31 EST


Hi.

We've had this feature in Suspend2 for a couple of years and I can
confirm that the approach works, provided that the on-disk filesystem
remains unchanged throughout this. (Useful mainly for kiosks etc).

This is not to say that I've reviewed the code below for correctness.

Regards,

Nigel

On Sun, 2005-07-17 at 16:26, Hiroyuki Machida wrote:
> We are now investigating fast startup/shutdown using
> 2.6 kernel PM functions.
>
> An attached patch enables kernel to preserve system image
> on startup, to implement "Snapshot boot".Majordomo@xxxxxxxxxxxxxxx wrote:
> Conventionally system image will be broken after startup.
>
> Snapshot boot uses un-hibernate from a permanent system image for
> startup. During shutdown, does a conventional shutdown without
> saving a system image.
>
> We'll explain concept and initial work at OLS. So if you have
> interest, we can talk with you at Ottawa.
>
> Thanks,
> Hiroyuki Machida
>
> ---
>
> This patch enables preserving swsuspend system image over boot cycle,
> against 2.6.12
>
> Signed-off-by: Hiroyui Machida <machida@xxxxxxxxxx> for CELF
>
> -----------------
> Index: alp-linux--dev-2-6-12--1.7/kernel/power/Kconfig
> ===================================================================
> --- alp-linux--dev-2-6-12--1.7.orig/kernel/power/Kconfig 2005-07-15 14:59:20.000000000 -0400
> +++ alp-linux--dev-2-6-12--1.7/kernel/power/Kconfig 2005-07-16 00:43:31.420000000 -0400
> @@ -84,6 +84,20 @@
> suspended image to. It will simply pick the first available swap
> device.
>
> +config PRESERVE_SWSUSP_IMAGE
> + bool "Preserve swsuspend image"
> + depends on SOFTWARE_SUSPEND
> + default n
> + ---help---
> + Useally boot with swsup destories the swsusp image.
> + This function enables to preserve swsup image over boot cycle.
> + Default behavior is not chaged even this configuration turned on.
> +
> + To preseve swsusp image, specify following option to command line;
> +
> + prsv-img
> +
> +
> config DEFERRED_RESUME
> bool "Deferred resume"
> depends on PM
> Index: alp-linux--dev-2-6-12--1.7/kernel/power/disk.c
> ===================================================================
> --- alp-linux--dev-2-6-12--1.7.orig/kernel/power/disk.c 2005-07-16 00:43:02.990000000 -0400
> +++ alp-linux--dev-2-6-12--1.7/kernel/power/disk.c 2005-07-16 01:01:42.220000000 -0400
> @@ -29,10 +29,29 @@
> extern void swsusp_close(void);
> extern int swsusp_resume(void);
> extern int swsusp_free(void);
> +extern void dump_pagedir_nosave(void);
> #ifdef CONFIG_SAFE_SUSPEND
> extern int suspend_remount(void);
> extern int resume_remount(void);
> #endif
> +#ifdef CONFIG_PRESERVE_SWSUSP_IMAGE
> +extern int preserve_swsusp_image;
> +extern dev_t swsusp_resume_device_nosave __nosavedata;
> +extern int swsusp_swap_rdonly(dev_t);
> +extern int swsusp_swap_off(dev_t);
> +#else
> +#define preserve_swsusp_image 0
> +#define swsusp_resume_device_nosave 0
> +static inline int swsusp_swap_rdonly(dev_t dev)
> +{
> + return 0;
> +}
> +static inline int swsusp_swap_off(dev_t dev)
> +{
> + return 0;
> +}
> +#endif
> +
>
>
> static int noresume = 0;
> @@ -135,6 +154,26 @@
> pm_restore_console();
> }
>
> +#ifdef CONFIG_PRESERVE_SWSUSP_IMAGE
> +void finish_in_resume(void)
> +{
> + device_resume();
> + platform_finish();
> + enable_nonboot_cpus();
> + thaw_processes();
> + if (preserve_swsusp_image) {
> + swsusp_swap_off(swsusp_resume_device_nosave);
> + }
> + pm_restore_console();
> +}
> +#else
> +void finish_in_resume(void)
> +{
> + finish();
> +}
> +#endif
> +
> +
> extern atomic_t on_suspend; /* See refrigerator() */
>
> static int prepare_processes(void)
> @@ -234,8 +273,15 @@
> error = swsusp_write();
> if (!error)
> power_down(pm_disk_mode);
> - } else
> + } else {
> pr_debug("PM: Image restored successfully.\n");
> + if (preserve_swsusp_image) {
> + swsusp_swap_rdonly(swsusp_resume_device_nosave);
> + }
> + swsusp_free();
> + finish_in_resume();
> + return 0;
> + }
> swsusp_free();
> Done:
> finish();
> Index: alp-linux--dev-2-6-12--1.7/kernel/power/swsusp.c
> ===================================================================
> --- alp-linux--dev-2-6-12--1.7.orig/kernel/power/swsusp.c 2005-07-16 00:43:03.000000000 -0400
> +++ alp-linux--dev-2-6-12--1.7/kernel/power/swsusp.c 2005-07-16 00:56:22.170000000 -0400
> @@ -128,6 +128,11 @@
>
> static struct swsusp_info swsusp_info;
>
> +#ifdef CONFIG_PRESERVE_SWSUSP_IMAGE
> +dev_t swsusp_resume_device_nosave __nosavedata;
> +struct swsusp_header swsusp_header_nosave __nosavedata ;
> +#endif
> +
> /*
> * XXX: We try to keep some more pages free so that I/O operations succeed
> * without paging. Might this be more?
> @@ -139,6 +144,24 @@
> #define PAGES_FOR_IO 512
> #endif
>
> +#ifdef CONFIG_PRESERVE_SWSUSP_IMAGE
> +int preserve_swsusp_image=0;
> +static int __init preserve_swsusp_image_setup(char *str)
> +{
> + if (*str)
> + return 0;
> + preserve_swsusp_image = 1;
> + return 1;
> +}
> +#else
> +static int __init preserve_swsusp_image_setup(char *str)
> +{
> + return 0;
> +}
> +#endif
> +
> +__setup("prsv-img", preserve_swsusp_image_setup);
> +
> /*
> * Saving part...
> */
> @@ -1250,6 +1273,53 @@
> return error;
> }
>
> +#ifdef CONFIG_PRESERVE_SWSUSP_IMAGE
> +/**
> + * mark_swapfiles - Revert swap signature
> + *
> + * Assumed that swsusp_header holds correct data
> + * and rw_swap_page_sync() works
> + */
> +int mark_swsusp(dev_t dev)
> +{
> + int error;
> +
> + resume_bdev = open_by_devnum(dev, FMODE_WRITE);
> + if (!IS_ERR(resume_bdev)) {
> + set_blocksize(resume_bdev, PAGE_SIZE);
> + error = bio_write_page(0, &swsusp_header_nosave);
> + blkdev_put(resume_bdev);
> + } else
> + error = PTR_ERR(resume_bdev);
> +
> + if (!error)
> + pr_debug("swsusp: Mark swsusp again\n");
> + else
> + pr_debug("swsusp: Error %d marking swsusp\n", error);
> + return error;
> +}
> +
> +inline static void update_swsusp_header(void)
> +{
> + swsusp_header_nosave=swsusp_header;
> +}
> +
> +inline static void update_swsusp_device(void)
> +{
> + swsusp_resume_device_nosave = swsusp_resume_device;
> +}
> +#else
> +inline static void update_swsusp_header(void)
> +{
> + ;
> +}
> +
> +inline static void update_swsusp_device(void)
> +{
> + ;
> +}
> +#endif
> +
> static int check_sig(void)
> {
> int error;
> @@ -1258,6 +1328,7 @@
> if ((error = bio_read_page(0, &swsusp_header)))
> return error;
> if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
> + update_swsusp_header();
> memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
>
> /*
> @@ -1412,6 +1483,7 @@
> pr_debug("swsusp: Resume From Partition %d:%d\n",
> MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device));
> }
> + update_swsusp_device();
>
> resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
> if (!IS_ERR(resume_bdev)) {
> Index: alp-linux--dev-2-6-12--1.7/mm/swapfile.c
> ===================================================================
> --- alp-linux--dev-2-6-12--1.7.orig/mm/swapfile.c 2005-07-15 10:34:54.000000000 -0400
> +++ alp-linux--dev-2-6-12--1.7/mm/swapfile.c 2005-07-16 00:43:31.000000000 -0400
> @@ -1065,6 +1065,186 @@
> }
> #endif
>
> +#ifdef CONFIG_PRESERVE_SWSUSP_IMAGE
> +extern int mark_swsusp(dev_t);
> +
> +#ifdef DEBUG
> +extern void cons_write(char *);
> +
> +#undef pr_debug
> +#define pr_debug(fmt,arg...) \
> + do { \
> + char __cw_buf[64]; \
> + __cw_buf[63]='\0'; \
> + snprintf(__cw_buf, 63, fmt,##arg); \
> + cons_write(__cw_buf); \
> + } while (0)
> +#endif /*DEBUG*/
> +
> +/**
> + * find_swapdev_info - Find swap block device info, currently used
> + *
> + */
> +
> +static struct swap_info_struct *find_swapdev_info(dev_t dev,
> + int *p_prev, int *p_type)
> +{
> + int prev, type;
> + struct inode *inode;
> + struct swap_info_struct * si = NULL;
> +
> + prev = -1;
> + for (type = swap_list.head; type >= 0; type = swap_info[type].next) {
> + si = swap_info + type;
> + pr_debug("P:0x%8.8x, TYPE:0x%8.8x\n", (int)si, type);
> + if (si->flags & SWP_USED) {
> + inode = si->swap_file->f_mapping->host;
> + if (S_ISBLK(inode->i_mode)) {
> + pr_debug("INODE: 0x%8.8x:0x%8.8x\n",
> + dev,
> + MKDEV(imajor(inode), iminor(inode)));
> + if (dev == MKDEV(imajor(inode), iminor(inode)))
> + break;
> + }
> + }
> + prev = type;
> + }
> +
> + if (type<0) {
> + si = 0;
> + }
> + *p_type = type;
> + *p_prev = prev;
> + return si;
> +}
> +
> +/**
> + * swsusp_swap_rdonly - Find Swap device for swsusp and mark ReadOnly
> + *
> + */
> +int swsusp_swap_rdonly(dev_t resume_dev)
> +{
> + struct swap_info_struct * si = NULL;
> + int prev;
> + int type;
> + int found=0;
> +
> + swap_list_lock();
> +
> + si = find_swapdev_info(resume_dev, &prev, &type);
> + if (si) {
> + si->flags &= ~SWP_WRITEOK;
> + found = 1;
> + }
> + swap_list_unlock();
> + return found;
> +
> +}
> +
> +/**
> + * swsusp_swpoff - Turn off swap and set signature for swsusp image
> + *
> + */
> +int swsusp_swap_off(dev_t resume_dev)
> +{
> + struct swap_info_struct * si = NULL;
> + unsigned short *swap_map;
> + int i;
> + int type, prev;
> + struct block_device *bdev;
> + int err = 0;
> +
> + swap_list_lock();
> +
> + si = find_swapdev_info(resume_dev, &prev, &type);
> +
> + /* swap area for swsusp image is not writable */
> + if ((!si) || (si->flags & SWP_WRITEOK)) {
> + err = -EINVAL;
> + swap_list_unlock();
> + goto out;
> + }
> +
> + if (!security_vm_enough_memory(si->pages)) {
> + vm_unacct_memory(si->pages);
> + } else {
> + err = -ENOMEM;
> + si->flags |= SWP_WRITEOK;
> + swap_list_unlock();
> + goto out;
> + }
> +
> + pr_debug("swsusp_swapoff:inuse_pages:%ld\n", si->inuse_pages);
> + pr_debug("swsusp_swapoff:pages:%d\n", si->pages);
> + if (prev < 0) {
> + swap_list.head = si->next;
> + } else {
> + swap_info[prev].next = si->next;
> + }
> + if (type == swap_list.next) {
> + /* just pick something that's safe... */
> + swap_list.next = swap_list.head;
> + }
> + nr_swap_pages -= si->pages;
> + total_swap_pages -= si->pages;
> + bdev = I_BDEV(si->swap_file->f_mapping->host);
> + swap_list_unlock();
> +
> + current->flags |= PF_SWAPOFF;
> + err = try_to_unuse(type);
> + current->flags &= ~PF_SWAPOFF;
> +
> + /* wait for any unplug function to finish */
> + down_write(&swap_unplug_sem);
> + up_write(&swap_unplug_sem);
> +
> + if (err) {
> + pr_debug("swsusp_swapoff:err:%d\n", err);
> + /* re-insert swap space back into swap_list */
> + swap_list_lock();
> + for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next)
> + if (si->prio >= swap_info[i].prio)
> + break;
> + si->next = i;
> + if (prev < 0)
> + swap_list.head = swap_list.next = si - swap_info;
> + else
> + swap_info[prev].next = si - swap_info;
> + nr_swap_pages += si->pages;
> + total_swap_pages += si->pages;
> + si->flags |= SWP_WRITEOK;
> + swap_list_unlock();
> + goto out;
> + }
> +
> + down(&swapon_sem);
> + swap_list_lock();
> + drain_mmlist();
> + swap_device_lock(si);
> + si->swap_file = NULL;
> + si->max = 0;
> + swap_map = si->swap_map;
> + si->swap_map = NULL;
> + si->flags = 0;
> + destroy_swap_extents(si);
> + swap_device_unlock(si);
> + swap_list_unlock();
> + up(&swapon_sem);
> + vfree(swap_map);
> +
> + /* set SWSUSP signature, again */
> + mark_swsusp(resume_dev);
> +
> + /* release device */
> + set_blocksize(bdev, si->old_block_size);
> + bd_release(bdev);
> + err = 0;
> +
> +out:
> + return err;
> +}
> +#endif /*CONFIG_PRESERVE_SWSUSP_IMAGE*/
> +
> asmlinkage long sys_swapoff(const char __user * specialfile)
> {
> struct swap_info_struct * p = NULL;
> ---
>
>
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
Evolution.
Enumerate the requirements.
Consider the interdependencies.
Calculate the probabilities.
Be amazed that people believe it happened.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/