| /* |
| * This file provides functions for block I/O operations on swap/file. |
| * |
| * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@ucw.cz> |
| * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl> |
| * |
| * This file is released under the GPLv2. |
| */ |
| |
| #include <linux/bio.h> |
| #include <linux/kernel.h> |
| #include <linux/pagemap.h> |
| #include <linux/swap.h> |
| |
| #include "power.h" |
| |
| /** |
| * submit - submit BIO request. |
| * @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. |
| */ |
| static int submit(int rw, struct block_device *bdev, sector_t sector, |
| struct page *page, struct bio **bio_chain) |
| { |
| const int bio_rw = rw | REQ_SYNC; |
| struct bio *bio; |
| |
| bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1); |
| bio->bi_sector = sector; |
| bio->bi_bdev = bdev; |
| bio->bi_end_io = end_swap_bio_read; |
| |
| if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { |
| printk(KERN_ERR "PM: Adding page to bio failed at %llu\n", |
| (unsigned long long)sector); |
| bio_put(bio); |
| return -EFAULT; |
| } |
| |
| lock_page(page); |
| bio_get(bio); |
| |
| if (bio_chain == NULL) { |
| submit_bio(bio_rw, bio); |
| wait_on_page_locked(page); |
| if (rw == READ) |
| bio_set_pages_dirty(bio); |
| bio_put(bio); |
| } else { |
| if (rw == READ) |
| get_page(page); /* These pages are freed later */ |
| bio->bi_private = *bio_chain; |
| *bio_chain = bio; |
| submit_bio(bio_rw, bio); |
| } |
| return 0; |
| } |
| |
| int hib_bio_read_page(pgoff_t page_off, void *addr, struct bio **bio_chain) |
| { |
| return submit(READ, hib_resume_bdev, page_off * (PAGE_SIZE >> 9), |
| virt_to_page(addr), bio_chain); |
| } |
| |
| int hib_bio_write_page(pgoff_t page_off, void *addr, struct bio **bio_chain) |
| { |
| return submit(WRITE, hib_resume_bdev, page_off * (PAGE_SIZE >> 9), |
| virt_to_page(addr), bio_chain); |
| } |
| |
| int hib_wait_on_bio_chain(struct bio **bio_chain) |
| { |
| 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; |
| } |