diff -ruN 1926/arch/i386/config.in 1927/arch/i386/config.in --- 1926/arch/i386/config.in 2003-04-28 07:59:03.000000000 +1200 +++ 1927/arch/i386/config.in 2003-05-01 11:19:23.000000000 +1200 @@ -330,6 +330,7 @@ dep_bool 'Software Suspend' CONFIG_SOFTWARE_SUSPEND $CONFIG_PM if [ "$CONFIG_SOFTWARE_SUSPEND" = "y" ]; then bool ' Compile in debugging output' CONFIG_SOFTWARE_SUSPEND_DEBUG + bool ' Checksum pages and report differences' CONFIG_SOFTWARE_SUSPEND_CHECKSUM bool ' Compress image' CONFIG_SOFTWARE_SUSPEND_COMPRESSION fi fi diff -ruN 1926/Documentation/Configure.help 1927/Documentation/Configure.help --- 1926/Documentation/Configure.help 2003-04-28 07:59:03.000000000 +1200 +++ 1927/Documentation/Configure.help 2003-05-01 11:32:34.000000000 +1200 @@ -134,6 +134,42 @@ For more information take a look at Documentation/swsusp.txt. +Debugging info for Software Suspend +CONFIG_SOFTWARE_SUSPEND_DEBUG + This option enables the inclusion of debugging info in the software + suspend code. Turning it off will reduce the kernel size but make + debugging suspend & resume issues harder to do. + + For normal usage, this option can be turned off. + +Checksum pages stored during Software Suspend +CONFIG_SOFTWARE_SUSPEND_CHECKSUM + This option enables code that calculates a simple checksum for each + page prior to writing the image and compares the checksum with the + value after restoring the image. There will always be some differences + between the saved image and the point where checksums are checked, but + knowing what the differences are may help in debugging the code. + + This option should normally be off. + +Compress the Software Suspend image +CONFIG_SOFTWARE_SUSPEND_COMPRESSION + This option enables compression of pages stored during Software Suspend + process. Pages are compressed using the zlib library, with a default + setting (in code) of fastest compression. If your swap device is + significantly slower than your CPU, you may improve the speed of a + suspend/resume cycle by enabling this option. + + You may also benefit from it if your swap space is small. Note, however, + that since we can't know how big the image will be until we actually + compress it, the algorithm assumes no compression will be achieved and + ensures that your data will fit on disk even if that happens. This means + that more memory will be eaten than probably needs to be. The best + solution to this issue is to increase your swapspace size. We may + implement support for a swap file in future. + + This option should be off for most people. + Symmetric Multi-Processing support CONFIG_SMP This enables support for systems with more than one CPU. If you have diff -ruN 1926/include/linux/suspend-debug.h 1927/include/linux/suspend-debug.h --- 1926/include/linux/suspend-debug.h 2003-04-28 08:07:10.000000000 +1200 +++ 1927/include/linux/suspend-debug.h 2003-05-02 07:29:42.000000000 +1200 @@ -6,7 +6,7 @@ #define name_suspend "Suspend Machine: " #define name_resume "Resume Machine: " -#define swsusp_version "beta 19-26" +#define swsusp_version "beta 19-27" #define name_swsusp "Swsusp " swsusp_version ": " #define console_suspend " S U S P E N D T O D I S K " /* Same length to ensure one overwrites the other */ #define console_resume "R E S U M E F R O M D I S K" @@ -31,6 +31,7 @@ #define SUSPEND_SLOW 0x8 #define SUSPEND_BEEP 0x10 #define SUSPEND_MEMORY 0x20 +#define SUSPEND_CHECKSUMS 0x40 /* fourth status register */ #define STAGE_FREEZER 0x1 #define STAGE_EAT_MEMORY 0x2 diff -ruN 1926/include/linux/suspend.h 1927/include/linux/suspend.h --- 1926/include/linux/suspend.h 2003-04-28 08:06:37.000000000 +1200 +++ 1927/include/linux/suspend.h 2003-05-01 11:41:13.000000000 +1200 @@ -22,6 +22,10 @@ struct page * origaddress; /* original address of pageset entry */ struct page * address; /* address of copy of pageset entry (pageset1 only) */ swp_entry_t swap_address; +#ifdef CONFIG_SOFTWARE_SUSPEND_CHECKSUM + unsigned long checksum; + unsigned long dummy2[3]; /* sizeof(struct pbe) must be multiple of 4*sizeof(long) */ +#endif swp_entry_t dummy; /* we need at least one sizeof(long) scratch space at * end of page (see link, diskpage) and * sizeof(struct pbe) must be multiple of 4*sizeof(long) diff -ruN 1926/kernel/suspend.c 1927/kernel/suspend.c --- 1926/kernel/suspend.c 2003-04-28 11:33:48.000000000 +1200 +++ 1927/kernel/suspend.c 2003-05-01 13:15:31.000000000 +1200 @@ -315,6 +315,7 @@ static unsigned long * inusemap = NULL; static unsigned long * pageset2map = NULL; +static unsigned long * pagedirmap = NULL; static void clear_map(unsigned long * pagemap) { @@ -324,9 +325,10 @@ } } -static int allocatemap(unsigned long ** pagemap) +static int allocatemap(unsigned long ** pagemap, int setnosave) { void * check; + int i; if (*pagemap) { printk("Error. Pagemap already allocated.\n"); } else { @@ -335,6 +337,9 @@ abort_suspend("Error. Unable to allocate memory for pagemap.\n"); return 1; } + if (setnosave) + for (i = 0; i < (1 << BITMAP_ORDER); i++) + SetPageNosave(virt_to_page((unsigned long) check + i * PAGE_SIZE)); *pagemap = (unsigned long *) check; } clear_map(*pagemap); @@ -343,10 +348,13 @@ static int freemap(unsigned long ** pagemap) { + int i; if (!*pagemap) { printk("Error. Pagemap not allocated.\n"); return 1; } else { + for (i = 0; i < (1 << BITMAP_ORDER); i++) + ClearPageNosave(virt_to_page((unsigned long) *pagemap + i * PAGE_SIZE)); free_pages((unsigned long) *pagemap, BITMAP_ORDER); *pagemap = NULL; return 0; @@ -370,6 +378,10 @@ #define SetPagePageset2(page) set_bit(PAGEBIT(page), &pageset2map[PAGEINDEX(page)]) #define ClearPagePageset2(page) clear_bit(PAGEBIT(page), &pageset2map[PAGEINDEX(page)]) +#define PagePagedir(page) test_bit(PAGEBIT(page), &pagedirmap[PAGEINDEX(page)]) +#define SetPagePagedir(page) set_bit(PAGEBIT(page), &pagedirmap[PAGEINDEX(page)]) +#define ClearPagePagedir(page) clear_bit(PAGEBIT(page), &pagedirmap[PAGEINDEX(page)]) + #ifdef CONFIG_SOFTWARE_SUSPEND_COMPRESSION extern int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); extern void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); @@ -795,6 +807,59 @@ } } +#ifdef CONFIG_SOFTWARE_SUSPEND_CHECKSUM +static unsigned long pagechecksum(struct page * page) +{ + char * address = kmap(page); + unsigned long checksum = 0; + int i = 0; + + for (i = 0; i < PAGE_SIZE; i++) + checksum+= (int) *(address + i); + kunmap(page); + return checksum; +} + +static void check_pagedir_checksums(struct pagedir * p) +{ + int i; + struct pbe * pbe; + + pbe = p->data; + for(i = 0; i < p->pageset_size; i++) { + if (!PagePagedir(virt_to_page(pbe))) /* If not marked as a pagedir page */ + if (pagechecksum(pbe->origaddress) != pbe->checksum) { + char * pageaddress = kmap(pbe->origaddress); + printk("Page checksum for page %p (%p) doesn't match (%lu != %lu).\n", + pbe->origaddress, + pageaddress, + pagechecksum(pbe->origaddress), + pbe->checksum); + kunmap(pbe->orgaddress); + } + pbe = get_next_pbe(pbe); + } +} + +static void store_pagedir_checksums(struct pagedir * p) +{ + int i; + struct pbe * pbe; + + pbe = p->data; + for(i = 0; i < p->pageset_size; i++) { + /* + * For pagedir1, checksum calculated before pages are copied + * but while interrupts are disabled. + * For pagedir2, checksum actual page (no copy made) + */ + if (!PagePagedir(virt_to_page(pbe))) /* If not marked as a pagedir page */ + pbe->checksum = pagechecksum(pbe->origaddress); + pbe = get_next_pbe(pbe); + } +} +#endif + /* * Saving part... */ @@ -1654,6 +1719,7 @@ struct pbe * pbe = pagedir1.data; prepare_status("Copying pages..."); + for (i = 0; i < pageset1_size; i++) { if (i >= nextupdate) nextupdate = update_status(i , pageset1_size, NULL); @@ -1664,6 +1730,7 @@ (char *) page_address(pbe->origaddress)); pbe = get_next_pbe(pbe); } + PRINTNOLOG(SUSPEND_VERBOSE, 0, "\n"); } @@ -1753,6 +1820,7 @@ } ClearPageNosave(virt_to_page(pbe)); ClearPagePageset2(virt_to_page(pbe)); + SetPagePagedir(virt_to_page(pbe)); if (i == 0) p->data = pbe; else @@ -2437,6 +2505,11 @@ if (SUSPEND_ABORTING) goto abort_saving; +#ifdef CONFIG_SOFTWARE_SUSPEND_CHECKSUM + store_pagedir_checksums(&pagedir2); + store_pagedir_checksums(&pagedir1); +#endif + PRINTK(SUSPEND_VERBOSE,"-- Copying pageset1\n"); copy_pageset1(); @@ -2559,10 +2632,21 @@ update_screen(fg_console); #endif +#ifdef CONFIG_SOFTWARE_SUSPEND_CHECKSUM + printk("Checking pagedir1 checksums...\n"); + check_pagedir_checksums(&pagedir1); + printk("Done.\n"); +#endif read_secondary_pagedir(0); prepare_status("Cleaning up..."); update_status(100, 100, NULL); +#ifdef CONFIG_SOFTWARE_SUSPEND_CHECKSUM + printk("Checking pagedir2 checksums...\n"); + check_pagedir_checksums(&pagedir2); + printk("Done.\n"); +#endif + PRINTK(SUSPEND_DEBUG, name_resume "Freeing prev allocated pagedir.\n"); free_pagedir(&pagedir2); free_pagedir(&pagedir1); @@ -2621,7 +2705,7 @@ PRINTFREEMEM("at start of do_software_suspend"); si_swapinfo(&swapinfo); /* FIXME: si_swapinfo(&i) returns all swap devices information.*/ - if (!swapinfo.freeswap) { + if (!swapinfo.totalswap) { printk(KERN_CRIT "You need some swap space to be able to suspend to disk.\n"); return; } @@ -2638,15 +2722,23 @@ /* Allocate bitmaps for suspend page flags */ PRINTK(SUSPEND_DEBUG, "Allocating inusemap\n"); - if (allocatemap(&inusemap)) + if (allocatemap(&inusemap, 1)) return; PRINTFREEMEM("after allocating inusemap"); - if (allocatemap(&pageset2map)) { + if (allocatemap(&pageset2map, 1)) { freemap(&inusemap); return; } PRINTFREEMEM("after allocating pageset2 map"); + /* Allocate bitmaps for ignoring pagedir in checksumming */ + if (allocatemap(&pagedirmap, 0)) { + freemap(&pageset2map); + freemap(&inusemap); + return; + } + PRINTFREEMEM("after allocating pagedir map"); + /* Free up memory if necessary */ PRINTK(SUSPEND_VERBOSE, "Eating memory.\n"); eat_memory(); @@ -2659,7 +2751,6 @@ prepare_status("Syncing data..."); PRINTK(SUSPEND_VERBOSE, "Calling sys_sync.\n"); sys_sync(); - schedule(); PRINTFREEMEM("after calling sys_sync"); /* Stop I/O */ @@ -2716,6 +2807,9 @@ PRINTFREEMEM("at 'out'"); free_grabbed_memory(); PRINTFREEMEM("after freeing grabbed memory"); + if (pagedirmap) + freemap(&pagedirmap); + PRINTFREEMEM("after freeing pagedir map"); if (pageset2map) freemap(&pageset2map); PRINTFREEMEM("after freeing pageset2 map"); @@ -2772,7 +2866,7 @@ int i; struct pbe * pbe = pagedir_nosave.data; - allocatemap(&inusemap); /* Doesn't get deallocated because forgotten + allocatemap(&inusemap, 0);/* Doesn't get deallocated because forgotten when we copy PageDir1 back. Doesn't matter if collides because not used during copy back. */