* 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6: PM / Hibernate: Fix memory corruption related to swap PM / Hibernate: Use async I/O when reading compressed hibernation image
Unified · Split
| @@ -360,7 +360,7 @@ void drain_local_pages(void *dummy); · 359 unmodified lines | ||
| 360 | 360 | |
| 361 | 361 | extern gfp_t gfp_allowed_mask; |
| 362 | 362 | |
| 363 | −extern void set_gfp_allowed_mask(gfp_t mask); | |
| 364 | −extern gfp_t clear_gfp_allowed_mask(gfp_t mask); | |
| 363 | +extern void pm_restrict_gfp_mask(void); | |
| 364 | +extern void pm_restore_gfp_mask(void); | |
| 365 | 365 | |
| 366 | 366 | #endif /* __LINUX_GFP_H */ |
| @@ -327,7 +327,6 @@ static int create_image(int platform_mode) · 326 unmodified lines | ||
| 327 | 327 | int hibernation_snapshot(int platform_mode) |
| 328 | 328 | { |
| 329 | 329 | int error; |
| 330 | − gfp_t saved_mask; | |
| 331 | 330 | |
| 332 | 331 | error = platform_begin(platform_mode); |
| 333 | 332 | if (error) |
| @@ -339,7 +338,7 @@ int hibernation_snapshot(int platform_mode) · 5 unmodified lines | ||
| 339 | 338 | goto Close; |
| 340 | 339 | |
| 341 | 340 | suspend_console(); |
| 342 | − saved_mask = clear_gfp_allowed_mask(GFP_IOFS); | |
| 341 | + pm_restrict_gfp_mask(); | |
| 343 | 342 | error = dpm_suspend_start(PMSG_FREEZE); |
| 344 | 343 | if (error) |
| 345 | 344 | goto Recover_platform; |
| @@ -348,7 +347,10 @@ int hibernation_snapshot(int platform_mode) · 2 unmodified lines | ||
| 348 | 347 | goto Recover_platform; |
| 349 | 348 | |
| 350 | 349 | error = create_image(platform_mode); |
| 351 | − /* Control returns here after successful restore */ | |
| 350 | + /* | |
| 351 | + * Control returns here (1) after the image has been created or the | |
| 352 | + * image creation has failed and (2) after a successful restore. | |
| 353 | + */ | |
| 352 | 354 | |
| 353 | 355 | Resume_devices: |
| 354 | 356 | /* We may need to release the preallocated image pages here. */ |
| @@ -357,7 +359,10 @@ int hibernation_snapshot(int platform_mode) · 2 unmodified lines | ||
| 357 | 359 | |
| 358 | 360 | dpm_resume_end(in_suspend ? |
| 359 | 361 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); |
| 360 | − set_gfp_allowed_mask(saved_mask); | |
| 362 | + | |
| 363 | + if (error || !in_suspend) | |
| 364 | + pm_restore_gfp_mask(); | |
| 365 | + | |
| 361 | 366 | resume_console(); |
| 362 | 367 | Close: |
| 363 | 368 | platform_end(platform_mode); |
| @@ -452,17 +457,16 @@ static int resume_target_kernel(bool platform_mode) · 88 unmodified lines | ||
| 452 | 457 | int hibernation_restore(int platform_mode) |
| 453 | 458 | { |
| 454 | 459 | int error; |
| 455 | − gfp_t saved_mask; | |
| 456 | 460 | |
| 457 | 461 | pm_prepare_console(); |
| 458 | 462 | suspend_console(); |
| 459 | − saved_mask = clear_gfp_allowed_mask(GFP_IOFS); | |
| 463 | + pm_restrict_gfp_mask(); | |
| 460 | 464 | error = dpm_suspend_start(PMSG_QUIESCE); |
| 461 | 465 | if (!error) { |
| 462 | 466 | error = resume_target_kernel(platform_mode); |
| 463 | 467 | dpm_resume_end(PMSG_RECOVER); |
| 464 | 468 | } |
| 465 | − set_gfp_allowed_mask(saved_mask); | |
| 469 | + pm_restore_gfp_mask(); | |
| 466 | 470 | resume_console(); |
| 467 | 471 | pm_restore_console(); |
| 468 | 472 | return error; |
| @@ -476,7 +480,6 @@ int hibernation_restore(int platform_mode) · 7 unmodified lines | ||
| 476 | 480 | int hibernation_platform_enter(void) |
| 477 | 481 | { |
| 478 | 482 | int error; |
| 479 | − gfp_t saved_mask; | |
| 480 | 483 | |
| 481 | 484 | if (!hibernation_ops) |
| 482 | 485 | return -ENOSYS; |
| @@ -492,7 +495,6 @@ int hibernation_platform_enter(void) · 9 unmodified lines | ||
| 492 | 495 | |
| 493 | 496 | entering_platform_hibernation = true; |
| 494 | 497 | suspend_console(); |
| 495 | − saved_mask = clear_gfp_allowed_mask(GFP_IOFS); | |
| 496 | 498 | error = dpm_suspend_start(PMSG_HIBERNATE); |
| 497 | 499 | if (error) { |
| 498 | 500 | if (hibernation_ops->recover) |
| @@ -536,7 +538,6 @@ int hibernation_platform_enter(void) · 37 unmodified lines | ||
| 536 | 538 | Resume_devices: |
| 537 | 539 | entering_platform_hibernation = false; |
| 538 | 540 | dpm_resume_end(PMSG_RESTORE); |
| 539 | − set_gfp_allowed_mask(saved_mask); | |
| 540 | 541 | resume_console(); |
| 541 | 542 | |
| 542 | 543 | Close: |
| @@ -646,6 +647,7 @@ int hibernate(void) · 103 unmodified lines | ||
| 646 | 647 | swsusp_free(); |
| 647 | 648 | if (!error) |
| 648 | 649 | power_down(); |
| 650 | + pm_restore_gfp_mask(); | |
| 649 | 651 | } else { |
| 650 | 652 | pr_debug("PM: Image restored successfully.\n"); |
| 651 | 653 | } |
| @@ -197,7 +197,6 @@ static int suspend_enter(suspend_state_t state) · 196 unmodified lines | ||
| 197 | 197 | int suspend_devices_and_enter(suspend_state_t state) |
| 198 | 198 | { |
| 199 | 199 | int error; |
| 200 | − gfp_t saved_mask; | |
| 201 | 200 | |
| 202 | 201 | if (!suspend_ops) |
| 203 | 202 | return -ENOSYS; |
| @@ -208,7 +207,7 @@ int suspend_devices_and_enter(suspend_state_t state) · 4 unmodified lines | ||
| 208 | 207 | goto Close; |
| 209 | 208 | } |
| 210 | 209 | suspend_console(); |
| 211 | − saved_mask = clear_gfp_allowed_mask(GFP_IOFS); | |
| 210 | + pm_restrict_gfp_mask(); | |
| 212 | 211 | suspend_test_start(); |
| 213 | 212 | error = dpm_suspend_start(PMSG_SUSPEND); |
| 214 | 213 | if (error) { |
| @@ -225,7 +224,7 @@ int suspend_devices_and_enter(suspend_state_t state) · 10 unmodified lines | ||
| 225 | 224 | suspend_test_start(); |
| 226 | 225 | dpm_resume_end(PMSG_RESUME); |
| 227 | 226 | suspend_test_finish("resume devices"); |
| 228 | − set_gfp_allowed_mask(saved_mask); | |
| 227 | + pm_restore_gfp_mask(); | |
| 229 | 228 | resume_console(); |
| 230 | 229 | Close: |
| 231 | 230 | if (suspend_ops->end) |
| @@ -6,6 +6,7 @@ · 5 unmodified lines | ||
| 6 | 6 | * |
| 7 | 7 | * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@ucw.cz> |
| 8 | 8 | * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl> |
| 9 | + * Copyright (C) 2010 Bojan Smojver <bojan@rexursive.com> | |
| 9 | 10 | * |
| 10 | 11 | * This file is released under the GPLv2. |
| 11 | 12 | * |
| @@ -753,30 +754,43 @@ static int load_image_lzo(struct swap_map_handle *handle, · 741 unmodified lines | ||
| 753 | 754 | { |
| 754 | 755 | unsigned int m; |
| 755 | 756 | int error = 0; |
| 757 | + struct bio *bio; | |
| 756 | 758 | struct timeval start; |
| 757 | 759 | struct timeval stop; |
| 758 | 760 | unsigned nr_pages; |
| 759 | − size_t off, unc_len, cmp_len; | |
| 760 | − unsigned char *unc, *cmp, *page; | |
| 761 | + size_t i, off, unc_len, cmp_len; | |
| 762 | + unsigned char *unc, *cmp, *page[LZO_CMP_PAGES]; | |
| 761 | 763 | |
| 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; | |
| 764 | + for (i = 0; i < LZO_CMP_PAGES; i++) { | |
| 765 | + page[i] = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); | |
| 766 | + if (!page[i]) { | |
| 767 | + printk(KERN_ERR "PM: Failed to allocate LZO page\n"); | |
| 768 | + | |
| 769 | + while (i) | |
| 770 | + free_page((unsigned long)page[--i]); | |
| 771 | + | |
| 772 | + return -ENOMEM; | |
| 773 | + } | |
| 766 | 774 | } |
| 767 | 775 | |
| 768 | 776 | unc = vmalloc(LZO_UNC_SIZE); |
| 769 | 777 | if (!unc) { |
| 770 | 778 | printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n"); |
| 771 | − free_page((unsigned long)page); | |
| 779 | + | |
| 780 | + for (i = 0; i < LZO_CMP_PAGES; i++) | |
| 781 | + free_page((unsigned long)page[i]); | |
| 782 | + | |
| 772 | 783 | return -ENOMEM; |
| 773 | 784 | } |
| 774 | 785 | |
| 775 | 786 | cmp = vmalloc(LZO_CMP_SIZE); |
| 776 | 787 | if (!cmp) { |
| 777 | 788 | printk(KERN_ERR "PM: Failed to allocate LZO compressed\n"); |
| 789 | + | |
| 778 | 790 | vfree(unc); |
| 779 | − free_page((unsigned long)page); | |
| 791 | + for (i = 0; i < LZO_CMP_PAGES; i++) | |
| 792 | + free_page((unsigned long)page[i]); | |
| 793 | + | |
| 780 | 794 | return -ENOMEM; |
| 781 | 795 | } |
| 782 | 796 | |
| @@ -787,6 +801,7 @@ static int load_image_lzo(struct swap_map_handle *handle, · 4 unmodified lines | ||
| 787 | 801 | if (!m) |
| 788 | 802 | m = 1; |
| 789 | 803 | nr_pages = 0; |
| 804 | + bio = NULL; | |
| 790 | 805 | do_gettimeofday(&start); |
| 791 | 806 | |
| 792 | 807 | error = snapshot_write_next(snapshot); |
| @@ -794,11 +809,11 @@ static int load_image_lzo(struct swap_map_handle *handle, · 1 unmodified line | ||
| 794 | 809 | goto out_finish; |
| 795 | 810 | |
| 796 | 811 | for (;;) { |
| 797 | − error = swap_read_page(handle, page, NULL); /* sync */ | |
| 812 | + error = swap_read_page(handle, page[0], NULL); /* sync */ | |
| 798 | 813 | if (error) |
| 799 | 814 | break; |
| 800 | 815 | |
| 801 | − cmp_len = *(size_t *)page; | |
| 816 | + cmp_len = *(size_t *)page[0]; | |
| 802 | 817 | if (unlikely(!cmp_len || |
| 803 | 818 | cmp_len > lzo1x_worst_compress(LZO_UNC_SIZE))) { |
| 804 | 819 | printk(KERN_ERR "PM: Invalid LZO compressed length\n"); |
| @@ -806,13 +821,20 @@ static int load_image_lzo(struct swap_map_handle *handle, · 1 unmodified line | ||
| 806 | 821 | break; |
| 807 | 822 | } |
| 808 | 823 | |
| 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 */ | |
| 824 | + for (off = PAGE_SIZE, i = 1; | |
| 825 | + off < LZO_HEADER + cmp_len; off += PAGE_SIZE, i++) { | |
| 826 | + error = swap_read_page(handle, page[i], &bio); | |
| 812 | 827 | if (error) |
| 813 | 828 | goto out_finish; |
| 829 | + } | |
| 814 | 830 | |
| 815 | − memcpy(cmp + off, page, PAGE_SIZE); | |
| 831 | + error = hib_wait_on_bio_chain(&bio); /* need all data now */ | |
| 832 | + if (error) | |
| 833 | + goto out_finish; | |
| 834 | + | |
| 835 | + for (off = 0, i = 0; | |
| 836 | + off < LZO_HEADER + cmp_len; off += PAGE_SIZE, i++) { | |
| 837 | + memcpy(cmp + off, page[i], PAGE_SIZE); | |
| 816 | 838 | } |
| 817 | 839 | |
| 818 | 840 | unc_len = LZO_UNC_SIZE; |
| @@ -857,7 +879,8 @@ out_finish: · 38 unmodified lines | ||
| 857 | 879 | |
| 858 | 880 | vfree(cmp); |
| 859 | 881 | vfree(unc); |
| 860 | − free_page((unsigned long)page); | |
| 882 | + for (i = 0; i < LZO_CMP_PAGES; i++) | |
| 883 | + free_page((unsigned long)page[i]); | |
| 861 | 884 | |
| 862 | 885 | return error; |
| 863 | 886 | } |
| @@ -263,6 +263,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, · 262 unmodified lines | ||
| 263 | 263 | case SNAPSHOT_UNFREEZE: |
| 264 | 264 | if (!data->frozen || data->ready) |
| 265 | 265 | break; |
| 266 | + pm_restore_gfp_mask(); | |
| 266 | 267 | thaw_processes(); |
| 267 | 268 | usermodehelper_enable(); |
| 268 | 269 | data->frozen = 0; |
| @@ -275,6 +276,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, · 6 unmodified lines | ||
| 275 | 276 | error = -EPERM; |
| 276 | 277 | break; |
| 277 | 278 | } |
| 279 | + pm_restore_gfp_mask(); | |
| 278 | 280 | error = hibernation_snapshot(data->platform_support); |
| 279 | 281 | if (!error) |
| 280 | 282 | error = put_user(in_suspend, (int __user *)arg); |
| @@ -104,19 +104,24 @@ gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK; · 103 unmodified lines | ||
| 104 | 104 | * only be modified with pm_mutex held, unless the suspend/hibernate code is |
| 105 | 105 | * guaranteed not to run in parallel with that modification). |
| 106 | 106 | */ |
| 107 | −void set_gfp_allowed_mask(gfp_t mask) | |
| 107 | + | |
| 108 | +static gfp_t saved_gfp_mask; | |
| 109 | + | |
| 110 | +void pm_restore_gfp_mask(void) | |
| 108 | 111 | { |
| 109 | 112 | WARN_ON(!mutex_is_locked(&pm_mutex)); |
| 110 | − gfp_allowed_mask = mask; | |
| 113 | + if (saved_gfp_mask) { | |
| 114 | + gfp_allowed_mask = saved_gfp_mask; | |
| 115 | + saved_gfp_mask = 0; | |
| 116 | + } | |
| 111 | 117 | } |
| 112 | 118 | |
| 113 | −gfp_t clear_gfp_allowed_mask(gfp_t mask) | |
| 119 | +void pm_restrict_gfp_mask(void) | |
| 114 | 120 | { |
| 115 | − gfp_t ret = gfp_allowed_mask; | |
| 116 | − | |
| 117 | 121 | WARN_ON(!mutex_is_locked(&pm_mutex)); |
| 118 | − gfp_allowed_mask &= ~mask; | |
| 119 | − return ret; | |
| 122 | + WARN_ON(saved_gfp_mask); | |
| 123 | + saved_gfp_mask = gfp_allowed_mask; | |
| 124 | + gfp_allowed_mask &= ~GFP_IOFS; | |
| 120 | 125 | } |
| 121 | 126 | #endif /* CONFIG_PM_SLEEP */ |
| 122 | 127 | |