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

From: Andrew Morton (akpm@zip.com.au)
Date: Sun May 26 2002 - 15:39:36 EST


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/



This archive was generated by hypermail 2b29 : Fri May 31 2002 - 22:00:18 EST