Commit f996fc9671d088bd5f52a70f18c64bfe3d0e418f

Authored by Bojan Smojver
Committed by Rafael J. Wysocki
1 parent 05aa55dddb

PM / Hibernate: Compress hibernation image with LZO

Compress hibernation image with LZO in order to save on I/O and
therefore time to hibernate/thaw.

[rjw: Added hibernate=nocompress command line option instead of just
 nocompress which would be confusing, fixed a couple of compiler
 warnings, fixed kerneldoc comments, minor cleanups.]

Signed-off-by: Bojan Smojver <bojan@rexursive.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Showing 6 changed files with 306 additions and 8 deletions Side-by-side Diff

Documentation/kernel-parameters.txt
... ... @@ -2165,6 +2165,11 @@
2165 2165 in <PAGE_SIZE> units (needed only for swap files).
2166 2166 See Documentation/power/swsusp-and-swap-files.txt
2167 2167  
  2168 + hibernate= [HIBERNATION]
  2169 + noresume Don't check if there's a hibernation image
  2170 + present during boot.
  2171 + nocompress Don't compress/decompress hibernation images.
  2172 +
2168 2173 retain_initrd [RAM] Keep initrd memory after extraction
2169 2174  
2170 2175 rhash_entries= [KNL,NET]
Documentation/power/swsusp.txt
... ... @@ -66,7 +66,8 @@
66 66 powerdowns. You must explicitly specify the swap partition to resume from with
67 67 ``resume='' kernel option. If signature is found it loads and restores saved
68 68 state. If the option ``noresume'' is specified as a boot parameter, it skips
69   -the resuming.
  69 +the resuming. If the option ``hibernate=nocompress'' is specified as a boot
  70 +parameter, it saves hibernation image without compression.
70 71  
71 72 In the meantime while the system is suspended you should not add/remove any
72 73 of the hardware, write to the filesystems, etc.
kernel/power/Kconfig
... ... @@ -137,6 +137,8 @@
137 137 config HIBERNATION
138 138 bool "Hibernation (aka 'suspend to disk')"
139 139 depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
  140 + select LZO_COMPRESS
  141 + select LZO_DECOMPRESS
140 142 select SUSPEND_NVS if HAS_IOMEM
141 143 ---help---
142 144 Enable the suspend to disk (STD) functionality, which is usually
kernel/power/hibernate.c
... ... @@ -29,6 +29,7 @@
29 29 #include "power.h"
30 30  
31 31  
  32 +static int nocompress = 0;
32 33 static int noresume = 0;
33 34 static char resume_file[256] = CONFIG_PM_STD_PARTITION;
34 35 dev_t swsusp_resume_device;
... ... @@ -638,6 +639,8 @@
638 639  
639 640 if (hibernation_mode == HIBERNATION_PLATFORM)
640 641 flags |= SF_PLATFORM_MODE;
  642 + if (nocompress)
  643 + flags |= SF_NOCOMPRESS_MODE;
641 644 pr_debug("PM: writing image.\n");
642 645 error = swsusp_write(flags);
643 646 swsusp_free();
... ... @@ -1004,6 +1007,15 @@
1004 1007 return 1;
1005 1008 }
1006 1009  
  1010 +static int __init hibernate_setup(char *str)
  1011 +{
  1012 + if (!strncmp(str, "noresume", 8))
  1013 + noresume = 1;
  1014 + else if (!strncmp(str, "nocompress", 10))
  1015 + nocompress = 1;
  1016 + return 1;
  1017 +}
  1018 +
1007 1019 static int __init noresume_setup(char *str)
1008 1020 {
1009 1021 noresume = 1;
... ... @@ -1013,4 +1025,5 @@
1013 1025 __setup("noresume", noresume_setup);
1014 1026 __setup("resume_offset=", resume_offset_setup);
1015 1027 __setup("resume=", resume_setup);
  1028 +__setup("hibernate=", hibernate_setup);
kernel/power/power.h
... ... @@ -134,6 +134,7 @@
134 134 * the image header.
135 135 */
136 136 #define SF_PLATFORM_MODE 1
  137 +#define SF_NOCOMPRESS_MODE 2
137 138  
138 139 /* kernel/power/hibernate.c */
139 140 extern int swsusp_check(void);
... ... @@ -24,6 +24,8 @@
24 24 #include <linux/swapops.h>
25 25 #include <linux/pm.h>
26 26 #include <linux/slab.h>
  27 +#include <linux/lzo.h>
  28 +#include <linux/vmalloc.h>
27 29  
28 30 #include "power.h"
29 31  
... ... @@ -357,6 +359,18 @@
357 359 return error;
358 360 }
359 361  
  362 +/* We need to remember how much compressed data we need to read. */
  363 +#define LZO_HEADER sizeof(size_t)
  364 +
  365 +/* Number of pages/bytes we'll compress at one time. */
  366 +#define LZO_UNC_PAGES 32
  367 +#define LZO_UNC_SIZE (LZO_UNC_PAGES * PAGE_SIZE)
  368 +
  369 +/* Number of pages/bytes we need for compressed data (worst case). */
  370 +#define LZO_CMP_PAGES DIV_ROUND_UP(lzo1x_worst_compress(LZO_UNC_SIZE) + \
  371 + LZO_HEADER, PAGE_SIZE)
  372 +#define LZO_CMP_SIZE (LZO_CMP_PAGES * PAGE_SIZE)
  373 +
360 374 /**
361 375 * save_image - save the suspend image data
362 376 */
363 377  
364 378  
365 379  
366 380  
... ... @@ -404,19 +418,154 @@
404 418 return ret;
405 419 }
406 420  
  421 +
407 422 /**
  423 + * save_image_lzo - Save the suspend image data compressed with LZO.
  424 + * @handle: Swap mam handle to use for saving the image.
  425 + * @snapshot: Image to read data from.
  426 + * @nr_to_write: Number of pages to save.
  427 + */
  428 +static int save_image_lzo(struct swap_map_handle *handle,
  429 + struct snapshot_handle *snapshot,
  430 + unsigned int nr_to_write)
  431 +{
  432 + unsigned int m;
  433 + int ret = 0;
  434 + int nr_pages;
  435 + int err2;
  436 + struct bio *bio;
  437 + struct timeval start;
  438 + struct timeval stop;
  439 + size_t off, unc_len, cmp_len;
  440 + unsigned char *unc, *cmp, *wrk, *page;
  441 +
  442 + page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
  443 + if (!page) {
  444 + printk(KERN_ERR "PM: Failed to allocate LZO page\n");
  445 + return -ENOMEM;
  446 + }
  447 +
  448 + wrk = vmalloc(LZO1X_1_MEM_COMPRESS);
  449 + if (!wrk) {
  450 + printk(KERN_ERR "PM: Failed to allocate LZO workspace\n");
  451 + free_page((unsigned long)page);
  452 + return -ENOMEM;
  453 + }
  454 +
  455 + unc = vmalloc(LZO_UNC_SIZE);
  456 + if (!unc) {
  457 + printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n");
  458 + vfree(wrk);
  459 + free_page((unsigned long)page);
  460 + return -ENOMEM;
  461 + }
  462 +
  463 + cmp = vmalloc(LZO_CMP_SIZE);
  464 + if (!cmp) {
  465 + printk(KERN_ERR "PM: Failed to allocate LZO compressed\n");
  466 + vfree(unc);
  467 + vfree(wrk);
  468 + free_page((unsigned long)page);
  469 + return -ENOMEM;
  470 + }
  471 +
  472 + printk(KERN_INFO
  473 + "PM: Compressing and saving image data (%u pages) ... ",
  474 + nr_to_write);
  475 + m = nr_to_write / 100;
  476 + if (!m)
  477 + m = 1;
  478 + nr_pages = 0;
  479 + bio = NULL;
  480 + do_gettimeofday(&start);
  481 + for (;;) {
  482 + for (off = 0; off < LZO_UNC_SIZE; off += PAGE_SIZE) {
  483 + ret = snapshot_read_next(snapshot);
  484 + if (ret < 0)
  485 + goto out_finish;
  486 +
  487 + if (!ret)
  488 + break;
  489 +
  490 + memcpy(unc + off, data_of(*snapshot), PAGE_SIZE);
  491 +
  492 + if (!(nr_pages % m))
  493 + printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m);
  494 + nr_pages++;
  495 + }
  496 +
  497 + if (!off)
  498 + break;
  499 +
  500 + unc_len = off;
  501 + ret = lzo1x_1_compress(unc, unc_len,
  502 + cmp + LZO_HEADER, &cmp_len, wrk);
  503 + if (ret < 0) {
  504 + printk(KERN_ERR "PM: LZO compression failed\n");
  505 + break;
  506 + }
  507 +
  508 + if (unlikely(!cmp_len ||
  509 + cmp_len > lzo1x_worst_compress(unc_len))) {
  510 + printk(KERN_ERR "PM: Invalid LZO compressed length\n");
  511 + ret = -1;
  512 + break;
  513 + }
  514 +
  515 + *(size_t *)cmp = cmp_len;
  516 +
  517 + /*
  518 + * Given we are writing one page at a time to disk, we copy
  519 + * that much from the buffer, although the last bit will likely
  520 + * be smaller than full page. This is OK - we saved the length
  521 + * of the compressed data, so any garbage at the end will be
  522 + * discarded when we read it.
  523 + */
  524 + for (off = 0; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) {
  525 + memcpy(page, cmp + off, PAGE_SIZE);
  526 +
  527 + ret = swap_write_page(handle, page, &bio);
  528 + if (ret)
  529 + goto out_finish;
  530 + }
  531 + }
  532 +
  533 +out_finish:
  534 + err2 = hib_wait_on_bio_chain(&bio);
  535 + do_gettimeofday(&stop);
  536 + if (!ret)
  537 + ret = err2;
  538 + if (!ret)
  539 + printk(KERN_CONT "\b\b\b\bdone\n");
  540 + else
  541 + printk(KERN_CONT "\n");
  542 + swsusp_show_speed(&start, &stop, nr_to_write, "Wrote");
  543 +
  544 + vfree(cmp);
  545 + vfree(unc);
  546 + vfree(wrk);
  547 + free_page((unsigned long)page);
  548 +
  549 + return ret;
  550 +}
  551 +
  552 +/**
408 553 * enough_swap - Make sure we have enough swap to save the image.
409 554 *
410 555 * Returns TRUE or FALSE after checking the total amount of swap
411 556 * space avaiable from the resume partition.
412 557 */
413 558  
414   -static int enough_swap(unsigned int nr_pages)
  559 +static int enough_swap(unsigned int nr_pages, unsigned int flags)
415 560 {
416 561 unsigned int free_swap = count_swap_pages(root_swap, 1);
  562 + unsigned int required;
417 563  
418 564 pr_debug("PM: Free swap pages: %u\n", free_swap);
419   - return free_swap > nr_pages + PAGES_FOR_IO;
  565 +
  566 + required = PAGES_FOR_IO + ((flags & SF_NOCOMPRESS_MODE) ?
  567 + nr_pages : (nr_pages * LZO_CMP_PAGES) / LZO_UNC_PAGES + 1);
  568 + return free_swap > required;
420 569 }
421 570  
422 571 /**
... ... @@ -443,7 +592,7 @@
443 592 printk(KERN_ERR "PM: Cannot get swap writer\n");
444 593 return error;
445 594 }
446   - if (!enough_swap(pages)) {
  595 + if (!enough_swap(pages, flags)) {
447 596 printk(KERN_ERR "PM: Not enough free swap\n");
448 597 error = -ENOSPC;
449 598 goto out_finish;
... ... @@ -458,8 +607,11 @@
458 607 }
459 608 header = (struct swsusp_info *)data_of(snapshot);
460 609 error = swap_write_page(&handle, header, NULL);
461   - if (!error)
462   - error = save_image(&handle, &snapshot, pages - 1);
  610 + if (!error) {
  611 + error = (flags & SF_NOCOMPRESS_MODE) ?
  612 + save_image(&handle, &snapshot, pages - 1) :
  613 + save_image_lzo(&handle, &snapshot, pages - 1);
  614 + }
463 615 out_finish:
464 616 error = swap_writer_finish(&handle, flags, error);
465 617 return error;
... ... @@ -590,6 +742,127 @@
590 742 }
591 743  
592 744 /**
  745 + * load_image_lzo - Load compressed image data and decompress them with LZO.
  746 + * @handle: Swap map handle to use for loading data.
  747 + * @snapshot: Image to copy uncompressed data into.
  748 + * @nr_to_read: Number of pages to load.
  749 + */
  750 +static int load_image_lzo(struct swap_map_handle *handle,
  751 + struct snapshot_handle *snapshot,
  752 + unsigned int nr_to_read)
  753 +{
  754 + unsigned int m;
  755 + int error = 0;
  756 + struct timeval start;
  757 + struct timeval stop;
  758 + unsigned nr_pages;
  759 + size_t off, unc_len, cmp_len;
  760 + unsigned char *unc, *cmp, *page;
  761 +
  762 + page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
  763 + if (!page) {
  764 + printk(KERN_ERR "PM: Failed to allocate LZO page\n");
  765 + return -ENOMEM;
  766 + }
  767 +
  768 + unc = vmalloc(LZO_UNC_SIZE);
  769 + if (!unc) {
  770 + printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n");
  771 + free_page((unsigned long)page);
  772 + return -ENOMEM;
  773 + }
  774 +
  775 + cmp = vmalloc(LZO_CMP_SIZE);
  776 + if (!cmp) {
  777 + printk(KERN_ERR "PM: Failed to allocate LZO compressed\n");
  778 + vfree(unc);
  779 + free_page((unsigned long)page);
  780 + return -ENOMEM;
  781 + }
  782 +
  783 + printk(KERN_INFO
  784 + "PM: Loading and decompressing image data (%u pages) ... ",
  785 + nr_to_read);
  786 + m = nr_to_read / 100;
  787 + if (!m)
  788 + m = 1;
  789 + nr_pages = 0;
  790 + do_gettimeofday(&start);
  791 +
  792 + error = snapshot_write_next(snapshot);
  793 + if (error <= 0)
  794 + goto out_finish;
  795 +
  796 + for (;;) {
  797 + error = swap_read_page(handle, page, NULL); /* sync */
  798 + if (error)
  799 + break;
  800 +
  801 + cmp_len = *(size_t *)page;
  802 + if (unlikely(!cmp_len ||
  803 + cmp_len > lzo1x_worst_compress(LZO_UNC_SIZE))) {
  804 + printk(KERN_ERR "PM: Invalid LZO compressed length\n");
  805 + error = -1;
  806 + break;
  807 + }
  808 +
  809 + memcpy(cmp, page, PAGE_SIZE);
  810 + for (off = PAGE_SIZE; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) {
  811 + error = swap_read_page(handle, page, NULL); /* sync */
  812 + if (error)
  813 + goto out_finish;
  814 +
  815 + memcpy(cmp + off, page, PAGE_SIZE);
  816 + }
  817 +
  818 + unc_len = LZO_UNC_SIZE;
  819 + error = lzo1x_decompress_safe(cmp + LZO_HEADER, cmp_len,
  820 + unc, &unc_len);
  821 + if (error < 0) {
  822 + printk(KERN_ERR "PM: LZO decompression failed\n");
  823 + break;
  824 + }
  825 +
  826 + if (unlikely(!unc_len ||
  827 + unc_len > LZO_UNC_SIZE ||
  828 + unc_len & (PAGE_SIZE - 1))) {
  829 + printk(KERN_ERR "PM: Invalid LZO uncompressed length\n");
  830 + error = -1;
  831 + break;
  832 + }
  833 +
  834 + for (off = 0; off < unc_len; off += PAGE_SIZE) {
  835 + memcpy(data_of(*snapshot), unc + off, PAGE_SIZE);
  836 +
  837 + if (!(nr_pages % m))
  838 + printk("\b\b\b\b%3d%%", nr_pages / m);
  839 + nr_pages++;
  840 +
  841 + error = snapshot_write_next(snapshot);
  842 + if (error <= 0)
  843 + goto out_finish;
  844 + }
  845 + }
  846 +
  847 +out_finish:
  848 + do_gettimeofday(&stop);
  849 + if (!error) {
  850 + printk("\b\b\b\bdone\n");
  851 + snapshot_write_finalize(snapshot);
  852 + if (!snapshot_image_loaded(snapshot))
  853 + error = -ENODATA;
  854 + } else
  855 + printk("\n");
  856 + swsusp_show_speed(&start, &stop, nr_to_read, "Read");
  857 +
  858 + vfree(cmp);
  859 + vfree(unc);
  860 + free_page((unsigned long)page);
  861 +
  862 + return error;
  863 +}
  864 +
  865 +/**
593 866 * swsusp_read - read the hibernation image.
594 867 * @flags_p: flags passed by the "frozen" kernel in the image header should
595 868 * be written into this memeory location
... ... @@ -612,8 +885,11 @@
612 885 goto end;
613 886 if (!error)
614 887 error = swap_read_page(&handle, header, NULL);
615   - if (!error)
616   - error = load_image(&handle, &snapshot, header->pages - 1);
  888 + if (!error) {
  889 + error = (*flags_p & SF_NOCOMPRESS_MODE) ?
  890 + load_image(&handle, &snapshot, header->pages - 1) :
  891 + load_image_lzo(&handle, &snapshot, header->pages - 1);
  892 + }
617 893 swap_reader_finish(&handle);
618 894 end:
619 895 if (!error)