Commit bd052817 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] 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.
parent 12feeeda
...@@ -168,7 +168,8 @@ static void figure_loop_size(struct loop_device *lo) ...@@ -168,7 +168,8 @@ static void figure_loop_size(struct loop_device *lo)
} }
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 file *file = lo->lo_backing_file; /* kudos to NFsckingS */
struct address_space *mapping = file->f_dentry->d_inode->i_mapping; struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
...@@ -178,12 +179,13 @@ static int lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t po ...@@ -178,12 +179,13 @@ static int lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t po
unsigned long index; unsigned long index;
unsigned size, offset; unsigned size, offset;
int len; int len;
int ret = 0;
down(&mapping->host->i_sem); down(&mapping->host->i_sem);
index = pos >> PAGE_CACHE_SHIFT; index = pos >> PAGE_CACHE_SHIFT;
offset = pos & (PAGE_CACHE_SIZE - 1); offset = pos & (PAGE_CACHE_SIZE - 1);
len = bio->bi_size; data = kmap(bvec->bv_page) + bvec->bv_offset;
data = bio_data(bio); len = bvec->bv_len;
while (len > 0) { while (len > 0) {
int IV = index * (PAGE_CACHE_SIZE/bsize) + offset/bsize; int IV = index * (PAGE_CACHE_SIZE/bsize) + offset/bsize;
int transfer_result; int transfer_result;
...@@ -221,14 +223,34 @@ static int lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t po ...@@ -221,14 +223,34 @@ static int lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t po
page_cache_release(page); page_cache_release(page);
} }
up(&mapping->host->i_sem); up(&mapping->host->i_sem);
return 0; out:
kunmap(bvec->bv_page);
return ret;
unlock: unlock:
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
fail: fail:
up(&mapping->host->i_sem); 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 { struct lo_read_data {
...@@ -262,26 +284,46 @@ static int lo_read_actor(read_descriptor_t * desc, struct page *page, unsigned l ...@@ -262,26 +284,46 @@ static int lo_read_actor(read_descriptor_t * desc, struct page *page, unsigned l
return size; 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; struct lo_read_data cookie;
read_descriptor_t desc; read_descriptor_t desc;
struct file *file; struct file *file;
cookie.lo = lo; cookie.lo = lo;
cookie.data = bio_data(bio); cookie.data = kmap(bvec->bv_page) + bvec->bv_offset;
cookie.bsize = bsize; cookie.bsize = bsize;
desc.written = 0; desc.written = 0;
desc.count = bio->bi_size; desc.count = bvec->bv_len;
desc.buf = (char*)&cookie; desc.buf = (char*)&cookie;
desc.error = 0; desc.error = 0;
spin_lock_irq(&lo->lo_lock); spin_lock_irq(&lo->lo_lock);
file = lo->lo_backing_file; file = lo->lo_backing_file;
spin_unlock_irq(&lo->lo_lock); spin_unlock_irq(&lo->lo_lock);
do_generic_file_read(file, &pos, &desc, lo_read_actor); do_generic_file_read(file, &pos, &desc, lo_read_actor);
kunmap(bvec->bv_page);
return desc.error; 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) static inline int loop_get_bs(struct loop_device *lo)
{ {
return block_size(lo->lo_device); return block_size(lo->lo_device);
......
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