[patch 4/18] fix loop driver for large BIOs

Andrew Morton (akpm@zip.com.au)
Sun, 26 May 2002 13:39:36 -0700


Fix bug in the loop driver.

When presented with a multipage BIO, loop is overindexing the first
page in the BIO rather than advancing to the second page. It scribbles
on the backing file and/or on kernel memory.

This happens with multipage BIO-based pagecache I/O and presumably with
O_DIRECT also.

The fix is much-needed with the multipage-BIO patches - using that code
on loop-backed filesystems has rather messy results.

=====================================

--- 2.5.18/drivers/block/loop.c~loop-large-bio Sat May 25 23:26:45 2002
+++ 2.5.18-akpm/drivers/block/loop.c Sat May 25 23:26:45 2002
@@ -168,7 +168,8 @@ static void figure_loop_size(struct loop

}

-static int lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos)
+static int
+do_lo_send(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos)
{
struct file *file = lo->lo_backing_file; /* kudos to NFsckingS */
struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
@@ -178,12 +179,13 @@ static int lo_send(struct loop_device *l
unsigned long index;
unsigned size, offset;
int len;
+ int ret = 0;

down(&mapping->host->i_sem);
index = pos >> PAGE_CACHE_SHIFT;
offset = pos & (PAGE_CACHE_SIZE - 1);
- len = bio->bi_size;
- data = bio_data(bio);
+ data = kmap(bvec->bv_page) + bvec->bv_offset;
+ len = bvec->bv_len;
while (len > 0) {
int IV = index * (PAGE_CACHE_SIZE/bsize) + offset/bsize;
int transfer_result;
@@ -221,14 +223,34 @@ static int lo_send(struct loop_device *l
page_cache_release(page);
}
up(&mapping->host->i_sem);
- return 0;
+out:
+ kunmap(bvec->bv_page);
+ return ret;

unlock:
unlock_page(page);
page_cache_release(page);
fail:
up(&mapping->host->i_sem);
- return -1;
+ ret = -1;
+ goto out;
+}
+
+static int
+lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos)
+{
+ unsigned vecnr;
+ int ret = 0;
+
+ for (vecnr = 0; vecnr < bio->bi_vcnt; vecnr++) {
+ struct bio_vec *bvec = &bio->bi_io_vec[vecnr];
+
+ ret = do_lo_send(lo, bvec, bsize, pos);
+ if (ret < 0)
+ break;
+ pos += bvec->bv_len;
+ }
+ return ret;
}

struct lo_read_data {
@@ -262,26 +284,46 @@ static int lo_read_actor(read_descriptor
return size;
}

-static int lo_receive(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos)
+static int
+do_lo_receive(struct loop_device *lo,
+ struct bio_vec *bvec, int bsize, loff_t pos)
{
struct lo_read_data cookie;
read_descriptor_t desc;
struct file *file;

cookie.lo = lo;
- cookie.data = bio_data(bio);
+ cookie.data = kmap(bvec->bv_page) + bvec->bv_offset;
cookie.bsize = bsize;
desc.written = 0;
- desc.count = bio->bi_size;
+ desc.count = bvec->bv_len;
desc.buf = (char*)&cookie;
desc.error = 0;
spin_lock_irq(&lo->lo_lock);
file = lo->lo_backing_file;
spin_unlock_irq(&lo->lo_lock);
do_generic_file_read(file, &pos, &desc, lo_read_actor);
+ kunmap(bvec->bv_page);
return desc.error;
}

+static int
+lo_receive(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos)
+{
+ unsigned vecnr;
+ int ret = 0;
+
+ for (vecnr = 0; vecnr < bio->bi_vcnt; vecnr++) {
+ struct bio_vec *bvec = &bio->bi_io_vec[vecnr];
+
+ ret = do_lo_receive(lo, bvec, bsize, pos);
+ if (ret < 0)
+ break;
+ pos += bvec->bv_len;
+ }
+ return ret;
+}
+
static inline int loop_get_bs(struct loop_device *lo)
{
return block_size(lo->lo_device);

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