#include <linux/device.h>
#include <linux/buffer_head.h>
#include <linux/bio.h>
-#include <linux/blkdev.h>
#include <linux/swap.h>
#include <linux/swapops.h>
#include <linux/pm.h>
{
int error;
- rw_swap_page_sync(READ, swp_entry(root_swap, 0),
- virt_to_page((unsigned long)&swsusp_header), NULL);
+ rw_swap_page_sync(READ,
+ swp_entry(root_swap, 0),
+ virt_to_page((unsigned long)&swsusp_header));
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),
- NULL);
+ error = rw_swap_page_sync(WRITE,
+ swp_entry(root_swap, 0),
+ virt_to_page((unsigned long)
+ &swsusp_header));
} else {
pr_debug("swsusp: Partition is not swap space.\n");
error = -ENODEV;
* 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, struct bio **bio_chain)
+static int write_page(void *buf, unsigned long offset)
{
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, page, bio_chain);
+ error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
}
return error;
}
handle->bitmap = NULL;
}
-static void show_speed(struct timeval *start, struct timeval *stop,
- unsigned nr_pages, char *msg)
-{
- s64 elapsed_centisecs64;
- int centisecs;
- int k;
- int kps;
-
- elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start);
- do_div(elapsed_centisecs64, NSEC_PER_SEC / 100);
- centisecs = elapsed_centisecs64;
- if (centisecs == 0)
- centisecs = 1; /* avoid div-by-zero */
- k = nr_pages * (PAGE_SIZE / 1024);
- kps = (k * 100) / centisecs;
- printk("%s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", msg, k,
- centisecs / 100, centisecs % 100,
- kps / 1000, (kps % 1000) / 10);
-}
-
static int get_swap_writer(struct swap_map_handle *handle)
{
handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
return 0;
}
-static int wait_on_bio_chain(struct bio **bio_chain)
+static int swap_write_page(struct swap_map_handle *handle, void *buf)
{
- struct bio *bio;
- struct bio *next_bio;
- int ret = 0;
-
- if (bio_chain == NULL)
- return 0;
-
- bio = *bio_chain;
- if (bio == NULL)
- return 0;
- 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;
+ int error;
unsigned long offset;
if (!handle->cur)
return -EINVAL;
offset = alloc_swap_page(root_swap, handle->bitmap);
- error = write_page(buf, offset, bio_chain);
+ error = write_page(buf, offset);
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, NULL);
+ error = write_page(handle->cur, handle->cur_swap);
if (error)
- goto out;
+ return error;
memset(handle->cur, 0, PAGE_SIZE);
handle->cur_swap = offset;
handle->k = 0;
}
-out:
- return error;
+ return 0;
}
static int flush_swap_writer(struct swap_map_handle *handle)
{
if (handle->cur && handle->cur_swap)
- return write_page(handle->cur, handle->cur_swap, NULL);
+ return write_page(handle->cur, handle->cur_swap);
else
return -EINVAL;
}
static int save_image(struct swap_map_handle *handle,
struct snapshot_handle *snapshot,
- unsigned int nr_to_write)
+ unsigned int nr_pages)
{
unsigned int m;
int ret;
int error = 0;
- int nr_pages;
- int err2;
- struct bio *bio;
- struct timeval start;
- struct timeval stop;
- printk("Saving image data pages (%u pages) ... ", nr_to_write);
- m = nr_to_write / 100;
+ printk("Saving image data pages (%u pages) ... ", nr_pages);
+ m = nr_pages / 100;
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),
- &bio);
+ error = swap_write_page(handle, data_of(*snapshot));
if (error)
break;
if (!(nr_pages % m))
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;
}
struct swap_map_handle handle;
struct snapshot_handle snapshot;
struct swsusp_info *header;
+ unsigned long start;
int error;
if ((error = swsusp_swap_check())) {
- printk(KERN_ERR "swsusp: Cannot find swap device, try "
- "swapon -a.\n");
+ printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n");
return error;
}
memset(&snapshot, 0, sizeof(struct snapshot_handle));
}
error = get_swap_writer(&handle);
if (!error) {
- unsigned long start = handle.cur_swap;
- error = swap_write_page(&handle, header, NULL);
- if (!error)
- error = save_image(&handle, &snapshot,
- header->pages - 1);
- if (!error) {
- flush_swap_writer(&handle);
- printk("S");
- error = mark_swapfiles(swp_entry(root_swap, start));
- printk("|\n");
- }
+ start = handle.cur_swap;
+ error = swap_write_page(&handle, header);
+ }
+ if (!error)
+ error = save_image(&handle, &snapshot, header->pages - 1);
+ if (!error) {
+ flush_swap_writer(&handle);
+ printk("S");
+ error = mark_swapfiles(swp_entry(root_swap, start));
+ printk("|\n");
}
if (error)
free_all_swap_pages(root_swap, handle.bitmap);
return error;
}
+/*
+ * Using bio to read from swap.
+ * This code requires a bit more work than just using buffer heads
+ * but, it is the recommended way for 2.5/2.6.
+ * The following are to signal the beginning and end of I/O. Bios
+ * finish asynchronously, while we want them to happen synchronously.
+ * A simple atomic_t, and a wait loop take care of this problem.
+ */
+
+static atomic_t io_done = ATOMIC_INIT(0);
+
+static int end_io(struct bio *bio, unsigned int num, int err)
+{
+ if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
+ panic("I/O error reading memory image");
+ atomic_set(&io_done, 0);
+ return 0;
+}
+
static struct block_device *resume_bdev;
/**
* @rw: READ or WRITE.
* @off physical offset of page.
* @page: page we're reading or writing.
- * @bio_chain: list of pending biod (for async reading)
*
* Straight from the textbook - allocate and initialize the bio.
- * If we're reading, make sure the page is marked as dirty.
- * Then submit it and, if @bio_chain == NULL, wait.
+ * If we're writing, make sure the page is marked as dirty.
+ * Then submit it and wait.
*/
-static int submit(int rw, pgoff_t page_off, struct page *page,
- struct bio **bio_chain)
+
+static int submit(int rw, pgoff_t page_off, void *page)
{
+ int error = 0;
struct bio *bio;
bio = bio_alloc(GFP_ATOMIC, 1);
return -ENOMEM;
bio->bi_sector = page_off * (PAGE_SIZE >> 9);
bio->bi_bdev = resume_bdev;
- bio->bi_end_io = end_swap_bio_read;
+ bio->bi_end_io = end_io;
- if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
- printk("swsusp: ERROR: adding page to bio at %ld\n", page_off);
- bio_put(bio);
- return -EFAULT;
+ if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
+ printk("swsusp: ERROR: adding page to bio at %ld\n",page_off);
+ error = -EFAULT;
+ goto Done;
}
- lock_page(page);
- bio_get(bio);
-
- if (bio_chain == NULL) {
- submit_bio(rw | (1 << BIO_RW_SYNC), bio);
- wait_on_page_locked(page);
- if (rw == READ)
- bio_set_pages_dirty(bio);
- bio_put(bio);
- } else {
- get_page(page);
- bio->bi_private = *bio_chain;
- *bio_chain = bio;
- submit_bio(rw, bio);
- }
- return 0;
+ atomic_set(&io_done, 1);
+ submit_bio(rw | (1 << BIO_RW_SYNC), bio);
+ while (atomic_read(&io_done))
+ yield();
+ if (rw == READ)
+ bio_set_pages_dirty(bio);
+ Done:
+ bio_put(bio);
+ return error;
}
-static int bio_read_page(pgoff_t page_off, void *addr, struct bio **bio_chain)
+static int bio_read_page(pgoff_t page_off, void *page)
{
- return submit(READ, page_off, virt_to_page(addr), bio_chain);
+ return submit(READ, page_off, page);
}
-static int bio_write_page(pgoff_t page_off, void *addr)
+static int bio_write_page(pgoff_t page_off, void *page)
{
- return submit(WRITE, page_off, virt_to_page(addr), NULL);
+ return submit(WRITE, page_off, page);
}
/**
handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
if (!handle->cur)
return -ENOMEM;
- error = bio_read_page(swp_offset(start), handle->cur, NULL);
+ error = bio_read_page(swp_offset(start), handle->cur);
if (error) {
release_swap_reader(handle);
return error;
return 0;
}
-static int swap_read_page(struct swap_map_handle *handle, void *buf,
- struct bio **bio_chain)
+static int swap_read_page(struct swap_map_handle *handle, void *buf)
{
unsigned long offset;
int error;
offset = handle->cur->entries[handle->k];
if (!offset)
return -EFAULT;
- error = bio_read_page(offset, buf, bio_chain);
+ error = bio_read_page(offset, buf);
if (error)
return error;
if (++handle->k >= MAP_PAGE_ENTRIES) {
- error = wait_on_bio_chain(bio_chain);
handle->k = 0;
offset = handle->cur->next_swap;
if (!offset)
release_swap_reader(handle);
- else if (!error)
- error = bio_read_page(offset, handle->cur, NULL);
+ else
+ error = bio_read_page(offset, handle->cur);
}
return error;
}
static int load_image(struct swap_map_handle *handle,
struct snapshot_handle *snapshot,
- unsigned int nr_to_read)
+ unsigned int nr_pages)
{
unsigned int m;
+ int ret;
int error = 0;
- struct timeval start;
- struct timeval stop;
- struct bio *bio;
- int err2;
- unsigned nr_pages;
- printk("Loading image data pages (%u pages) ... ", nr_to_read);
- m = nr_to_read / 100;
+ printk("Loading image data pages (%u pages) ... ", nr_pages);
+ m = nr_pages / 100;
if (!m)
m = 1;
nr_pages = 0;
- bio = NULL;
- do_gettimeofday(&start);
- for ( ; ; ) {
- error = snapshot_write_next(snapshot, PAGE_SIZE);
- if (error <= 0)
- break;
- error = swap_read_page(handle, data_of(*snapshot), &bio);
- if (error)
- break;
- if (snapshot->sync_read)
- error = wait_on_bio_chain(&bio);
- if (error)
- break;
- if (!(nr_pages % m))
- printk("\b\b\b\b%3d%%", nr_pages / m);
- nr_pages++;
- }
- err2 = wait_on_bio_chain(&bio);
- do_gettimeofday(&stop);
- if (!error)
- error = err2;
+ do {
+ ret = snapshot_write_next(snapshot, PAGE_SIZE);
+ if (ret > 0) {
+ error = swap_read_page(handle, data_of(*snapshot));
+ if (error)
+ break;
+ if (!(nr_pages % m))
+ printk("\b\b\b\b%3d%%", nr_pages / m);
+ nr_pages++;
+ }
+ } while (ret > 0);
if (!error) {
printk("\b\b\b\bdone\n");
if (!snapshot_image_loaded(snapshot))
error = -ENODATA;
}
- show_speed(&start, &stop, nr_to_read, "Read");
return error;
}
header = (struct swsusp_info *)data_of(snapshot);
error = get_swap_reader(&handle, swsusp_header.image);
if (!error)
- error = swap_read_page(&handle, header, NULL);
+ error = swap_read_page(&handle, header);
if (!error)
error = load_image(&handle, &snapshot, header->pages - 1);
release_swap_reader(&handle);
if (!IS_ERR(resume_bdev)) {
set_blocksize(resume_bdev, PAGE_SIZE);
memset(&swsusp_header, 0, sizeof(swsusp_header));
- if ((error = bio_read_page(0, &swsusp_header, NULL)))
+ if ((error = bio_read_page(0, &swsusp_header)))
return error;
if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);