[PATCH 17/23] Hibernation: Replace bio chain

From: Nigel Cunningham
Date: Mon Sep 27 2010 - 01:46:16 EST


Replace the bio_chain concept with:
(1) simple atomic_t recording how many pages are in flight
(2) a wait queue for waiting for all I/O to complete
(3) a custom completion routine that frees bio structs and pages used
for async writes as they complete, undates the atomic_t and
wakes any waiters.

Signed-off-by: Nigel Cunningham <nigel@xxxxxxxxxxxx>
---
kernel/power/block_io.c | 77 ++++++++++++++++++++++++++++------------------
1 files changed, 47 insertions(+), 30 deletions(-)

diff --git a/kernel/power/block_io.c b/kernel/power/block_io.c
index e6709db..24b6db5 100644
--- a/kernel/power/block_io.c
+++ b/kernel/power/block_io.c
@@ -16,23 +16,61 @@
#include "extents.h"
#include "block_io.h"

-static struct bio *bio_chain;
-
static char *hib_ppio_buffer;
static int hib_ppio_buffer_posn;
int hib_prepare_buffer(void);
void hib_free_buffer(void);

+static atomic_t hib_io_in_progress;
+static DECLARE_WAIT_QUEUE_HEAD(num_in_progress_wait);
+
+/**
+ * hib_end_bio - bio completion function.
+ * @bio: bio that has completed.
+ * @err: Error value. Yes, like end_swap_bio_read, we ignore it.
+ *
+ * Function called by the block driver from interrupt context when I/O is
+ * completed. If we were writing the page, we want to free it and will have
+ * set bio->bi_private to the parameter we should use in telling the page
+ * allocation accounting code what the page was allocated for. If we're
+ * reading the page, it will be in the singly linked list made from
+ * page->private pointers.
+ **/
+static void hib_end_bio(struct bio *bio, int err)
+{
+ const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+ struct page *page = bio->bi_io_vec[0].bv_page;
+
+ if (!uptodate) {
+ SetPageError(page);
+ ClearPageUptodate(page);
+ printk(KERN_ALERT "I/O on swap-device (%u:%u:%Lu)\n",
+ imajor(bio->bi_bdev->bd_inode),
+ iminor(bio->bi_bdev->bd_inode),
+ (unsigned long long)bio->bi_sector);
+ } else {
+ SetPageUptodate(page);
+ }
+ unlock_page(page);
+ bio_put(bio);
+
+ if (bio->bi_private)
+ __free_page(page);
+
+ atomic_dec(&hib_io_in_progress);
+ wake_up(&num_in_progress_wait);
+}
+
/**
* 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)
+ * @sync: whether the i/o should be done synchronously
*
* 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.
+ * Then submit it and, if @sync, wait.
*/
static int submit(int rw, struct block_device *bdev, sector_t sector,
struct page *page, int sync)
@@ -43,7 +81,8 @@ static int submit(int rw, struct block_device *bdev, sector_t sector,
bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1);
bio->bi_sector = sector;
bio->bi_bdev = bdev;
- bio->bi_end_io = end_swap_bio_read;
+ bio->bi_private = (void *) (rw && !sync);
+ bio->bi_end_io = hib_end_bio;

if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
printk(KERN_ERR "PM: Adding page to bio failed at %llu\n",
@@ -54,6 +93,7 @@ static int submit(int rw, struct block_device *bdev, sector_t sector,

lock_page(page);
bio_get(bio);
+ atomic_inc(&hib_io_in_progress);

if (sync) {
submit_bio(bio_rw, bio);
@@ -64,8 +104,6 @@ static int submit(int rw, struct block_device *bdev, sector_t sector,
} 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;
@@ -85,29 +123,8 @@ int hib_bio_write_page(pgoff_t page_off, void *addr, int sync)

int hib_wait_on_bio_chain(void)
{
- 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;
+ wait_event(num_in_progress_wait, !atomic_read(&hib_io_in_progress));
+ return 0;
}

/*
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/