Skip to content

Commit 71cd7e8

Browse files
Hongchen Zhangrafaeljw
authored andcommitted
PM: hibernate: Enforce ordering during image compression/decompression
An S4 (suspend to disk) test on the LoongArch 3A6000 platform sometimes fails with the following error messaged in the dmesg log: Invalid LZO compressed length That happens because when compressing/decompressing the image, the synchronization between the control thread and the compress/decompress/crc thread is based on a relaxed ordering interface, which is unreliable, and the following situation may occur: CPU 0 CPU 1 save_image_lzo lzo_compress_threadfn atomic_set(&d->stop, 1); atomic_read(&data[thr].stop) data[thr].cmp = data[thr].cmp_len; WRITE data[thr].cmp_len Then CPU0 gets a stale cmp_len and writes it to disk. During resume from S4, wrong cmp_len is loaded. To maintain data consistency between the two threads, use the acquire/release variants of atomic set and read operations. Fixes: 081a9d0 ("PM / Hibernate: Improve performance of LZO/plain hibernation, checksum image") Cc: All applicable <stable@vger.kernel.org> Signed-off-by: Hongchen Zhang <zhanghongchen@loongson.cn> Co-developed-by: Weihao Li <liweihao@loongson.cn> Signed-off-by: Weihao Li <liweihao@loongson.cn> [ rjw: Subject rewrite and changelog edits ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 0c4cae1 commit 71cd7e8

File tree

1 file changed

+19
-19
lines changed

1 file changed

+19
-19
lines changed

kernel/power/swap.c

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -606,11 +606,11 @@ static int crc32_threadfn(void *data)
606606
unsigned i;
607607

608608
while (1) {
609-
wait_event(d->go, atomic_read(&d->ready) ||
609+
wait_event(d->go, atomic_read_acquire(&d->ready) ||
610610
kthread_should_stop());
611611
if (kthread_should_stop()) {
612612
d->thr = NULL;
613-
atomic_set(&d->stop, 1);
613+
atomic_set_release(&d->stop, 1);
614614
wake_up(&d->done);
615615
break;
616616
}
@@ -619,7 +619,7 @@ static int crc32_threadfn(void *data)
619619
for (i = 0; i < d->run_threads; i++)
620620
*d->crc32 = crc32_le(*d->crc32,
621621
d->unc[i], *d->unc_len[i]);
622-
atomic_set(&d->stop, 1);
622+
atomic_set_release(&d->stop, 1);
623623
wake_up(&d->done);
624624
}
625625
return 0;
@@ -649,12 +649,12 @@ static int lzo_compress_threadfn(void *data)
649649
struct cmp_data *d = data;
650650

651651
while (1) {
652-
wait_event(d->go, atomic_read(&d->ready) ||
652+
wait_event(d->go, atomic_read_acquire(&d->ready) ||
653653
kthread_should_stop());
654654
if (kthread_should_stop()) {
655655
d->thr = NULL;
656656
d->ret = -1;
657-
atomic_set(&d->stop, 1);
657+
atomic_set_release(&d->stop, 1);
658658
wake_up(&d->done);
659659
break;
660660
}
@@ -663,7 +663,7 @@ static int lzo_compress_threadfn(void *data)
663663
d->ret = lzo1x_1_compress(d->unc, d->unc_len,
664664
d->cmp + LZO_HEADER, &d->cmp_len,
665665
d->wrk);
666-
atomic_set(&d->stop, 1);
666+
atomic_set_release(&d->stop, 1);
667667
wake_up(&d->done);
668668
}
669669
return 0;
@@ -798,20 +798,20 @@ static int save_image_lzo(struct swap_map_handle *handle,
798798

799799
data[thr].unc_len = off;
800800

801-
atomic_set(&data[thr].ready, 1);
801+
atomic_set_release(&data[thr].ready, 1);
802802
wake_up(&data[thr].go);
803803
}
804804

805805
if (!thr)
806806
break;
807807

808808
crc->run_threads = thr;
809-
atomic_set(&crc->ready, 1);
809+
atomic_set_release(&crc->ready, 1);
810810
wake_up(&crc->go);
811811

812812
for (run_threads = thr, thr = 0; thr < run_threads; thr++) {
813813
wait_event(data[thr].done,
814-
atomic_read(&data[thr].stop));
814+
atomic_read_acquire(&data[thr].stop));
815815
atomic_set(&data[thr].stop, 0);
816816

817817
ret = data[thr].ret;
@@ -850,7 +850,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
850850
}
851851
}
852852

853-
wait_event(crc->done, atomic_read(&crc->stop));
853+
wait_event(crc->done, atomic_read_acquire(&crc->stop));
854854
atomic_set(&crc->stop, 0);
855855
}
856856

@@ -1132,12 +1132,12 @@ static int lzo_decompress_threadfn(void *data)
11321132
struct dec_data *d = data;
11331133

11341134
while (1) {
1135-
wait_event(d->go, atomic_read(&d->ready) ||
1135+
wait_event(d->go, atomic_read_acquire(&d->ready) ||
11361136
kthread_should_stop());
11371137
if (kthread_should_stop()) {
11381138
d->thr = NULL;
11391139
d->ret = -1;
1140-
atomic_set(&d->stop, 1);
1140+
atomic_set_release(&d->stop, 1);
11411141
wake_up(&d->done);
11421142
break;
11431143
}
@@ -1150,7 +1150,7 @@ static int lzo_decompress_threadfn(void *data)
11501150
flush_icache_range((unsigned long)d->unc,
11511151
(unsigned long)d->unc + d->unc_len);
11521152

1153-
atomic_set(&d->stop, 1);
1153+
atomic_set_release(&d->stop, 1);
11541154
wake_up(&d->done);
11551155
}
11561156
return 0;
@@ -1335,7 +1335,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
13351335
}
13361336

13371337
if (crc->run_threads) {
1338-
wait_event(crc->done, atomic_read(&crc->stop));
1338+
wait_event(crc->done, atomic_read_acquire(&crc->stop));
13391339
atomic_set(&crc->stop, 0);
13401340
crc->run_threads = 0;
13411341
}
@@ -1371,7 +1371,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
13711371
pg = 0;
13721372
}
13731373

1374-
atomic_set(&data[thr].ready, 1);
1374+
atomic_set_release(&data[thr].ready, 1);
13751375
wake_up(&data[thr].go);
13761376
}
13771377

@@ -1390,7 +1390,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
13901390

13911391
for (run_threads = thr, thr = 0; thr < run_threads; thr++) {
13921392
wait_event(data[thr].done,
1393-
atomic_read(&data[thr].stop));
1393+
atomic_read_acquire(&data[thr].stop));
13941394
atomic_set(&data[thr].stop, 0);
13951395

13961396
ret = data[thr].ret;
@@ -1421,21 +1421,21 @@ static int load_image_lzo(struct swap_map_handle *handle,
14211421
ret = snapshot_write_next(snapshot);
14221422
if (ret <= 0) {
14231423
crc->run_threads = thr + 1;
1424-
atomic_set(&crc->ready, 1);
1424+
atomic_set_release(&crc->ready, 1);
14251425
wake_up(&crc->go);
14261426
goto out_finish;
14271427
}
14281428
}
14291429
}
14301430

14311431
crc->run_threads = thr;
1432-
atomic_set(&crc->ready, 1);
1432+
atomic_set_release(&crc->ready, 1);
14331433
wake_up(&crc->go);
14341434
}
14351435

14361436
out_finish:
14371437
if (crc->run_threads) {
1438-
wait_event(crc->done, atomic_read(&crc->stop));
1438+
wait_event(crc->done, atomic_read_acquire(&crc->stop));
14391439
atomic_set(&crc->stop, 0);
14401440
}
14411441
stop = ktime_get();

0 commit comments

Comments
 (0)