The patch titled swsusp: write speedup has been added to the -mm tree. Its filename is swsusp-write-speedup.patch See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this ------------------------------------------------------ Subject: swsusp: write speedup From: Andrew Morton <akpm@xxxxxxxx> Switch the swsusp writeout code from 4k-at-a-time to 4MB-at-a-time. Crufty old PIII testbox: 12.9 MB/s -> 20.9 MB/s Sony Vaio: 14.7 MB/s -> 26.5 MB/s The implementation is crude. A better one would use larger BIOs, but wouldn't gain any performance. The memcpys will be mostly pipelined with the IO and basically come for free. The ENOMEM path has not been tested. It should be. Cc: Pavel Machek <pavel@xxxxxx> Cc: "Rafael J. Wysocki" <rjw@xxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxx> --- include/linux/swap.h | 5 +- kernel/power/swap.c | 93 +++++++++++++++++++++++++++++++++-------- mm/page_io.c | 23 +++++++--- 3 files changed, 97 insertions(+), 24 deletions(-) diff -puN kernel/power/swap.c~swsusp-write-speedup kernel/power/swap.c --- a/kernel/power/swap.c~swsusp-write-speedup +++ a/kernel/power/swap.c @@ -49,18 +49,16 @@ static int mark_swapfiles(swp_entry_t st { int error; - rw_swap_page_sync(READ, - swp_entry(root_swap, 0), - virt_to_page((unsigned long)&swsusp_header)); + rw_swap_page_sync(READ, swp_entry(root_swap, 0), + virt_to_page((unsigned long)&swsusp_header), NULL); if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); memcpy(swsusp_header.sig,SWSUSP_SIG, 10); swsusp_header.image = start; - error = rw_swap_page_sync(WRITE, - swp_entry(root_swap, 0), - virt_to_page((unsigned long) - &swsusp_header)); + error = rw_swap_page_sync(WRITE, swp_entry(root_swap, 0), + virt_to_page((unsigned long)&swsusp_header), + NULL); } else { pr_debug("swsusp: Partition is not swap space.\n"); error = -ENODEV; @@ -88,16 +86,37 @@ static int swsusp_swap_check(void) /* Th * write_page - Write one page to given swap location. * @buf: Address we're writing. * @offset: Offset of the swap page we're writing to. + * @bio_chain: Link the next write BIO here */ -static int write_page(void *buf, unsigned long offset) +static int write_page(void *buf, unsigned long offset, struct bio **bio_chain) { swp_entry_t entry; int error = -ENOSPC; if (offset) { + struct page *page = virt_to_page(buf); + + if (bio_chain) { + /* + * Whether or not we successfully allocated a copy page, + * we take a ref on the page here. It gets undone in + * wait_on_bio_chain(). + */ + struct page *page_copy; + page_copy = alloc_page(GFP_ATOMIC); + if (page_copy == NULL) { + WARN_ON_ONCE(1); + bio_chain = NULL; /* Go synchronous */ + get_page(page); + } else { + memcpy(page_address(page_copy), + page_address(page), PAGE_SIZE); + page = page_copy; + } + } entry = swp_entry(root_swap, offset); - error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf)); + error = rw_swap_page_sync(WRITE, entry, page, bio_chain); } return error; } @@ -185,37 +204,68 @@ static int get_swap_writer(struct swap_m return 0; } -static int swap_write_page(struct swap_map_handle *handle, void *buf) +static int wait_on_bio_chain(struct bio **bio_chain) { - int error; + struct bio *bio; + struct bio *next_bio; + int ret = 0; + + if (bio_chain == NULL) + return 0; + + bio = *bio_chain; + while (bio) { + struct page *page; + + next_bio = bio->bi_private; + page = bio->bi_io_vec[0].bv_page; + wait_on_page_locked(page); + if (!PageUptodate(page) || PageError(page)) + ret = -EIO; + put_page(page); + bio_put(bio); + bio = next_bio; + } + *bio_chain = NULL; + return ret; +} + +static int swap_write_page(struct swap_map_handle *handle, void *buf, + struct bio **bio_chain) +{ + int error = 0; unsigned long offset; if (!handle->cur) return -EINVAL; offset = alloc_swap_page(root_swap, handle->bitmap); - error = write_page(buf, offset); + error = write_page(buf, offset, bio_chain); if (error) return error; handle->cur->entries[handle->k++] = offset; if (handle->k >= MAP_PAGE_ENTRIES) { + error = wait_on_bio_chain(bio_chain); + if (error) + goto out; offset = alloc_swap_page(root_swap, handle->bitmap); if (!offset) return -ENOSPC; handle->cur->next_swap = offset; - error = write_page(handle->cur, handle->cur_swap); + error = write_page(handle->cur, handle->cur_swap, NULL); if (error) - return error; + goto out; memset(handle->cur, 0, PAGE_SIZE); handle->cur_swap = offset; handle->k = 0; } - return 0; +out: + return error; } static int flush_swap_writer(struct swap_map_handle *handle) { if (handle->cur && handle->cur_swap) - return write_page(handle->cur, handle->cur_swap); + return write_page(handle->cur, handle->cur_swap, NULL); else return -EINVAL; } @@ -232,6 +282,8 @@ static int save_image(struct swap_map_ha int ret; int error = 0; int nr_pages; + int err2; + struct bio *bio; struct timeval start; struct timeval stop; @@ -240,11 +292,13 @@ static int save_image(struct swap_map_ha if (!m) m = 1; nr_pages = 0; + bio = NULL; do_gettimeofday(&start); do { ret = snapshot_read_next(snapshot, PAGE_SIZE); if (ret > 0) { - error = swap_write_page(handle, data_of(*snapshot)); + error = swap_write_page(handle, data_of(*snapshot), + &bio); if (error) break; if (!(nr_pages % m)) @@ -252,8 +306,11 @@ static int save_image(struct swap_map_ha nr_pages++; } } while (ret > 0); + err2 = wait_on_bio_chain(&bio); do_gettimeofday(&stop); if (!error) + error = err2; + if (!error) printk("\b\b\b\bdone\n"); show_speed(&start, &stop, nr_to_write, "Wrote"); return error; @@ -307,7 +364,7 @@ int swsusp_write(void) error = get_swap_writer(&handle); if (!error) { unsigned long start = handle.cur_swap; - error = swap_write_page(&handle, header); + error = swap_write_page(&handle, header, NULL); if (!error) error = save_image(&handle, &snapshot, header->pages - 1); diff -puN include/linux/swap.h~swsusp-write-speedup include/linux/swap.h --- a/include/linux/swap.h~swsusp-write-speedup +++ a/include/linux/swap.h @@ -10,6 +10,8 @@ #include <asm/atomic.h> #include <asm/page.h> +struct bio; + #define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */ #define SWAP_FLAG_PRIO_MASK 0x7fff #define SWAP_FLAG_PRIO_SHIFT 0 @@ -211,7 +213,8 @@ extern void swap_unplug_io_fn(struct bac /* linux/mm/page_io.c */ extern int swap_readpage(struct file *, struct page *); extern int swap_writepage(struct page *page, struct writeback_control *wbc); -extern int rw_swap_page_sync(int, swp_entry_t, struct page *); +extern int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page, + struct bio **bio_chain); /* linux/mm/swap_state.c */ extern struct address_space swapper_space; diff -puN mm/page_io.c~swsusp-write-speedup mm/page_io.c --- a/mm/page_io.c~swsusp-write-speedup +++ a/mm/page_io.c @@ -137,10 +137,12 @@ out: * We use end_swap_bio_read() even for writes, because it happens to do what * we want. */ -int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page) +int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page, + struct bio **bio_chain) { struct bio *bio; int ret = 0; + int bio_rw; lock_page(page); @@ -151,11 +153,22 @@ int rw_swap_page_sync(int rw, swp_entry_ goto out; } - submit_bio(rw | (1 << BIO_RW_SYNC), bio); - wait_on_page_locked(page); + bio_rw = rw; + if (!bio_chain) + bio_rw |= (1 << BIO_RW_SYNC); + if (bio_chain) + bio_get(bio); + submit_bio(bio_rw, bio); + if (bio_chain == NULL) { + wait_on_page_locked(page); - if (!PageUptodate(page) || PageError(page)) - ret = -EIO; + if (!PageUptodate(page) || PageError(page)) + ret = -EIO; + } + if (bio_chain) { + bio->bi_private = *bio_chain; + *bio_chain = bio; + } out: return ret; } _ Patches currently in -mm which might be from akpm@xxxxxxxx are dont-select-config_hotplug.patch x86_64-e820c-needs-pgtableh.patch count_vm_events-fix.patch add-computone-intelliport-plus-serial-hotplug-support.patch add-specialix-io8-card-support-hotplug-support.patch fadvise-remove-dead-comments.patch vt-remove-vt-specific-declarations-and-definitions-from.patch tty-remove-include-of-screen_infoh-from-ttyh.patch md-include-sector-number-in-messages-about-corrected-read-errors.patch md-oops-workaround.patch disable-debugging-version-of-write_lock.patch acpi-initialise-cm_sbs_sem.patch acpi-asus-s3-resume-fix-fix.patch sony_apci-resume.patch maestro3-section-fix.patch kauditd_thread-warning-fix.patch add-__must_check-to-device-management-code.patch v4l-dev2-handle-__must_check.patch videodev-check-return-values.patch git-geode-fixup.patch git-gfs2.patch git-gfs2-fixup.patch git-ia64.patch git-ia64-fixup.patch git-ieee1394-fixup.patch git-input.patch git-klibc.patch git-hdrcleanup-vs-git-klibc-on-ia64.patch git-hdrcleanup-vs-git-klibc-on-ia64-2.patch git-libata-all.patch sata-is-bust-on-s390.patch git-netdev-all.patch e1000-irq-naming-update.patch 8139cp-printk-fix.patch 82596-section-fixes.patch ac3200-section-fixes.patch cops-section-fix.patch cs89x0-section-fix.patch at1700-section-fix.patch e2100-section-fix.patch eepro-section-fix.patch eexpress-section-fix.patch es3210-section-fix.patch eth16i-section-fix.patch lance-section-fix.patch lne390-section-fix.patch ni52-section-fix.patch ibmtr-section-fix.patch smctr-section-fix.patch wd-section-fix.patch ni65-section-fix.patch seeq8005-section-fix.patch winbond-840-section-fix.patch fealnx-section-fix.patch sundance-section-fix.patch drivers-net-ns83820c-add-paramter-to-disable-auto.patch git-sas.patch serial-8250-sysrq-deadlock-fix.patch serial-fix-uart_bug_txen-test.patch revert-gregkh-pci-pci-test-that-drivers-properly-call-pci_set_master.patch revert-gregkh-pci-msi-drop-pci_msi_quirk.patch revert-gregkh-pci-msi-stop-inheriting-bus-flags-and-check-root-chipset-bus-flags-instead.patch revert-gregkh-pci-msi-factorize-common-msi-detection-code-from-pci_enable_msi-and-msix.patch revert-gregkh-pci-msi-blacklist-pci-e-chipsets-depending-on-hypertransport-msi-capabality.patch revert-gregkh-pci-msi-rename-pci_cap_id_ht_irqconf-into-pci_cap_id_ht.patch revert-gregkh-pci-msi-merge-existing-msi-disabling-quirks.patch revert-VIA-quirk-fixup-additional-PCI-IDs.patch revert-PCI-quirk-VIA-IRQ-fixup-should-only-run-for-VIA-southbridges.patch NCR_D700-section-fix.patch areca-raid-linux-scsi-driver.patch git-scsi-target-fixup.patch usb-storage-uname-in-pr-sc-unneeded-message-fix.patch pm-usb-hcds-use-pm_event_prethaw-fix.patch kill-usb-kconfig-warning.patch rtl8150_disconnect-needs-tasklet_kill.patch git-supertrak-fixup.patch bcm43xx-opencoded-locking.patch mm-x86_64-mm-init-rdtscp-warning-fix.patch sleazy-fpu-feature-x86_64-support.patch x86_64-wire-up-oops_enter-oops_exit.patch adix-tree-rcu-lockless-readside-update-tidy.patch mm-tracking-shared-dirty-pages-checks.patch mm-tracking-shared-dirty-pages-wimp.patch convert-i386-numa-kva-space-to-bootmem-tidy.patch reduce-max_nr_zones-make-display-of-highmem-counters-conditional-on-config_highmem-tidy.patch reduce-max_nr_zones-use-enum-to-define-zones-reformat-and-comment-cleanup.patch acx1xx-wireless-driver.patch tiacx-pci-build-fix.patch tiacx-ia64-fix.patch tiacx-build-fix.patch binfmt_elf-consistently-use-loff_t.patch fdpic-move-roundup-into-linux-kernelh-fix.patch swsusp-warning-fix.patch swsusp-write-timer.patch swsusp-write-speedup.patch swsusp-read-timer.patch swsusp-read-speedup.patch swsusp-read-speedup-cleanup.patch uml-timer-initialization-cleanup-fix.patch uml-move-_kernc-files-fix.patch deprecate-smbfs-in-favour-of-cifs.patch edac-new-opteron-athlon64-memory-controller-driver-tidy.patch x86-microcode-microcode-driver-cleanup-tidy.patch x86-microcode-add-sysfs-and-hotplug-support-fix.patch x86-microcode-add-sysfs-and-hotplug-support-fix-fix.patch add-address_space_operationsbatch_write-tidy.patch fix-weird-logic-in-alloc_fdtable.patch alloc_fdtable-cleanup.patch reiserfs-on-demand-bitmap-loading.patch per-task-delay-accounting-taskstats-interface.patch per-task-delay-accounting-proc-export-of-aggregated-block-i-o-delays.patch delay-accounting-taskstats-interface-send-tgid-once.patch per-task-delay-accounting-taskstats-interface-control-exit-data-through-cpumasks-fix.patch task-watchers-task-watchers.patch task-watchers-add-support-for-per-task-watchers.patch swap_prefetch-vs-zoned-counters.patch ecryptfs-mmap-operations.patch ecryptfs-alpha-build-fix.patch ecryptfs-more-elegant-aes-key-size-manipulation.patch ecryptfs-get_sb_dev-fix.patch namespaces-add-nsproxy-dont-include-compileh.patch namespaces-utsname-switch-to-using-uts-namespaces.patch namespaces-utsname-use-init_utsname-when-appropriate.patch namespaces-utsname-implement-utsname-namespaces.patch namespaces-utsname-sysctl-hack.patch ipc-namespace-core.patch readahead-sysctl-parameters-fix.patch make-copy_from_user_inatomic-not-zero-the-tail-on-i386-vs-reiser4.patch reiser4-hardirq-include-fix.patch reiser4-run-truncate_inode_pages-in-reiser4_delete_inode.patch reiser4-get_sb_dev-fix.patch reiser4-vs-zoned-allocator.patch hpt3xx-rework-rate-filtering-tidy.patch cirrus-logic-framebuffer-i2c-support-fix.patch genirq-convert-the-i386-architecture-to-irq-chips.patch genirq-x86_64-irq-reenable-migrating-irqs-to-other-cpus.patch genirq-msi-simplify-msi-enable-and-disable.patch genirq-ia64-irq-dynamic-irq-support.patch genirq-msi-only-build-msi-apicc-on-ia64-fix.patch genirq-i386-irq-remove-the-msi-assumption-that-irq-==-vector.patch nr_blockdev_pages-in_interrupt-warning.patch device-suspend-debug.patch revert-tty-buffering-comment-out-debug-code.patch slab-leaks3-default-y.patch x86-kmap_atomic-debugging.patch - To unsubscribe from this list: send the line "unsubscribe mm-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html