diff options
author | Andrew Morton <akpm@osdl.org> | 2006-09-26 02:32:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-26 11:48:58 -0400 |
commit | 546e0d271941dd1ff6961e2a1f7eac75f1fc277e (patch) | |
tree | 60c74a9598f7cb4622c1b6acd25df5df67284353 | |
parent | 8c002494b55119a3fd1dddee83b4fb75cfda47e5 (diff) |
[PATCH] swsusp: read speedup
Implement async reads for swsusp resuming.
Crufty old PIII testbox:
15.7 MB/s -> 20.3 MB/s
Sony Vaio:
14.6 MB/s -> 33.3 MB/s
I didn't implement the post-resume bio_set_pages_dirty(). I don't really
understand why resume needs to run set_page_dirty() against these pages.
It might be a worry that this code modifies PG_Uptodate, PG_Error and
PG_Locked against the image pages. Can this possibly affect the resumed-into
kernel? Hopefully not, if we're atomically restoring its mem_map?
Cc: Pavel Machek <pavel@ucw.cz>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Jens Axboe <axboe@suse.de>
Cc: Laurent Riffard <laurent.riffard@free.fr>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | include/linux/swap.h | 1 | ||||
-rw-r--r-- | kernel/power/power.h | 1 | ||||
-rw-r--r-- | kernel/power/snapshot.c | 11 | ||||
-rw-r--r-- | kernel/power/swap.c | 138 | ||||
-rw-r--r-- | mm/page_io.c | 2 |
5 files changed, 81 insertions, 72 deletions
diff --git a/include/linux/swap.h b/include/linux/swap.h index 3d434cbffe26..e7c36ba2a2db 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
@@ -220,6 +220,7 @@ extern int swap_readpage(struct file *, struct page *); | |||
220 | extern int swap_writepage(struct page *page, struct writeback_control *wbc); | 220 | extern int swap_writepage(struct page *page, struct writeback_control *wbc); |
221 | extern int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page, | 221 | extern int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page, |
222 | struct bio **bio_chain); | 222 | struct bio **bio_chain); |
223 | extern int end_swap_bio_read(struct bio *bio, unsigned int bytes_done, int err); | ||
223 | 224 | ||
224 | /* linux/mm/swap_state.c */ | 225 | /* linux/mm/swap_state.c */ |
225 | extern struct address_space swapper_space; | 226 | extern struct address_space swapper_space; |
diff --git a/kernel/power/power.h b/kernel/power/power.h index 57a792982fb9..59ce712f082d 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -58,6 +58,7 @@ struct snapshot_handle { | |||
58 | struct pbe *pbe, *last_pbe; | 58 | struct pbe *pbe, *last_pbe; |
59 | void *buffer; | 59 | void *buffer; |
60 | unsigned int buf_offset; | 60 | unsigned int buf_offset; |
61 | int sync_read; | ||
61 | }; | 62 | }; |
62 | 63 | ||
63 | #define data_of(handle) ((handle).buffer + (handle).buf_offset) | 64 | #define data_of(handle) ((handle).buffer + (handle).buf_offset) |
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 75d4886e648e..591301ae8b7d 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -314,7 +314,7 @@ static unsigned int unsafe_pages; | |||
314 | * and we count them using unsafe_pages | 314 | * and we count them using unsafe_pages |
315 | */ | 315 | */ |
316 | 316 | ||
317 | static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed) | 317 | static void *alloc_image_page(gfp_t gfp_mask, int safe_needed) |
318 | { | 318 | { |
319 | void *res; | 319 | void *res; |
320 | 320 | ||
@@ -828,13 +828,16 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count) | |||
828 | } | 828 | } |
829 | if (!handle->offset) | 829 | if (!handle->offset) |
830 | handle->buffer = buffer; | 830 | handle->buffer = buffer; |
831 | handle->sync_read = 1; | ||
831 | if (handle->prev < handle->page) { | 832 | if (handle->prev < handle->page) { |
832 | if (!handle->prev) { | 833 | if (!handle->prev) { |
833 | error = load_header(handle, (struct swsusp_info *)buffer); | 834 | error = load_header(handle, |
835 | (struct swsusp_info *)buffer); | ||
834 | if (error) | 836 | if (error) |
835 | return error; | 837 | return error; |
836 | } else if (handle->prev <= nr_meta_pages) { | 838 | } else if (handle->prev <= nr_meta_pages) { |
837 | handle->pbe = unpack_orig_addresses(buffer, handle->pbe); | 839 | handle->pbe = unpack_orig_addresses(buffer, |
840 | handle->pbe); | ||
838 | if (!handle->pbe) { | 841 | if (!handle->pbe) { |
839 | error = prepare_image(handle); | 842 | error = prepare_image(handle); |
840 | if (error) | 843 | if (error) |
@@ -842,10 +845,12 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count) | |||
842 | handle->pbe = pagedir_nosave; | 845 | handle->pbe = pagedir_nosave; |
843 | handle->last_pbe = NULL; | 846 | handle->last_pbe = NULL; |
844 | handle->buffer = get_buffer(handle); | 847 | handle->buffer = get_buffer(handle); |
848 | handle->sync_read = 0; | ||
845 | } | 849 | } |
846 | } else { | 850 | } else { |
847 | handle->pbe = handle->pbe->next; | 851 | handle->pbe = handle->pbe->next; |
848 | handle->buffer = get_buffer(handle); | 852 | handle->buffer = get_buffer(handle); |
853 | handle->sync_read = 0; | ||
849 | } | 854 | } |
850 | handle->prev = handle->page; | 855 | handle->prev = handle->page; |
851 | } | 856 | } |
diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 9ab989572164..8309d20b2563 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/device.h> | 22 | #include <linux/device.h> |
23 | #include <linux/buffer_head.h> | 23 | #include <linux/buffer_head.h> |
24 | #include <linux/bio.h> | 24 | #include <linux/bio.h> |
25 | #include <linux/blkdev.h> | ||
25 | #include <linux/swap.h> | 26 | #include <linux/swap.h> |
26 | #include <linux/swapops.h> | 27 | #include <linux/swapops.h> |
27 | #include <linux/pm.h> | 28 | #include <linux/pm.h> |
@@ -214,6 +215,8 @@ static int wait_on_bio_chain(struct bio **bio_chain) | |||
214 | return 0; | 215 | return 0; |
215 | 216 | ||
216 | bio = *bio_chain; | 217 | bio = *bio_chain; |
218 | if (bio == NULL) | ||
219 | return 0; | ||
217 | while (bio) { | 220 | while (bio) { |
218 | struct page *page; | 221 | struct page *page; |
219 | 222 | ||
@@ -349,7 +352,8 @@ int swsusp_write(void) | |||
349 | int error; | 352 | int error; |
350 | 353 | ||
351 | if ((error = swsusp_swap_check())) { | 354 | if ((error = swsusp_swap_check())) { |
352 | printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n"); | 355 | printk(KERN_ERR "swsusp: Cannot find swap device, try " |
356 | "swapon -a.\n"); | ||
353 | return error; | 357 | return error; |
354 | } | 358 | } |
355 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | 359 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); |
@@ -381,27 +385,6 @@ int swsusp_write(void) | |||
381 | return error; | 385 | return error; |
382 | } | 386 | } |
383 | 387 | ||
384 | /* | ||
385 | * Using bio to read from swap. | ||
386 | * This code requires a bit more work than just using buffer heads | ||
387 | * but, it is the recommended way for 2.5/2.6. | ||
388 | * The following are to signal the beginning and end of I/O. Bios | ||
389 | * finish asynchronously, while we want them to happen synchronously. | ||
390 | * A simple atomic_t, and a wait loop take care of this problem. | ||
391 | */ | ||
392 | |||
393 | static atomic_t io_done = ATOMIC_INIT(0); | ||
394 | |||
395 | static int end_io(struct bio *bio, unsigned int num, int err) | ||
396 | { | ||
397 | if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { | ||
398 | printk(KERN_ERR "I/O error reading swsusp image.\n"); | ||
399 | return -EIO; | ||
400 | } | ||
401 | atomic_set(&io_done, 0); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static struct block_device *resume_bdev; | 388 | static struct block_device *resume_bdev; |
406 | 389 | ||
407 | /** | 390 | /** |
@@ -409,15 +392,15 @@ static struct block_device *resume_bdev; | |||
409 | * @rw: READ or WRITE. | 392 | * @rw: READ or WRITE. |
410 | * @off physical offset of page. | 393 | * @off physical offset of page. |
411 | * @page: page we're reading or writing. | 394 | * @page: page we're reading or writing. |
395 | * @bio_chain: list of pending biod (for async reading) | ||
412 | * | 396 | * |
413 | * Straight from the textbook - allocate and initialize the bio. | 397 | * Straight from the textbook - allocate and initialize the bio. |
414 | * If we're writing, make sure the page is marked as dirty. | 398 | * If we're reading, make sure the page is marked as dirty. |
415 | * Then submit it and wait. | 399 | * Then submit it and, if @bio_chain == NULL, wait. |
416 | */ | 400 | */ |
417 | 401 | static int submit(int rw, pgoff_t page_off, struct page *page, | |
418 | static int submit(int rw, pgoff_t page_off, void *page) | 402 | struct bio **bio_chain) |
419 | { | 403 | { |
420 | int error = 0; | ||
421 | struct bio *bio; | 404 | struct bio *bio; |
422 | 405 | ||
423 | bio = bio_alloc(GFP_ATOMIC, 1); | 406 | bio = bio_alloc(GFP_ATOMIC, 1); |
@@ -425,33 +408,40 @@ static int submit(int rw, pgoff_t page_off, void *page) | |||
425 | return -ENOMEM; | 408 | return -ENOMEM; |
426 | bio->bi_sector = page_off * (PAGE_SIZE >> 9); | 409 | bio->bi_sector = page_off * (PAGE_SIZE >> 9); |
427 | bio->bi_bdev = resume_bdev; | 410 | bio->bi_bdev = resume_bdev; |
428 | bio->bi_end_io = end_io; | 411 | bio->bi_end_io = end_swap_bio_read; |
429 | 412 | ||
430 | if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { | 413 | if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { |
431 | printk("swsusp: ERROR: adding page to bio at %ld\n",page_off); | 414 | printk("swsusp: ERROR: adding page to bio at %ld\n", page_off); |
432 | error = -EFAULT; | 415 | bio_put(bio); |
433 | goto Done; | 416 | return -EFAULT; |
434 | } | 417 | } |
435 | 418 | ||
436 | atomic_set(&io_done, 1); | 419 | lock_page(page); |
437 | submit_bio(rw | (1 << BIO_RW_SYNC), bio); | 420 | bio_get(bio); |
438 | while (atomic_read(&io_done)) | 421 | |
439 | yield(); | 422 | if (bio_chain == NULL) { |
440 | if (rw == READ) | 423 | submit_bio(rw | (1 << BIO_RW_SYNC), bio); |
441 | bio_set_pages_dirty(bio); | 424 | wait_on_page_locked(page); |
442 | Done: | 425 | if (rw == READ) |
443 | bio_put(bio); | 426 | bio_set_pages_dirty(bio); |
444 | return error; | 427 | bio_put(bio); |
428 | } else { | ||
429 | get_page(page); | ||
430 | bio->bi_private = *bio_chain; | ||
431 | *bio_chain = bio; | ||
432 | submit_bio(rw | (1 << BIO_RW_SYNC), bio); | ||
433 | } | ||
434 | return 0; | ||
445 | } | 435 | } |
446 | 436 | ||
447 | static int bio_read_page(pgoff_t page_off, void *page) | 437 | static int bio_read_page(pgoff_t page_off, void *addr, struct bio **bio_chain) |
448 | { | 438 | { |
449 | return submit(READ, page_off, page); | 439 | return submit(READ, page_off, virt_to_page(addr), bio_chain); |
450 | } | 440 | } |
451 | 441 | ||
452 | static int bio_write_page(pgoff_t page_off, void *page) | 442 | static int bio_write_page(pgoff_t page_off, void *addr) |
453 | { | 443 | { |
454 | return submit(WRITE, page_off, page); | 444 | return submit(WRITE, page_off, virt_to_page(addr), NULL); |
455 | } | 445 | } |
456 | 446 | ||
457 | /** | 447 | /** |
@@ -476,7 +466,7 @@ static int get_swap_reader(struct swap_map_handle *handle, | |||
476 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | 466 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); |
477 | if (!handle->cur) | 467 | if (!handle->cur) |
478 | return -ENOMEM; | 468 | return -ENOMEM; |
479 | error = bio_read_page(swp_offset(start), handle->cur); | 469 | error = bio_read_page(swp_offset(start), handle->cur, NULL); |
480 | if (error) { | 470 | if (error) { |
481 | release_swap_reader(handle); | 471 | release_swap_reader(handle); |
482 | return error; | 472 | return error; |
@@ -485,7 +475,8 @@ static int get_swap_reader(struct swap_map_handle *handle, | |||
485 | return 0; | 475 | return 0; |
486 | } | 476 | } |
487 | 477 | ||
488 | static int swap_read_page(struct swap_map_handle *handle, void *buf) | 478 | static int swap_read_page(struct swap_map_handle *handle, void *buf, |
479 | struct bio **bio_chain) | ||
489 | { | 480 | { |
490 | unsigned long offset; | 481 | unsigned long offset; |
491 | int error; | 482 | int error; |
@@ -495,16 +486,17 @@ static int swap_read_page(struct swap_map_handle *handle, void *buf) | |||
495 | offset = handle->cur->entries[handle->k]; | 486 | offset = handle->cur->entries[handle->k]; |
496 | if (!offset) | 487 | if (!offset) |
497 | return -EFAULT; | 488 | return -EFAULT; |
498 | error = bio_read_page(offset, buf); | 489 | error = bio_read_page(offset, buf, bio_chain); |
499 | if (error) | 490 | if (error) |
500 | return error; | 491 | return error; |
501 | if (++handle->k >= MAP_PAGE_ENTRIES) { | 492 | if (++handle->k >= MAP_PAGE_ENTRIES) { |
493 | error = wait_on_bio_chain(bio_chain); | ||
502 | handle->k = 0; | 494 | handle->k = 0; |
503 | offset = handle->cur->next_swap; | 495 | offset = handle->cur->next_swap; |
504 | if (!offset) | 496 | if (!offset) |
505 | release_swap_reader(handle); | 497 | release_swap_reader(handle); |
506 | else | 498 | else if (!error) |
507 | error = bio_read_page(offset, handle->cur); | 499 | error = bio_read_page(offset, handle->cur, NULL); |
508 | } | 500 | } |
509 | return error; | 501 | return error; |
510 | } | 502 | } |
@@ -517,38 +509,48 @@ static int swap_read_page(struct swap_map_handle *handle, void *buf) | |||
517 | 509 | ||
518 | static int load_image(struct swap_map_handle *handle, | 510 | static int load_image(struct swap_map_handle *handle, |
519 | struct snapshot_handle *snapshot, | 511 | struct snapshot_handle *snapshot, |
520 | unsigned int nr_pages) | 512 | unsigned int nr_to_read) |
521 | { | 513 | { |
522 | unsigned int m; | 514 | unsigned int m; |
523 | int ret; | ||
524 | int error = 0; | 515 | int error = 0; |
525 | struct timeval start; | 516 | struct timeval start; |
526 | struct timeval stop; | 517 | struct timeval stop; |
518 | struct bio *bio; | ||
519 | int err2; | ||
520 | unsigned nr_pages; | ||
527 | 521 | ||
528 | printk("Loading image data pages (%u pages) ... ", nr_pages); | 522 | printk("Loading image data pages (%u pages) ... ", nr_to_read); |
529 | m = nr_pages / 100; | 523 | m = nr_to_read / 100; |
530 | if (!m) | 524 | if (!m) |
531 | m = 1; | 525 | m = 1; |
532 | nr_pages = 0; | 526 | nr_pages = 0; |
527 | bio = NULL; | ||
533 | do_gettimeofday(&start); | 528 | do_gettimeofday(&start); |
534 | do { | 529 | for ( ; ; ) { |
535 | ret = snapshot_write_next(snapshot, PAGE_SIZE); | 530 | error = snapshot_write_next(snapshot, PAGE_SIZE); |
536 | if (ret > 0) { | 531 | if (error <= 0) |
537 | error = swap_read_page(handle, data_of(*snapshot)); | 532 | break; |
538 | if (error) | 533 | error = swap_read_page(handle, data_of(*snapshot), &bio); |
539 | break; | 534 | if (error) |
540 | if (!(nr_pages % m)) | 535 | break; |
541 | printk("\b\b\b\b%3d%%", nr_pages / m); | 536 | if (snapshot->sync_read) |
542 | nr_pages++; | 537 | error = wait_on_bio_chain(&bio); |
543 | } | 538 | if (error) |
544 | } while (ret > 0); | 539 | break; |
540 | if (!(nr_pages % m)) | ||
541 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
542 | nr_pages++; | ||
543 | } | ||
544 | err2 = wait_on_bio_chain(&bio); | ||
545 | do_gettimeofday(&stop); | 545 | do_gettimeofday(&stop); |
546 | if (!error) | ||
547 | error = err2; | ||
546 | if (!error) { | 548 | if (!error) { |
547 | printk("\b\b\b\bdone\n"); | 549 | printk("\b\b\b\bdone\n"); |
548 | if (!snapshot_image_loaded(snapshot)) | 550 | if (!snapshot_image_loaded(snapshot)) |
549 | error = -ENODATA; | 551 | error = -ENODATA; |
550 | } | 552 | } |
551 | show_speed(&start, &stop, nr_pages, "Read"); | 553 | show_speed(&start, &stop, nr_to_read, "Read"); |
552 | return error; | 554 | return error; |
553 | } | 555 | } |
554 | 556 | ||
@@ -571,7 +573,7 @@ int swsusp_read(void) | |||
571 | header = (struct swsusp_info *)data_of(snapshot); | 573 | header = (struct swsusp_info *)data_of(snapshot); |
572 | error = get_swap_reader(&handle, swsusp_header.image); | 574 | error = get_swap_reader(&handle, swsusp_header.image); |
573 | if (!error) | 575 | if (!error) |
574 | error = swap_read_page(&handle, header); | 576 | error = swap_read_page(&handle, header, NULL); |
575 | if (!error) | 577 | if (!error) |
576 | error = load_image(&handle, &snapshot, header->pages - 1); | 578 | error = load_image(&handle, &snapshot, header->pages - 1); |
577 | release_swap_reader(&handle); | 579 | release_swap_reader(&handle); |
@@ -597,7 +599,7 @@ int swsusp_check(void) | |||
597 | if (!IS_ERR(resume_bdev)) { | 599 | if (!IS_ERR(resume_bdev)) { |
598 | set_blocksize(resume_bdev, PAGE_SIZE); | 600 | set_blocksize(resume_bdev, PAGE_SIZE); |
599 | memset(&swsusp_header, 0, sizeof(swsusp_header)); | 601 | memset(&swsusp_header, 0, sizeof(swsusp_header)); |
600 | if ((error = bio_read_page(0, &swsusp_header))) | 602 | if ((error = bio_read_page(0, &swsusp_header, NULL))) |
601 | return error; | 603 | return error; |
602 | if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { | 604 | if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { |
603 | memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); | 605 | memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); |
diff --git a/mm/page_io.c b/mm/page_io.c index f46a9862b7ef..d4840ecbf8f9 100644 --- a/mm/page_io.c +++ b/mm/page_io.c | |||
@@ -74,7 +74,7 @@ static int end_swap_bio_write(struct bio *bio, unsigned int bytes_done, int err) | |||
74 | return 0; | 74 | return 0; |
75 | } | 75 | } |
76 | 76 | ||
77 | static int end_swap_bio_read(struct bio *bio, unsigned int bytes_done, int err) | 77 | int end_swap_bio_read(struct bio *bio, unsigned int bytes_done, int err) |
78 | { | 78 | { |
79 | const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); | 79 | const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); |
80 | struct page *page = bio->bi_io_vec[0].bv_page; | 80 | struct page *page = bio->bi_io_vec[0].bv_page; |