All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Rafael J. Wysocki" <rjw@sisk.pl>
To: Bojan Smojver <bojan@rexursive.com>
Cc: linux-pm@lists.linux-foundation.org
Subject: Re: [PATCH 1/2] PM: Compress hibernation image with LZO
Date: Mon, 23 Aug 2010 23:43:10 +0200	[thread overview]
Message-ID: <201008232343.10665.rjw@sisk.pl> (raw)
In-Reply-To: <1282520861.23359.1.camel@shrek.rexursive.com>

On Monday, August 23, 2010, Bojan Smojver wrote:
> On Mon, 2010-08-23 at 08:33 +1000, Bojan Smojver wrote:
> > Good point. Will change.
> 
> Here you go:

OK, so changelog please?

Or should I use the original one?

Rafael


>  Documentation/power/swsusp.txt |    3 +-
>  kernel/power/Kconfig           |    2 +
>  kernel/power/hibernate.c       |   10 ++
>  kernel/power/power.h           |    1 +
>  kernel/power/swap.c            |  289 +++++++++++++++++++++++++++++++++++++++-
>  5 files changed, 297 insertions(+), 8 deletions(-)
> 
> diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt
> index 9d60ab7..986dcf5 100644
> --- a/Documentation/power/swsusp.txt
> +++ b/Documentation/power/swsusp.txt
> @@ -66,7 +66,8 @@ swsusp saves the state of the machine into active swaps and then reboots or
>  powerdowns.  You must explicitly specify the swap partition to resume from with
>  ``resume='' kernel option. If signature is found it loads and restores saved
>  state. If the option ``noresume'' is specified as a boot parameter, it skips
> -the resuming.
> +the resuming. If the option ``nocompress'' is specified as a boot parameter, it
> +saves hibernation image without compression.
>  
>  In the meantime while the system is suspended you should not add/remove any
>  of the hardware, write to the filesystems, etc.
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index ca6066a..cb57eb9 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -137,6 +137,8 @@ config SUSPEND_FREEZER
>  config HIBERNATION
>  	bool "Hibernation (aka 'suspend to disk')"
>  	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
> +	select LZO_COMPRESS
> +	select LZO_DECOMPRESS
>  	select SUSPEND_NVS if HAS_IOMEM
>  	---help---
>  	  Enable the suspend to disk (STD) functionality, which is usually
> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
> index aa9e916..fabf905 100644
> --- a/kernel/power/hibernate.c
> +++ b/kernel/power/hibernate.c
> @@ -29,6 +29,7 @@
>  #include "power.h"
>  
>  
> +static int nocompress = 0;
>  static int noresume = 0;
>  static char resume_file[256] = CONFIG_PM_STD_PARTITION;
>  dev_t swsusp_resume_device;
> @@ -630,6 +631,8 @@ int hibernate(void)
>  
>  		if (hibernation_mode == HIBERNATION_PLATFORM)
>  			flags |= SF_PLATFORM_MODE;
> +		if (nocompress)
> +			flags |= SF_NOCOMPRESS_MODE;
>  		pr_debug("PM: writing image.\n");
>  		error = swsusp_write(flags);
>  		swsusp_free();
> @@ -996,12 +999,19 @@ static int __init resume_offset_setup(char *str)
>  	return 1;
>  }
>  
> +static int __init nocompress_setup(char *str)
> +{
> +	nocompress = 1;
> +	return 1;
> +}
> +
>  static int __init noresume_setup(char *str)
>  {
>  	noresume = 1;
>  	return 1;
>  }
>  
> +__setup("nocompress", nocompress_setup);
>  __setup("noresume", noresume_setup);
>  __setup("resume_offset=", resume_offset_setup);
>  __setup("resume=", resume_setup);
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 006270f..c7e42e4 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -134,6 +134,7 @@ extern int swsusp_swap_in_use(void);
>   * the image header.
>   */
>  #define SF_PLATFORM_MODE	1
> +#define SF_NOCOMPRESS_MODE	2
>  
>  /* kernel/power/hibernate.c */
>  extern int swsusp_check(void);
> diff --git a/kernel/power/swap.c b/kernel/power/swap.c
> index b0bb217..54ab029 100644
> --- a/kernel/power/swap.c
> +++ b/kernel/power/swap.c
> @@ -24,6 +24,7 @@
>  #include <linux/swapops.h>
>  #include <linux/pm.h>
>  #include <linux/slab.h>
> +#include <linux/lzo.h>
>  
>  #include "power.h"
>  
> @@ -357,6 +358,18 @@ static int swap_writer_finish(struct swap_map_handle *handle,
>  	return error;
>  }
>  
> +/* We need to remember how much compressed data we need to read. */
> +#define LZO_HEADER	sizeof(size_t)
> +
> +/* Number of pages/bytes we'll compress at one time. */
> +#define LZO_UNC_PAGES	32
> +#define LZO_UNC_SIZE	(LZO_UNC_PAGES * PAGE_SIZE)
> +
> +/* Number of pages/bytes we need for compressed data (worst case). */
> +#define LZO_CMP_PAGES	DIV_ROUND_UP(lzo1x_worst_compress(LZO_UNC_SIZE) + \
> +			             LZO_HEADER, PAGE_SIZE)
> +#define LZO_CMP_SIZE	(LZO_CMP_PAGES * PAGE_SIZE)
> +
>  /**
>   *	save_image - save the suspend image data
>   */
> @@ -404,6 +417,133 @@ static int save_image(struct swap_map_handle *handle,
>  	return ret;
>  }
>  
> +
> +/**
> + *	save_image_lzo - save the suspend image data, LZO compress
> + */
> +
> +static int save_image_lzo(struct swap_map_handle *handle,
> +                          struct snapshot_handle *snapshot,
> +                          unsigned int nr_to_write)
> +{
> +	unsigned int m;
> +	int ret;
> +	int nr_pages;
> +	int err2;
> +	struct bio *bio;
> +	struct timeval start;
> +	struct timeval stop;
> +	size_t off, unc_len, cmp_len;
> +	unsigned char *unc, *cmp, *wrk, *page;
> +
> +	page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
> +	if (!page) {
> +		printk(KERN_ERR "PM: Failed to allocate LZO page\n");
> +		return -ENOMEM;
> +	}
> +
> +	wrk = vmalloc(LZO1X_1_MEM_COMPRESS);
> +	if (!wrk) {
> +		printk(KERN_ERR "PM: Failed to allocate LZO workspace\n");
> +		free_page((unsigned long)page);
> +		return -ENOMEM;
> +	}
> +
> +	unc = vmalloc(LZO_UNC_SIZE);
> +	if (!unc) {
> +		printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n");
> +		vfree(wrk);
> +		free_page((unsigned long)page);
> +		return -ENOMEM;
> +	}
> +
> +	cmp = vmalloc(LZO_CMP_SIZE);
> +	if (!cmp) {
> +		printk(KERN_ERR "PM: Failed to allocate LZO compressed\n");
> +		vfree(unc);
> +		vfree(wrk);
> +		free_page((unsigned long)page);
> +		return -ENOMEM;
> +	}
> +
> +	printk(KERN_INFO "PM: Saving image data pages (%u pages) ...     ",
> +		nr_to_write);
> +	m = nr_to_write / 100;
> +	if (!m)
> +		m = 1;
> +	nr_pages = 0;
> +	bio = NULL;
> +	do_gettimeofday(&start);
> +	while (1) {
> +		for (off = 0; off < LZO_UNC_SIZE; off += PAGE_SIZE) {
> +			ret = snapshot_read_next(snapshot);
> +			if (ret < 0)
> +				goto out_finish;
> +
> +			if (!ret)
> +				break;
> +
> +			memcpy(unc + off, data_of(*snapshot), PAGE_SIZE);
> +
> +			if (!(nr_pages % m))
> +				printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m);
> +			nr_pages++;
> +		}
> +
> +		if (!off)
> +			break;
> +
> +		unc_len = off;
> +		ret = lzo1x_1_compress(unc, unc_len,
> +		                       cmp + LZO_HEADER, &cmp_len, wrk);
> +		if (ret < 0) {
> +			printk(KERN_ERR "PM: LZO compression failed\n");
> +			break;
> +		}
> +
> +		if (unlikely(!cmp_len ||
> +		             cmp_len > lzo1x_worst_compress(unc_len))) {
> +			printk(KERN_ERR "PM: Invalid LZO compressed length\n");
> +			ret = -1;
> +			break;
> +		}
> +
> +		*(size_t *)cmp = cmp_len;
> +
> +		/* Given we are writing one page at a time to disk, we copy
> +		 * that much from the buffer, although the last bit will likely
> +		 * be smaller than full page. This is OK - we saved the length
> +		 * of the compressed data, so any garbage at the end will be
> +		 * discarded when we read it.
> +		 */
> +		for (off = 0; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) {
> +			memcpy(page, cmp + off, PAGE_SIZE);
> +
> +			ret = swap_write_page(handle, page, &bio);
> +			if (ret)
> +				goto out_finish;
> +		}
> +	}
> +
> +out_finish:
> +	err2 = hib_wait_on_bio_chain(&bio);
> +	do_gettimeofday(&stop);
> +	if (!ret)
> +		ret = err2;
> +	if (!ret)
> +		printk(KERN_CONT "\b\b\b\bdone\n");
> +	else
> +		printk(KERN_CONT "\n");
> +	swsusp_show_speed(&start, &stop, nr_to_write, "Wrote");
> +
> +	vfree(cmp);
> +	vfree(unc);
> +	vfree(wrk);
> +	free_page((unsigned long)page);
> +
> +	return ret;
> +}
> +
>  /**
>   *	enough_swap - Make sure we have enough swap to save the image.
>   *
> @@ -411,12 +551,18 @@ static int save_image(struct swap_map_handle *handle,
>   *	space avaiable from the resume partition.
>   */
>  
> -static int enough_swap(unsigned int nr_pages)
> +static int enough_swap(unsigned int nr_pages, unsigned int flags)
>  {
>  	unsigned int free_swap = count_swap_pages(root_swap, 1);
> +	unsigned int required = PAGES_FOR_IO;
> +
> +	if (flags & SF_NOCOMPRESS_MODE)
> +		required += nr_pages;
> +	else
> +		required += (nr_pages * LZO_CMP_PAGES) / LZO_UNC_PAGES + 1;
>  
>  	pr_debug("PM: Free swap pages: %u\n", free_swap);
> -	return free_swap > nr_pages + PAGES_FOR_IO;
> +	return free_swap > required;
>  }
>  
>  /**
> @@ -443,7 +589,7 @@ int swsusp_write(unsigned int flags)
>  		printk(KERN_ERR "PM: Cannot get swap writer\n");
>  		return error;
>  	}
> -	if (!enough_swap(pages)) {
> +	if (!enough_swap(pages, flags)) {
>  		printk(KERN_ERR "PM: Not enough free swap\n");
>  		error = -ENOSPC;
>  		goto out_finish;
> @@ -458,8 +604,12 @@ int swsusp_write(unsigned int flags)
>  	}
>  	header = (struct swsusp_info *)data_of(snapshot);
>  	error = swap_write_page(&handle, header, NULL);
> -	if (!error)
> -		error = save_image(&handle, &snapshot, pages - 1);
> +	if (!error) {
> +		if (flags & SF_NOCOMPRESS_MODE)
> +			error = save_image(&handle, &snapshot, pages - 1);
> +		else
> +			error = save_image_lzo(&handle, &snapshot, pages - 1);
> +	}
>  out_finish:
>  	error = swap_writer_finish(&handle, flags, error);
>  	return error;
> @@ -590,6 +740,127 @@ static int load_image(struct swap_map_handle *handle,
>  }
>  
>  /**
> + *	load_image_lzo - load the image using the swap map handle, LZO
> + *	decompress
> + *	@handle and the snapshot handle @snapshot
> + *	(assume there are @nr_pages pages to load)
> + */
> +
> +static int load_image_lzo(struct swap_map_handle *handle,
> +                          struct snapshot_handle *snapshot,
> +                          unsigned int nr_to_read)
> +{
> +	unsigned int m;
> +	int error = 0;
> +	struct timeval start;
> +	struct timeval stop;
> +	unsigned nr_pages;
> +	size_t off, unc_len, cmp_len;
> +	unsigned char *unc, *cmp, *page;
> +
> +	page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
> +	if (!page) {
> +		printk(KERN_ERR "PM: Failed to allocate LZO page\n");
> +		return -ENOMEM;
> +	}
> +
> +	unc = vmalloc(LZO_UNC_SIZE);
> +	if (!unc) {
> +		printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n");
> +		free_page((unsigned long)page);
> +		return -ENOMEM;
> +	}
> +
> +	cmp = vmalloc(LZO_CMP_SIZE);
> +	if (!cmp) {
> +		printk(KERN_ERR "PM: Failed to allocate LZO compressed\n");
> +		vfree(unc);
> +		free_page((unsigned long)page);
> +		return -ENOMEM;
> +	}
> +
> +	printk(KERN_INFO "PM: Loading image data pages (%u pages) ...     ",
> +		nr_to_read);
> +	m = nr_to_read / 100;
> +	if (!m)
> +		m = 1;
> +	nr_pages = 0;
> +	do_gettimeofday(&start);
> +
> +	error = snapshot_write_next(snapshot);
> +	if (error <= 0)
> +		goto out_finish;
> +
> +	for ( ; ; ) {
> +		error = swap_read_page(handle, page, NULL); /* sync */
> +		if (error)
> +			break;
> +
> +		cmp_len = *(size_t *)page;
> +		if (unlikely(!cmp_len ||
> +		             cmp_len > lzo1x_worst_compress(LZO_UNC_SIZE))) {
> +			printk(KERN_ERR "PM: Invalid LZO compressed length\n");
> +			error = -1;
> +			break;
> +		}
> +
> +		memcpy(cmp, page, PAGE_SIZE);
> +		for (off = PAGE_SIZE; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) {
> +			error = swap_read_page(handle, page, NULL); /* sync */
> +			if (error)
> +				goto out_finish;
> +
> +			memcpy(cmp + off, page, PAGE_SIZE);
> +		}
> +
> +		unc_len = LZO_UNC_SIZE;
> +		error = lzo1x_decompress_safe(cmp + LZO_HEADER, cmp_len,
> +		                              unc, &unc_len);
> +		if (error < 0) {
> +			printk(KERN_ERR "PM: LZO decompression failed\n");
> +			break;
> +		}
> +
> +		if (unlikely(!unc_len ||
> +		             unc_len > LZO_UNC_SIZE ||
> +		             unc_len & (PAGE_SIZE - 1))) {
> +			printk(KERN_ERR "PM: Invalid LZO uncompressed length\n");
> +			error = -1;
> +			break;
> +		}
> +
> +		for (off = 0; off < unc_len; off += PAGE_SIZE) {
> +			memcpy(data_of(*snapshot), unc + off, PAGE_SIZE);
> +
> +			if (!(nr_pages % m))
> +				printk("\b\b\b\b%3d%%", nr_pages / m);
> +			nr_pages++;
> +
> +			error = snapshot_write_next(snapshot);
> +			if (error <= 0)
> +				goto out_finish;
> +		}
> +	}
> +
> +out_finish:
> +	do_gettimeofday(&stop);
> +	if (!error) {
> +		printk("\b\b\b\bdone\n");
> +		snapshot_write_finalize(snapshot);
> +		if (!snapshot_image_loaded(snapshot))
> +			error = -ENODATA;
> +	} else
> +		printk("\n");
> +	swsusp_show_speed(&start, &stop, nr_to_read, "Read");
> +
> +	vfree(cmp);
> +	vfree(unc);
> +	free_page((unsigned long)page);
> +
> +	return error;
> +}
> +
> +/**
>   *	swsusp_read - read the hibernation image.
>   *	@flags_p: flags passed by the "frozen" kernel in the image header should
>   *		  be written into this memeory location
> @@ -612,8 +883,12 @@ int swsusp_read(unsigned int *flags_p)
>  		goto end;
>  	if (!error)
>  		error = swap_read_page(&handle, header, NULL);
> -	if (!error)
> -		error = load_image(&handle, &snapshot, header->pages - 1);
> +	if (!error) {
> +		if (*flags_p & SF_NOCOMPRESS_MODE)
> +			error = load_image(&handle, &snapshot, header->pages - 1);
> +		else	
> +			error = load_image_lzo(&handle, &snapshot, header->pages - 1);
> +	}
>  	swap_reader_finish(&handle);
>  end:
>  	if (!error)
> 
> 

  reply	other threads:[~2010-08-23 21:43 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-08-10  2:29 [PATCH 1/2] PM: Compress hibernation image with LZO Bojan Smojver
2010-08-17  0:52 ` Bojan Smojver
2010-08-17 10:55   ` Rafael J. Wysocki
2010-08-17 13:38     ` Bojan Smojver
2010-08-18 23:02 ` Rafael J. Wysocki
2010-08-18 23:41   ` Bojan Smojver
2010-08-19  9:49     ` Bojan Smojver
2010-08-19 11:11       ` Bojan Smojver
2010-08-20 23:32         ` Rafael J. Wysocki
2010-08-20 12:11       ` Bojan Smojver
2010-08-20 23:36         ` Rafael J. Wysocki
2010-08-21  4:40           ` Bojan Smojver
2010-08-22 19:23             ` Rafael J. Wysocki
2010-08-22 19:29           ` Rafael J. Wysocki
2010-08-22 22:33             ` Bojan Smojver
2010-08-22 23:47               ` Bojan Smojver
2010-08-23 21:43                 ` Rafael J. Wysocki [this message]
2010-08-23 23:20                   ` Bojan Smojver
2010-09-08 23:45                 ` Rafael J. Wysocki
2010-09-09  0:08                   ` Bojan Smojver
2010-08-21  6:15       ` Nigel Cunningham
2010-08-22 19:47         ` Rafael J. Wysocki
2010-08-22 21:52           ` Nigel Cunningham
2010-08-19 13:40     ` Rafael J. Wysocki
  -- strict thread matches above, loose matches on Subject: below --
2010-08-05  3:08 Bojan Smojver
2010-08-05  3:38 ` Bojan Smojver

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=201008232343.10665.rjw@sisk.pl \
    --to=rjw@sisk.pl \
    --cc=bojan@rexursive.com \
    --cc=linux-pm@lists.linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.