diff options
Diffstat (limited to 'kernel/power/swsusp.c')
| -rw-r--r-- | kernel/power/swsusp.c | 93 |
1 files changed, 40 insertions, 53 deletions
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 53f9f8720ee4..c285fc5a2320 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
| @@ -10,12 +10,12 @@ | |||
| 10 | * This file is released under the GPLv2. | 10 | * This file is released under the GPLv2. |
| 11 | * | 11 | * |
| 12 | * I'd like to thank the following people for their work: | 12 | * I'd like to thank the following people for their work: |
| 13 | * | 13 | * |
| 14 | * Pavel Machek <pavel@ucw.cz>: | 14 | * Pavel Machek <pavel@ucw.cz>: |
| 15 | * Modifications, defectiveness pointing, being with me at the very beginning, | 15 | * Modifications, defectiveness pointing, being with me at the very beginning, |
| 16 | * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17. | 16 | * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17. |
| 17 | * | 17 | * |
| 18 | * Steve Doddi <dirk@loth.demon.co.uk>: | 18 | * Steve Doddi <dirk@loth.demon.co.uk>: |
| 19 | * Support the possibility of hardware state restoring. | 19 | * Support the possibility of hardware state restoring. |
| 20 | * | 20 | * |
| 21 | * Raph <grey.havens@earthling.net>: | 21 | * Raph <grey.havens@earthling.net>: |
| @@ -84,11 +84,11 @@ extern char resume_file[]; | |||
| 84 | static unsigned int nr_copy_pages __nosavedata = 0; | 84 | static unsigned int nr_copy_pages __nosavedata = 0; |
| 85 | 85 | ||
| 86 | /* Suspend pagedir is allocated before final copy, therefore it | 86 | /* Suspend pagedir is allocated before final copy, therefore it |
| 87 | must be freed after resume | 87 | must be freed after resume |
| 88 | 88 | ||
| 89 | Warning: this is evil. There are actually two pagedirs at time of | 89 | Warning: this is evil. There are actually two pagedirs at time of |
| 90 | resume. One is "pagedir_save", which is empty frame allocated at | 90 | resume. One is "pagedir_save", which is empty frame allocated at |
| 91 | time of suspend, that must be freed. Second is "pagedir_nosave", | 91 | time of suspend, that must be freed. Second is "pagedir_nosave", |
| 92 | allocated at time of resume, that travels through memory not to | 92 | allocated at time of resume, that travels through memory not to |
| 93 | collide with anything. | 93 | collide with anything. |
| 94 | 94 | ||
| @@ -132,7 +132,7 @@ static int mark_swapfiles(swp_entry_t prev) | |||
| 132 | { | 132 | { |
| 133 | int error; | 133 | int error; |
| 134 | 134 | ||
| 135 | rw_swap_page_sync(READ, | 135 | rw_swap_page_sync(READ, |
| 136 | swp_entry(root_swap, 0), | 136 | swp_entry(root_swap, 0), |
| 137 | virt_to_page((unsigned long)&swsusp_header)); | 137 | virt_to_page((unsigned long)&swsusp_header)); |
| 138 | if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || | 138 | if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || |
| @@ -140,7 +140,7 @@ static int mark_swapfiles(swp_entry_t prev) | |||
| 140 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); | 140 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); |
| 141 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); | 141 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); |
| 142 | swsusp_header.swsusp_info = prev; | 142 | swsusp_header.swsusp_info = prev; |
| 143 | error = rw_swap_page_sync(WRITE, | 143 | error = rw_swap_page_sync(WRITE, |
| 144 | swp_entry(root_swap, 0), | 144 | swp_entry(root_swap, 0), |
| 145 | virt_to_page((unsigned long) | 145 | virt_to_page((unsigned long) |
| 146 | &swsusp_header)); | 146 | &swsusp_header)); |
| @@ -174,22 +174,22 @@ static int is_resume_device(const struct swap_info_struct *swap_info) | |||
| 174 | static int swsusp_swap_check(void) /* This is called before saving image */ | 174 | static int swsusp_swap_check(void) /* This is called before saving image */ |
| 175 | { | 175 | { |
| 176 | int i, len; | 176 | int i, len; |
| 177 | 177 | ||
| 178 | len=strlen(resume_file); | 178 | len=strlen(resume_file); |
| 179 | root_swap = 0xFFFF; | 179 | root_swap = 0xFFFF; |
| 180 | 180 | ||
| 181 | swap_list_lock(); | 181 | swap_list_lock(); |
| 182 | for(i=0; i<MAX_SWAPFILES; i++) { | 182 | for (i=0; i<MAX_SWAPFILES; i++) { |
| 183 | if (swap_info[i].flags == 0) { | 183 | if (swap_info[i].flags == 0) { |
| 184 | swapfile_used[i]=SWAPFILE_UNUSED; | 184 | swapfile_used[i]=SWAPFILE_UNUSED; |
| 185 | } else { | 185 | } else { |
| 186 | if(!len) { | 186 | if (!len) { |
| 187 | printk(KERN_WARNING "resume= option should be used to set suspend device" ); | 187 | printk(KERN_WARNING "resume= option should be used to set suspend device" ); |
| 188 | if(root_swap == 0xFFFF) { | 188 | if (root_swap == 0xFFFF) { |
| 189 | swapfile_used[i] = SWAPFILE_SUSPEND; | 189 | swapfile_used[i] = SWAPFILE_SUSPEND; |
| 190 | root_swap = i; | 190 | root_swap = i; |
| 191 | } else | 191 | } else |
| 192 | swapfile_used[i] = SWAPFILE_IGNORED; | 192 | swapfile_used[i] = SWAPFILE_IGNORED; |
| 193 | } else { | 193 | } else { |
| 194 | /* we ignore all swap devices that are not the resume_file */ | 194 | /* we ignore all swap devices that are not the resume_file */ |
| 195 | if (is_resume_device(&swap_info[i])) { | 195 | if (is_resume_device(&swap_info[i])) { |
| @@ -209,15 +209,15 @@ static int swsusp_swap_check(void) /* This is called before saving image */ | |||
| 209 | * This is called after saving image so modification | 209 | * This is called after saving image so modification |
| 210 | * will be lost after resume... and that's what we want. | 210 | * will be lost after resume... and that's what we want. |
| 211 | * we make the device unusable. A new call to | 211 | * we make the device unusable. A new call to |
| 212 | * lock_swapdevices can unlock the devices. | 212 | * lock_swapdevices can unlock the devices. |
| 213 | */ | 213 | */ |
| 214 | static void lock_swapdevices(void) | 214 | static void lock_swapdevices(void) |
| 215 | { | 215 | { |
| 216 | int i; | 216 | int i; |
| 217 | 217 | ||
| 218 | swap_list_lock(); | 218 | swap_list_lock(); |
| 219 | for(i = 0; i< MAX_SWAPFILES; i++) | 219 | for (i = 0; i< MAX_SWAPFILES; i++) |
| 220 | if(swapfile_used[i] == SWAPFILE_IGNORED) { | 220 | if (swapfile_used[i] == SWAPFILE_IGNORED) { |
| 221 | swap_info[i].flags ^= 0xFF; | 221 | swap_info[i].flags ^= 0xFF; |
| 222 | } | 222 | } |
| 223 | swap_list_unlock(); | 223 | swap_list_unlock(); |
| @@ -229,7 +229,7 @@ static void lock_swapdevices(void) | |||
| 229 | * @loc: Place to store the entry we used. | 229 | * @loc: Place to store the entry we used. |
| 230 | * | 230 | * |
| 231 | * Allocate a new swap entry and 'sync' it. Note we discard -EIO | 231 | * Allocate a new swap entry and 'sync' it. Note we discard -EIO |
| 232 | * errors. That is an artifact left over from swsusp. It did not | 232 | * errors. That is an artifact left over from swsusp. It did not |
| 233 | * check the return of rw_swap_page_sync() at all, since most pages | 233 | * check the return of rw_swap_page_sync() at all, since most pages |
| 234 | * written back to swap would return -EIO. | 234 | * written back to swap would return -EIO. |
| 235 | * This is a partial improvement, since we will at least return other | 235 | * This is a partial improvement, since we will at least return other |
| @@ -241,7 +241,7 @@ static int write_page(unsigned long addr, swp_entry_t * loc) | |||
| 241 | int error = 0; | 241 | int error = 0; |
| 242 | 242 | ||
| 243 | entry = get_swap_page(); | 243 | entry = get_swap_page(); |
| 244 | if (swp_offset(entry) && | 244 | if (swp_offset(entry) && |
| 245 | swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) { | 245 | swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) { |
| 246 | error = rw_swap_page_sync(WRITE, entry, | 246 | error = rw_swap_page_sync(WRITE, entry, |
| 247 | virt_to_page(addr)); | 247 | virt_to_page(addr)); |
| @@ -257,7 +257,7 @@ static int write_page(unsigned long addr, swp_entry_t * loc) | |||
| 257 | /** | 257 | /** |
| 258 | * data_free - Free the swap entries used by the saved image. | 258 | * data_free - Free the swap entries used by the saved image. |
| 259 | * | 259 | * |
| 260 | * Walk the list of used swap entries and free each one. | 260 | * Walk the list of used swap entries and free each one. |
| 261 | * This is only used for cleanup when suspend fails. | 261 | * This is only used for cleanup when suspend fails. |
| 262 | */ | 262 | */ |
| 263 | static void data_free(void) | 263 | static void data_free(void) |
| @@ -290,7 +290,7 @@ static int data_write(void) | |||
| 290 | mod = 1; | 290 | mod = 1; |
| 291 | 291 | ||
| 292 | printk( "Writing data to swap (%d pages)... ", nr_copy_pages ); | 292 | printk( "Writing data to swap (%d pages)... ", nr_copy_pages ); |
| 293 | for_each_pbe(p, pagedir_nosave) { | 293 | for_each_pbe (p, pagedir_nosave) { |
| 294 | if (!(i%mod)) | 294 | if (!(i%mod)) |
| 295 | printk( "\b\b\b\b%3d%%", i / mod ); | 295 | printk( "\b\b\b\b%3d%%", i / mod ); |
| 296 | if ((error = write_page(p->address, &(p->swap_address)))) | 296 | if ((error = write_page(p->address, &(p->swap_address)))) |
| @@ -335,7 +335,7 @@ static int close_swap(void) | |||
| 335 | 335 | ||
| 336 | dump_info(); | 336 | dump_info(); |
| 337 | error = write_page((unsigned long)&swsusp_info, &entry); | 337 | error = write_page((unsigned long)&swsusp_info, &entry); |
| 338 | if (!error) { | 338 | if (!error) { |
| 339 | printk( "S" ); | 339 | printk( "S" ); |
| 340 | error = mark_swapfiles(entry); | 340 | error = mark_swapfiles(entry); |
| 341 | printk( "|\n" ); | 341 | printk( "|\n" ); |
| @@ -370,7 +370,7 @@ static int write_pagedir(void) | |||
| 370 | struct pbe * pbe; | 370 | struct pbe * pbe; |
| 371 | 371 | ||
| 372 | printk( "Writing pagedir..."); | 372 | printk( "Writing pagedir..."); |
| 373 | for_each_pb_page(pbe, pagedir_nosave) { | 373 | for_each_pb_page (pbe, pagedir_nosave) { |
| 374 | if ((error = write_page((unsigned long)pbe, &swsusp_info.pagedir[n++]))) | 374 | if ((error = write_page((unsigned long)pbe, &swsusp_info.pagedir[n++]))) |
| 375 | return error; | 375 | return error; |
| 376 | } | 376 | } |
| @@ -472,7 +472,7 @@ static int save_highmem(void) | |||
| 472 | int res = 0; | 472 | int res = 0; |
| 473 | 473 | ||
| 474 | pr_debug("swsusp: Saving Highmem\n"); | 474 | pr_debug("swsusp: Saving Highmem\n"); |
| 475 | for_each_zone(zone) { | 475 | for_each_zone (zone) { |
| 476 | if (is_highmem(zone)) | 476 | if (is_highmem(zone)) |
| 477 | res = save_highmem_zone(zone); | 477 | res = save_highmem_zone(zone); |
| 478 | if (res) | 478 | if (res) |
| @@ -547,7 +547,7 @@ static void count_data_pages(void) | |||
| 547 | 547 | ||
| 548 | nr_copy_pages = 0; | 548 | nr_copy_pages = 0; |
| 549 | 549 | ||
| 550 | for_each_zone(zone) { | 550 | for_each_zone (zone) { |
| 551 | if (is_highmem(zone)) | 551 | if (is_highmem(zone)) |
| 552 | continue; | 552 | continue; |
| 553 | mark_free_pages(zone); | 553 | mark_free_pages(zone); |
| @@ -562,9 +562,9 @@ static void copy_data_pages(void) | |||
| 562 | struct zone *zone; | 562 | struct zone *zone; |
| 563 | unsigned long zone_pfn; | 563 | unsigned long zone_pfn; |
| 564 | struct pbe * pbe = pagedir_nosave; | 564 | struct pbe * pbe = pagedir_nosave; |
| 565 | 565 | ||
| 566 | pr_debug("copy_data_pages(): pages to copy: %d\n", nr_copy_pages); | 566 | pr_debug("copy_data_pages(): pages to copy: %d\n", nr_copy_pages); |
| 567 | for_each_zone(zone) { | 567 | for_each_zone (zone) { |
| 568 | if (is_highmem(zone)) | 568 | if (is_highmem(zone)) |
| 569 | continue; | 569 | continue; |
| 570 | mark_free_pages(zone); | 570 | mark_free_pages(zone); |
| @@ -702,7 +702,7 @@ static void free_image_pages(void) | |||
| 702 | { | 702 | { |
| 703 | struct pbe * p; | 703 | struct pbe * p; |
| 704 | 704 | ||
| 705 | for_each_pbe(p, pagedir_save) { | 705 | for_each_pbe (p, pagedir_save) { |
| 706 | if (p->address) { | 706 | if (p->address) { |
| 707 | ClearPageNosave(virt_to_page(p->address)); | 707 | ClearPageNosave(virt_to_page(p->address)); |
| 708 | free_page(p->address); | 708 | free_page(p->address); |
| @@ -719,7 +719,7 @@ static int alloc_image_pages(void) | |||
| 719 | { | 719 | { |
| 720 | struct pbe * p; | 720 | struct pbe * p; |
| 721 | 721 | ||
| 722 | for_each_pbe(p, pagedir_save) { | 722 | for_each_pbe (p, pagedir_save) { |
| 723 | p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); | 723 | p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); |
| 724 | if (!p->address) | 724 | if (!p->address) |
| 725 | return -ENOMEM; | 725 | return -ENOMEM; |
| @@ -740,7 +740,7 @@ void swsusp_free(void) | |||
| 740 | /** | 740 | /** |
| 741 | * enough_free_mem - Make sure we enough free memory to snapshot. | 741 | * enough_free_mem - Make sure we enough free memory to snapshot. |
| 742 | * | 742 | * |
| 743 | * Returns TRUE or FALSE after checking the number of available | 743 | * Returns TRUE or FALSE after checking the number of available |
| 744 | * free pages. | 744 | * free pages. |
| 745 | */ | 745 | */ |
| 746 | 746 | ||
| @@ -758,11 +758,11 @@ static int enough_free_mem(void) | |||
| 758 | /** | 758 | /** |
| 759 | * enough_swap - Make sure we have enough swap to save the image. | 759 | * enough_swap - Make sure we have enough swap to save the image. |
| 760 | * | 760 | * |
| 761 | * Returns TRUE or FALSE after checking the total amount of swap | 761 | * Returns TRUE or FALSE after checking the total amount of swap |
| 762 | * space avaiable. | 762 | * space avaiable. |
| 763 | * | 763 | * |
| 764 | * FIXME: si_swapinfo(&i) returns all swap devices information. | 764 | * FIXME: si_swapinfo(&i) returns all swap devices information. |
| 765 | * We should only consider resume_device. | 765 | * We should only consider resume_device. |
| 766 | */ | 766 | */ |
| 767 | 767 | ||
| 768 | static int enough_swap(void) | 768 | static int enough_swap(void) |
| @@ -781,18 +781,18 @@ static int swsusp_alloc(void) | |||
| 781 | { | 781 | { |
| 782 | int error; | 782 | int error; |
| 783 | 783 | ||
| 784 | pagedir_nosave = NULL; | ||
| 785 | nr_copy_pages = calc_nr(nr_copy_pages); | ||
| 786 | |||
| 784 | pr_debug("suspend: (pages needed: %d + %d free: %d)\n", | 787 | pr_debug("suspend: (pages needed: %d + %d free: %d)\n", |
| 785 | nr_copy_pages, PAGES_FOR_IO, nr_free_pages()); | 788 | nr_copy_pages, PAGES_FOR_IO, nr_free_pages()); |
| 786 | 789 | ||
| 787 | pagedir_nosave = NULL; | ||
| 788 | if (!enough_free_mem()) | 790 | if (!enough_free_mem()) |
| 789 | return -ENOMEM; | 791 | return -ENOMEM; |
| 790 | 792 | ||
| 791 | if (!enough_swap()) | 793 | if (!enough_swap()) |
| 792 | return -ENOSPC; | 794 | return -ENOSPC; |
| 793 | 795 | ||
| 794 | nr_copy_pages = calc_nr(nr_copy_pages); | ||
| 795 | |||
| 796 | if (!(pagedir_save = alloc_pagedir(nr_copy_pages))) { | 796 | if (!(pagedir_save = alloc_pagedir(nr_copy_pages))) { |
| 797 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); | 797 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); |
| 798 | return -ENOMEM; | 798 | return -ENOMEM; |
| @@ -827,8 +827,8 @@ static int suspend_prepare_image(void) | |||
| 827 | error = swsusp_alloc(); | 827 | error = swsusp_alloc(); |
| 828 | if (error) | 828 | if (error) |
| 829 | return error; | 829 | return error; |
| 830 | 830 | ||
| 831 | /* During allocating of suspend pagedir, new cold pages may appear. | 831 | /* During allocating of suspend pagedir, new cold pages may appear. |
| 832 | * Kill them. | 832 | * Kill them. |
| 833 | */ | 833 | */ |
| 834 | drain_local_pages(); | 834 | drain_local_pages(); |
| @@ -929,21 +929,6 @@ int swsusp_resume(void) | |||
| 929 | return error; | 929 | return error; |
| 930 | } | 930 | } |
| 931 | 931 | ||
| 932 | /* More restore stuff */ | ||
| 933 | |||
| 934 | /* | ||
| 935 | * Returns true if given address/order collides with any orig_address | ||
| 936 | */ | ||
| 937 | static int does_collide_order(unsigned long addr, int order) | ||
| 938 | { | ||
| 939 | int i; | ||
| 940 | |||
| 941 | for (i=0; i < (1<<order); i++) | ||
| 942 | if (!PageNosaveFree(virt_to_page(addr + i * PAGE_SIZE))) | ||
| 943 | return 1; | ||
| 944 | return 0; | ||
| 945 | } | ||
| 946 | |||
| 947 | /** | 932 | /** |
| 948 | * On resume, for storing the PBE list and the image, | 933 | * On resume, for storing the PBE list and the image, |
| 949 | * we can only use memory pages that do not conflict with the pages | 934 | * we can only use memory pages that do not conflict with the pages |
| @@ -973,7 +958,7 @@ static unsigned long get_usable_page(unsigned gfp_mask) | |||
| 973 | unsigned long m; | 958 | unsigned long m; |
| 974 | 959 | ||
| 975 | m = get_zeroed_page(gfp_mask); | 960 | m = get_zeroed_page(gfp_mask); |
| 976 | while (does_collide_order(m, 0)) { | 961 | while (!PageNosaveFree(virt_to_page(m))) { |
| 977 | eat_page((void *)m); | 962 | eat_page((void *)m); |
| 978 | m = get_zeroed_page(gfp_mask); | 963 | m = get_zeroed_page(gfp_mask); |
| 979 | if (!m) | 964 | if (!m) |
| @@ -1045,7 +1030,7 @@ static struct pbe * swsusp_pagedir_relocate(struct pbe *pblist) | |||
| 1045 | 1030 | ||
| 1046 | /* Set page flags */ | 1031 | /* Set page flags */ |
| 1047 | 1032 | ||
| 1048 | for_each_zone(zone) { | 1033 | for_each_zone (zone) { |
| 1049 | for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) | 1034 | for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) |
| 1050 | SetPageNosaveFree(pfn_to_page(zone_pfn + | 1035 | SetPageNosaveFree(pfn_to_page(zone_pfn + |
| 1051 | zone->zone_start_pfn)); | 1036 | zone->zone_start_pfn)); |
| @@ -1061,7 +1046,7 @@ static struct pbe * swsusp_pagedir_relocate(struct pbe *pblist) | |||
| 1061 | /* Relocate colliding pages */ | 1046 | /* Relocate colliding pages */ |
| 1062 | 1047 | ||
| 1063 | for_each_pb_page (pbpage, pblist) { | 1048 | for_each_pb_page (pbpage, pblist) { |
| 1064 | if (does_collide_order((unsigned long)pbpage, 0)) { | 1049 | if (!PageNosaveFree(virt_to_page((unsigned long)pbpage))) { |
| 1065 | m = (void *)get_usable_page(GFP_ATOMIC | __GFP_COLD); | 1050 | m = (void *)get_usable_page(GFP_ATOMIC | __GFP_COLD); |
| 1066 | if (!m) { | 1051 | if (!m) { |
| 1067 | error = -ENOMEM; | 1052 | error = -ENOMEM; |
| @@ -1193,8 +1178,10 @@ static const char * sanity_check(void) | |||
| 1193 | return "version"; | 1178 | return "version"; |
| 1194 | if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) | 1179 | if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) |
| 1195 | return "machine"; | 1180 | return "machine"; |
| 1181 | #if 0 | ||
| 1196 | if(swsusp_info.cpus != num_online_cpus()) | 1182 | if(swsusp_info.cpus != num_online_cpus()) |
| 1197 | return "number of cpus"; | 1183 | return "number of cpus"; |
| 1184 | #endif | ||
| 1198 | return NULL; | 1185 | return NULL; |
| 1199 | } | 1186 | } |
| 1200 | 1187 | ||
