--- block/ll_rw_blk.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/bio.c | 3 + fs/buffer.c | 3 + include/linux/bio.h | 1 4 files changed, 86 insertions(+) Index: linux-2.6/block/ll_rw_blk.c =================================================================== --- linux-2.6.orig/block/ll_rw_blk.c 2007-09-30 22:48:37.000000000 -0700 +++ linux-2.6/block/ll_rw_blk.c 2007-09-30 23:09:24.000000000 -0700 @@ -2923,6 +2923,78 @@ static void init_request_from_bio(struct req->start_time = jiffies; } + +static void blk_queue_de_virtualize(struct request_queue *q, + struct bio **bio_orig) +{ + struct page *page; + struct bio *bio = NULL; + int i, nr; + struct bio_vec *to, *from; + int segments; + + if (!(bio->bi_flags & BIO_VIRTUAL_COMPOUND)) + return; + + /* Calculate number of segments. */ + segments = 0; + bio_for_each_segment(from, *bio_orig, i) { + page = from->bv_page; + + if (!PageVcompound(page)) + segments++; + + segments += page_cache_pages(page); + } + if (seg == (*bio_orig)->bi_vcnt) + /* No need for any new pages */ + return; + + /* Linearize the pages */ + + bio = bio_alloc(GFP_NOIO, segments); + + nr = 0; + bio_for_each_segment(from, *bio_orig, i) { + page = from->bv_page; + + if (!PageVcompound(page)) { + /* Straight copy is possible */ + to = bio->bi_io_vec + nr++; + to->bv_page = from->bv_page; + to->bv_len = from->bv->len; + to->bv_offset = from->bv_offset; + continue; + } + + address = compound_address(page); + pages = page_cache_pages(page); + offset = from->bv_offset; + length = from->bv_len; + + for (i = offset >> PAGE_SHIFT; i < pages; i++) + to = bio->bi_io_vec + nr++; + to->bv_page = page_cache_nth(page, i); + to->bv_len = min(PAGE_SIZE, length); + to->bv_offset = offset; + offset = 0; + length -= PAGE_SIZE + } + } + + bio->bi_bdev = (*bio_orig)->bi_bdev; + bio->bi_sector = (*bio_orig)->bi_sector; + bio->bi_rw = (*bio_orig)->bi_rw; + + bio->bi_vcnt = nr; + bio->bi_idx = (*bio_orig)->bi_idx; + bio->bi_size = (*bio_orig)->bi_size; + + bio_put(*bio_orig); + *bio_orig = bio; +} + + static int __make_request(struct request_queue *q, struct bio *bio) { struct request *req; @@ -2934,6 +3006,13 @@ static int __make_request(struct request nr_sectors = bio_sectors(bio); /* + * If there are any virtual compounds on the bio then those must + * now be separated out into individual pages since there is + * no guarantee for physical linearity. + */ + blk_queue_de_virtualize(q, &bio); + + /* * low level driver can indicate that it wants pages above a * certain limit bounced to low memory (ie for highmem, or even * ISA dma in theory) Index: linux-2.6/fs/bio.c =================================================================== --- linux-2.6.orig/fs/bio.c 2007-09-30 22:40:22.000000000 -0700 +++ linux-2.6/fs/bio.c 2007-09-30 22:45:48.000000000 -0700 @@ -328,6 +328,9 @@ static int __bio_add_page(struct request if (unlikely(bio_flagged(bio, BIO_CLONED))) return 0; + if (PageHead(page) && compound_pagearray(page)) + bio->bi_flags |= BIO_VIRTUAL_COMPOUND; + if (((bio->bi_size + len) >> 9) > max_sectors) return 0; Index: linux-2.6/fs/buffer.c =================================================================== --- linux-2.6.orig/fs/buffer.c 2007-09-30 22:46:17.000000000 -0700 +++ linux-2.6/fs/buffer.c 2007-09-30 22:47:05.000000000 -0700 @@ -2689,6 +2689,9 @@ int submit_bh(int rw, struct buffer_head bio->bi_end_io = end_bio_bh_io_sync; bio->bi_private = bh; + if (PageVcompound(bh->bh_bpage)) + bio->bi_flags |= BIO_VIRTUAL_COMPOUND; + bio_get(bio); submit_bio(rw, bio); Index: linux-2.6/include/linux/bio.h =================================================================== --- linux-2.6.orig/include/linux/bio.h 2007-09-30 22:39:58.000000000 -0700 +++ linux-2.6/include/linux/bio.h 2007-09-30 22:40:18.000000000 -0700 @@ -127,6 +127,7 @@ struct bio { #define BIO_BOUNCED 5 /* bio is a bounce bio */ #define BIO_USER_MAPPED 6 /* contains user pages */ #define BIO_EOPNOTSUPP 7 /* not supported */ +#define BIO_VMALLOC 8 /* Bio has vmalloc'ed memory */ #define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag))) /*