Skip to content

Commit 7787d2c

Browse files
committed
Merge branch 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* '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
2 parents 60658f8 + c9e664f commit 7787d2c

File tree

6 files changed

+68
-37
lines changed

6 files changed

+68
-37
lines changed

include/linux/gfp.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ void drain_local_pages(void *dummy);
360360

361361
extern gfp_t gfp_allowed_mask;
362362

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);
365365

366366
#endif /* __LINUX_GFP_H */

kernel/power/hibernate.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,6 @@ static int create_image(int platform_mode)
327327
int hibernation_snapshot(int platform_mode)
328328
{
329329
int error;
330-
gfp_t saved_mask;
331330

332331
error = platform_begin(platform_mode);
333332
if (error)
@@ -339,7 +338,7 @@ int hibernation_snapshot(int platform_mode)
339338
goto Close;
340339

341340
suspend_console();
342-
saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
341+
pm_restrict_gfp_mask();
343342
error = dpm_suspend_start(PMSG_FREEZE);
344343
if (error)
345344
goto Recover_platform;
@@ -348,7 +347,10 @@ int hibernation_snapshot(int platform_mode)
348347
goto Recover_platform;
349348

350349
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+
*/
352354

353355
Resume_devices:
354356
/* We may need to release the preallocated image pages here. */
@@ -357,7 +359,10 @@ int hibernation_snapshot(int platform_mode)
357359

358360
dpm_resume_end(in_suspend ?
359361
(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+
361366
resume_console();
362367
Close:
363368
platform_end(platform_mode);
@@ -452,17 +457,16 @@ static int resume_target_kernel(bool platform_mode)
452457
int hibernation_restore(int platform_mode)
453458
{
454459
int error;
455-
gfp_t saved_mask;
456460

457461
pm_prepare_console();
458462
suspend_console();
459-
saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
463+
pm_restrict_gfp_mask();
460464
error = dpm_suspend_start(PMSG_QUIESCE);
461465
if (!error) {
462466
error = resume_target_kernel(platform_mode);
463467
dpm_resume_end(PMSG_RECOVER);
464468
}
465-
set_gfp_allowed_mask(saved_mask);
469+
pm_restore_gfp_mask();
466470
resume_console();
467471
pm_restore_console();
468472
return error;
@@ -476,7 +480,6 @@ int hibernation_restore(int platform_mode)
476480
int hibernation_platform_enter(void)
477481
{
478482
int error;
479-
gfp_t saved_mask;
480483

481484
if (!hibernation_ops)
482485
return -ENOSYS;
@@ -492,7 +495,6 @@ int hibernation_platform_enter(void)
492495

493496
entering_platform_hibernation = true;
494497
suspend_console();
495-
saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
496498
error = dpm_suspend_start(PMSG_HIBERNATE);
497499
if (error) {
498500
if (hibernation_ops->recover)
@@ -536,7 +538,6 @@ int hibernation_platform_enter(void)
536538
Resume_devices:
537539
entering_platform_hibernation = false;
538540
dpm_resume_end(PMSG_RESTORE);
539-
set_gfp_allowed_mask(saved_mask);
540541
resume_console();
541542

542543
Close:
@@ -646,6 +647,7 @@ int hibernate(void)
646647
swsusp_free();
647648
if (!error)
648649
power_down();
650+
pm_restore_gfp_mask();
649651
} else {
650652
pr_debug("PM: Image restored successfully.\n");
651653
}

kernel/power/suspend.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ static int suspend_enter(suspend_state_t state)
197197
int suspend_devices_and_enter(suspend_state_t state)
198198
{
199199
int error;
200-
gfp_t saved_mask;
201200

202201
if (!suspend_ops)
203202
return -ENOSYS;
@@ -208,7 +207,7 @@ int suspend_devices_and_enter(suspend_state_t state)
208207
goto Close;
209208
}
210209
suspend_console();
211-
saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
210+
pm_restrict_gfp_mask();
212211
suspend_test_start();
213212
error = dpm_suspend_start(PMSG_SUSPEND);
214213
if (error) {
@@ -225,7 +224,7 @@ int suspend_devices_and_enter(suspend_state_t state)
225224
suspend_test_start();
226225
dpm_resume_end(PMSG_RESUME);
227226
suspend_test_finish("resume devices");
228-
set_gfp_allowed_mask(saved_mask);
227+
pm_restore_gfp_mask();
229228
resume_console();
230229
Close:
231230
if (suspend_ops->end)

kernel/power/swap.c

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*
77
* Copyright (C) 1998,2001-2005 Pavel Machek <[email protected]>
88
* Copyright (C) 2006 Rafael J. Wysocki <[email protected]>
9+
* Copyright (C) 2010 Bojan Smojver <[email protected]>
910
*
1011
* This file is released under the GPLv2.
1112
*
@@ -753,30 +754,43 @@ static int load_image_lzo(struct swap_map_handle *handle,
753754
{
754755
unsigned int m;
755756
int error = 0;
757+
struct bio *bio;
756758
struct timeval start;
757759
struct timeval stop;
758760
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];
761763

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+
}
766774
}
767775

768776
unc = vmalloc(LZO_UNC_SIZE);
769777
if (!unc) {
770778
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+
772783
return -ENOMEM;
773784
}
774785

775786
cmp = vmalloc(LZO_CMP_SIZE);
776787
if (!cmp) {
777788
printk(KERN_ERR "PM: Failed to allocate LZO compressed\n");
789+
778790
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+
780794
return -ENOMEM;
781795
}
782796

@@ -787,32 +801,40 @@ static int load_image_lzo(struct swap_map_handle *handle,
787801
if (!m)
788802
m = 1;
789803
nr_pages = 0;
804+
bio = NULL;
790805
do_gettimeofday(&start);
791806

792807
error = snapshot_write_next(snapshot);
793808
if (error <= 0)
794809
goto out_finish;
795810

796811
for (;;) {
797-
error = swap_read_page(handle, page, NULL); /* sync */
812+
error = swap_read_page(handle, page[0], NULL); /* sync */
798813
if (error)
799814
break;
800815

801-
cmp_len = *(size_t *)page;
816+
cmp_len = *(size_t *)page[0];
802817
if (unlikely(!cmp_len ||
803818
cmp_len > lzo1x_worst_compress(LZO_UNC_SIZE))) {
804819
printk(KERN_ERR "PM: Invalid LZO compressed length\n");
805820
error = -1;
806821
break;
807822
}
808823

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);
812827
if (error)
813828
goto out_finish;
829+
}
814830

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);
816838
}
817839

818840
unc_len = LZO_UNC_SIZE;
@@ -857,7 +879,8 @@ static int load_image_lzo(struct swap_map_handle *handle,
857879

858880
vfree(cmp);
859881
vfree(unc);
860-
free_page((unsigned long)page);
882+
for (i = 0; i < LZO_CMP_PAGES; i++)
883+
free_page((unsigned long)page[i]);
861884

862885
return error;
863886
}

kernel/power/user.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
263263
case SNAPSHOT_UNFREEZE:
264264
if (!data->frozen || data->ready)
265265
break;
266+
pm_restore_gfp_mask();
266267
thaw_processes();
267268
usermodehelper_enable();
268269
data->frozen = 0;
@@ -275,6 +276,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
275276
error = -EPERM;
276277
break;
277278
}
279+
pm_restore_gfp_mask();
278280
error = hibernation_snapshot(data->platform_support);
279281
if (!error)
280282
error = put_user(in_suspend, (int __user *)arg);

mm/page_alloc.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,24 @@ gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;
104104
* only be modified with pm_mutex held, unless the suspend/hibernate code is
105105
* guaranteed not to run in parallel with that modification).
106106
*/
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)
108111
{
109112
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+
}
111117
}
112118

113-
gfp_t clear_gfp_allowed_mask(gfp_t mask)
119+
void pm_restrict_gfp_mask(void)
114120
{
115-
gfp_t ret = gfp_allowed_mask;
116-
117121
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;
120125
}
121126
#endif /* CONFIG_PM_SLEEP */
122127

0 commit comments

Comments
 (0)