Commit 5aa6ba1a authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] direct-io bio_add_page fix

Patch from Badari Pulavarty

There was a corner case in the conversion of direct-io to use
bio_add_page() where we would start a new page out at the wrong sector
number.

Fix that by explicitly passing in the current page's starting sector,
and use that in the new BIO if we have to open a new one.

Fix an error-path page->count leak in dio_bio_add_page().
parent 75a08275
...@@ -388,7 +388,7 @@ static void dio_prep_bio(struct dio *dio) ...@@ -388,7 +388,7 @@ static void dio_prep_bio(struct dio *dio)
/* /*
* There is no bio. Make one now. * There is no bio. Make one now.
*/ */
static int dio_new_bio(struct dio *dio) static int dio_new_bio(struct dio *dio, sector_t blkno)
{ {
sector_t sector; sector_t sector;
int ret, nr_pages; int ret, nr_pages;
...@@ -396,7 +396,7 @@ static int dio_new_bio(struct dio *dio) ...@@ -396,7 +396,7 @@ static int dio_new_bio(struct dio *dio)
ret = dio_bio_reap(dio); ret = dio_bio_reap(dio);
if (ret) if (ret)
goto out; goto out;
sector = dio->next_block_in_bio << (dio->blkbits - 9); sector = blkno << (dio->blkbits - 9);
nr_pages = min(dio->pages_left, bio_get_nr_vecs(dio->map_bh.b_bdev)); nr_pages = min(dio->pages_left, bio_get_nr_vecs(dio->map_bh.b_bdev));
BUG_ON(nr_pages <= 0); BUG_ON(nr_pages <= 0);
ret = dio_bio_alloc(dio, dio->map_bh.b_bdev, sector, nr_pages); ret = dio_bio_alloc(dio, dio->map_bh.b_bdev, sector, nr_pages);
...@@ -408,23 +408,26 @@ static int dio_new_bio(struct dio *dio) ...@@ -408,23 +408,26 @@ static int dio_new_bio(struct dio *dio)
static int static int
dio_bio_add_page(struct dio *dio, struct page *page, dio_bio_add_page(struct dio *dio, struct page *page,
unsigned int bv_len, unsigned int bv_offset) unsigned int bv_len, unsigned int bv_offset, sector_t blkno)
{ {
int ret = 0; int ret = 0;
if (bv_len == 0) if (bv_len == 0)
goto out; goto out;
/* Take a ref against the page each time it is placed into a BIO */
page_cache_get(page); page_cache_get(page);
if (bio_add_page(dio->bio, page, bv_len, bv_offset)) { if (bio_add_page(dio->bio, page, bv_len, bv_offset)) {
dio_bio_submit(dio); dio_bio_submit(dio);
ret = dio_new_bio(dio); ret = dio_new_bio(dio, blkno);
if (ret == 0) { if (ret == 0) {
ret = bio_add_page(dio->bio, page, bv_len, bv_offset); ret = bio_add_page(dio->bio, page, bv_len, bv_offset);
BUG_ON(ret != 0); BUG_ON(ret != 0);
} else {
/* The page didn't make it into a BIO */
page_cache_release(page);
} }
} }
page_cache_release(page);
dio->pages_left--; dio->pages_left--;
out: out:
return ret; return ret;
...@@ -460,6 +463,7 @@ int do_direct_IO(struct dio *dio) ...@@ -460,6 +463,7 @@ int do_direct_IO(struct dio *dio)
int new_page; /* Need to insert this page into the BIO? */ int new_page; /* Need to insert this page into the BIO? */
unsigned int bv_offset; unsigned int bv_offset;
unsigned int bv_len; unsigned int bv_len;
sector_t curr_blkno;
page = dio_get_page(dio); page = dio_get_page(dio);
if (IS_ERR(page)) { if (IS_ERR(page)) {
...@@ -470,6 +474,7 @@ int do_direct_IO(struct dio *dio) ...@@ -470,6 +474,7 @@ int do_direct_IO(struct dio *dio)
new_page = 1; new_page = 1;
bv_offset = 0; bv_offset = 0;
bv_len = 0; bv_len = 0;
curr_blkno = 0;
while (block_in_page < blocks_per_page) { while (block_in_page < blocks_per_page) {
unsigned this_chunk_bytes; /* # of bytes mapped */ unsigned this_chunk_bytes; /* # of bytes mapped */
unsigned this_chunk_blocks; /* # of blocks */ unsigned this_chunk_blocks; /* # of blocks */
...@@ -477,7 +482,7 @@ int do_direct_IO(struct dio *dio) ...@@ -477,7 +482,7 @@ int do_direct_IO(struct dio *dio)
ret = get_more_blocks(dio); ret = get_more_blocks(dio);
if (ret) if (ret)
goto out; goto fail_release;
/* Handle holes */ /* Handle holes */
if (!buffer_mapped(&dio->map_bh)) { if (!buffer_mapped(&dio->map_bh)) {
...@@ -494,15 +499,16 @@ int do_direct_IO(struct dio *dio) ...@@ -494,15 +499,16 @@ int do_direct_IO(struct dio *dio)
dio_prep_bio(dio); dio_prep_bio(dio);
if (dio->bio == NULL) { if (dio->bio == NULL) {
ret = dio_new_bio(dio); ret = dio_new_bio(dio, dio->next_block_in_bio);
if (ret) if (ret)
goto out; goto fail_release;
new_page = 1; new_page = 1;
} }
if (new_page) { if (new_page) {
bv_len = 0; bv_len = 0;
bv_offset = block_in_page << blkbits; bv_offset = block_in_page << blkbits;
curr_blkno = dio->next_block_in_bio;
new_page = 0; new_page = 0;
} }
...@@ -530,11 +536,18 @@ int do_direct_IO(struct dio *dio) ...@@ -530,11 +536,18 @@ int do_direct_IO(struct dio *dio)
if (dio->block_in_file == dio->final_block_in_request) if (dio->block_in_file == dio->final_block_in_request)
break; break;
} }
ret = dio_bio_add_page(dio, page, bv_len, bv_offset); ret = dio_bio_add_page(dio, page, bv_len,
bv_offset, curr_blkno);
if (ret) if (ret)
goto out; goto fail_release;
/* Drop the ref which was taken in get_user_pages() */
page_cache_release(page);
block_in_page = 0; block_in_page = 0;
} }
goto out;
fail_release:
page_cache_release(page);
out: out:
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment