fix loop driver for large BIOs
PATCH 4/18 fix loop driver for large BIOs
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.
=====================================
drivers/block/loop.c renamed from drivers/block/loop.c~loop-large-bio +50 -8
@@ -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 *lunsigned 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 *lpage_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_descriptorreturn 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);
-