Commit aa4d8616 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Al Viro

block: loop: switch to VFS ITER_BVEC

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 6683de38
...@@ -88,28 +88,6 @@ static int part_shift; ...@@ -88,28 +88,6 @@ static int part_shift;
static struct workqueue_struct *loop_wq; static struct workqueue_struct *loop_wq;
/*
* Transfer functions
*/
static int transfer_none(struct loop_device *lo, int cmd,
struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t real_block)
{
char *raw_buf = kmap_atomic(raw_page) + raw_off;
char *loop_buf = kmap_atomic(loop_page) + loop_off;
if (cmd == READ)
memcpy(loop_buf, raw_buf, size);
else
memcpy(raw_buf, loop_buf, size);
kunmap_atomic(loop_buf);
kunmap_atomic(raw_buf);
cond_resched();
return 0;
}
static int transfer_xor(struct loop_device *lo, int cmd, static int transfer_xor(struct loop_device *lo, int cmd,
struct page *raw_page, unsigned raw_off, struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off, struct page *loop_page, unsigned loop_off,
...@@ -148,14 +126,13 @@ static int xor_init(struct loop_device *lo, const struct loop_info64 *info) ...@@ -148,14 +126,13 @@ static int xor_init(struct loop_device *lo, const struct loop_info64 *info)
static struct loop_func_table none_funcs = { static struct loop_func_table none_funcs = {
.number = LO_CRYPT_NONE, .number = LO_CRYPT_NONE,
.transfer = transfer_none, };
};
static struct loop_func_table xor_funcs = { static struct loop_func_table xor_funcs = {
.number = LO_CRYPT_XOR, .number = LO_CRYPT_XOR,
.transfer = transfer_xor, .transfer = transfer_xor,
.init = xor_init .init = xor_init
}; };
/* xfer_funcs[0] is special - its release function is never called */ /* xfer_funcs[0] is special - its release function is never called */
static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = { static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = {
...@@ -215,207 +192,169 @@ lo_do_transfer(struct loop_device *lo, int cmd, ...@@ -215,207 +192,169 @@ lo_do_transfer(struct loop_device *lo, int cmd,
struct page *lpage, unsigned loffs, struct page *lpage, unsigned loffs,
int size, sector_t rblock) int size, sector_t rblock)
{ {
if (unlikely(!lo->transfer)) int ret;
ret = lo->transfer(lo, cmd, rpage, roffs, lpage, loffs, size, rblock);
if (likely(!ret))
return 0; return 0;
return lo->transfer(lo, cmd, rpage, roffs, lpage, loffs, size, rblock); printk_ratelimited(KERN_ERR
"loop: Transfer error at byte offset %llu, length %i.\n",
(unsigned long long)rblock << 9, size);
return ret;
} }
/** static int lo_write_bvec(struct file *file, struct bio_vec *bvec, loff_t *ppos)
* __do_lo_send_write - helper for writing data to a loop device
*
* This helper just factors out common code between do_lo_send_direct_write()
* and do_lo_send_write().
*/
static int __do_lo_send_write(struct file *file,
u8 *buf, const int len, loff_t pos)
{ {
struct kvec kvec = {.iov_base = buf, .iov_len = len}; struct iov_iter i;
struct iov_iter from;
ssize_t bw; ssize_t bw;
iov_iter_kvec(&from, ITER_KVEC | WRITE, &kvec, 1, len); iov_iter_bvec(&i, ITER_BVEC, bvec, 1, bvec->bv_len);
file_start_write(file); file_start_write(file);
bw = vfs_iter_write(file, &from, &pos); bw = vfs_iter_write(file, &i, ppos);
file_end_write(file); file_end_write(file);
if (likely(bw == len))
if (likely(bw == bvec->bv_len))
return 0; return 0;
printk_ratelimited(KERN_ERR "loop: Write error at byte offset %llu, length %i.\n",
(unsigned long long)pos, len); printk_ratelimited(KERN_ERR
"loop: Write error at byte offset %llu, length %i.\n",
(unsigned long long)*ppos, bvec->bv_len);
if (bw >= 0) if (bw >= 0)
bw = -EIO; bw = -EIO;
return bw; return bw;
} }
/** static int lo_write_simple(struct loop_device *lo, struct request *rq,
* do_lo_send_direct_write - helper for writing data to a loop device loff_t pos)
*
* This is the fast, non-transforming version that does not need double
* buffering.
*/
static int do_lo_send_direct_write(struct loop_device *lo,
struct bio_vec *bvec, loff_t pos, struct page *page)
{ {
ssize_t bw = __do_lo_send_write(lo->lo_backing_file, struct bio_vec bvec;
kmap(bvec->bv_page) + bvec->bv_offset, struct req_iterator iter;
bvec->bv_len, pos); int ret = 0;
kunmap(bvec->bv_page);
cond_resched(); rq_for_each_segment(bvec, rq, iter) {
return bw; ret = lo_write_bvec(lo->lo_backing_file, &bvec, &pos);
if (ret < 0)
break;
cond_resched();
}
return ret;
} }
/** /*
* do_lo_send_write - helper for writing data to a loop device
*
* This is the slow, transforming version that needs to double buffer the * This is the slow, transforming version that needs to double buffer the
* data as it cannot do the transformations in place without having direct * data as it cannot do the transformations in place without having direct
* access to the destination pages of the backing file. * access to the destination pages of the backing file.
*/ */
static int do_lo_send_write(struct loop_device *lo, struct bio_vec *bvec, static int lo_write_transfer(struct loop_device *lo, struct request *rq,
loff_t pos, struct page *page) loff_t pos)
{ {
int ret = lo_do_transfer(lo, WRITE, page, 0, bvec->bv_page, struct bio_vec bvec, b;
bvec->bv_offset, bvec->bv_len, pos >> 9);
if (likely(!ret))
return __do_lo_send_write(lo->lo_backing_file,
page_address(page), bvec->bv_len,
pos);
printk_ratelimited(KERN_ERR "loop: Transfer error at byte offset %llu, "
"length %i.\n", (unsigned long long)pos, bvec->bv_len);
if (ret > 0)
ret = -EIO;
return ret;
}
static int lo_send(struct loop_device *lo, struct request *rq, loff_t pos)
{
int (*do_lo_send)(struct loop_device *, struct bio_vec *, loff_t,
struct page *page);
struct bio_vec bvec;
struct req_iterator iter; struct req_iterator iter;
struct page *page = NULL; struct page *page;
int ret = 0; int ret = 0;
if (lo->transfer != transfer_none) { page = alloc_page(GFP_NOIO);
page = alloc_page(GFP_NOIO | __GFP_HIGHMEM); if (unlikely(!page))
if (unlikely(!page)) return -ENOMEM;
goto fail;
kmap(page);
do_lo_send = do_lo_send_write;
} else {
do_lo_send = do_lo_send_direct_write;
}
rq_for_each_segment(bvec, rq, iter) { rq_for_each_segment(bvec, rq, iter) {
ret = do_lo_send(lo, &bvec, pos, page); ret = lo_do_transfer(lo, WRITE, page, 0, bvec.bv_page,
bvec.bv_offset, bvec.bv_len, pos >> 9);
if (unlikely(ret))
break;
b.bv_page = page;
b.bv_offset = 0;
b.bv_len = bvec.bv_len;
ret = lo_write_bvec(lo->lo_backing_file, &b, &pos);
if (ret < 0) if (ret < 0)
break; break;
pos += bvec.bv_len;
} }
if (page) {
kunmap(page); __free_page(page);
__free_page(page);
}
out:
return ret; return ret;
fail:
printk_ratelimited(KERN_ERR "loop: Failed to allocate temporary page for write.\n");
ret = -ENOMEM;
goto out;
} }
struct lo_read_data { static int lo_read_simple(struct loop_device *lo, struct request *rq,
struct loop_device *lo; loff_t pos)
struct page *page; {
unsigned offset; struct bio_vec bvec;
int bsize; struct req_iterator iter;
}; struct iov_iter i;
ssize_t len;
static int rq_for_each_segment(bvec, rq, iter) {
lo_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, iov_iter_bvec(&i, ITER_BVEC, &bvec, 1, bvec.bv_len);
struct splice_desc *sd) len = vfs_iter_read(lo->lo_backing_file, &i, &pos);
{ if (len < 0)
struct lo_read_data *p = sd->u.data; return len;
struct loop_device *lo = p->lo;
struct page *page = buf->page;
sector_t IV;
int size;
IV = ((sector_t) page->index << (PAGE_CACHE_SHIFT - 9)) +
(buf->offset >> 9);
size = sd->len;
if (size > p->bsize)
size = p->bsize;
if (lo_do_transfer(lo, READ, page, buf->offset, p->page, p->offset, size, IV)) {
printk_ratelimited(KERN_ERR "loop: transfer error block %ld\n",
page->index);
size = -EINVAL;
}
flush_dcache_page(p->page); flush_dcache_page(bvec.bv_page);
if (size > 0) if (len != bvec.bv_len) {
p->offset += size; struct bio *bio;
return size; __rq_for_each_bio(bio, rq)
} zero_fill_bio(bio);
break;
}
cond_resched();
}
static int return 0;
lo_direct_splice_actor(struct pipe_inode_info *pipe, struct splice_desc *sd)
{
return __splice_from_pipe(pipe, sd, lo_splice_actor);
} }
static ssize_t static int lo_read_transfer(struct loop_device *lo, struct request *rq,
do_lo_receive(struct loop_device *lo, loff_t pos)
struct bio_vec *bvec, int bsize, loff_t pos)
{ {
struct lo_read_data cookie; struct bio_vec bvec, b;
struct splice_desc sd; struct req_iterator iter;
struct file *file; struct iov_iter i;
ssize_t retval; struct page *page;
ssize_t len;
int ret = 0;
cookie.lo = lo; page = alloc_page(GFP_NOIO);
cookie.page = bvec->bv_page; if (unlikely(!page))
cookie.offset = bvec->bv_offset; return -ENOMEM;
cookie.bsize = bsize;
sd.len = 0; rq_for_each_segment(bvec, rq, iter) {
sd.total_len = bvec->bv_len; loff_t offset = pos;
sd.flags = 0;
sd.pos = pos;
sd.u.data = &cookie;
file = lo->lo_backing_file; b.bv_page = page;
retval = splice_direct_to_actor(file, &sd, lo_direct_splice_actor); b.bv_offset = 0;
b.bv_len = bvec.bv_len;
return retval; iov_iter_bvec(&i, ITER_BVEC, &b, 1, b.bv_len);
} len = vfs_iter_read(lo->lo_backing_file, &i, &pos);
if (len < 0) {
ret = len;
goto out_free_page;
}
static int ret = lo_do_transfer(lo, READ, page, 0, bvec.bv_page,
lo_receive(struct loop_device *lo, struct request *rq, int bsize, loff_t pos) bvec.bv_offset, len, offset >> 9);
{ if (ret)
struct bio_vec bvec; goto out_free_page;
struct req_iterator iter;
ssize_t s;
rq_for_each_segment(bvec, rq, iter) { flush_dcache_page(bvec.bv_page);
s = do_lo_receive(lo, &bvec, bsize, pos);
if (s < 0)
return s;
if (s != bvec.bv_len) { if (len != bvec.bv_len) {
struct bio *bio; struct bio *bio;
__rq_for_each_bio(bio, rq) __rq_for_each_bio(bio, rq)
zero_fill_bio(bio); zero_fill_bio(bio);
break; break;
} }
pos += bvec.bv_len;
} }
return 0;
ret = 0;
out_free_page:
__free_page(page);
return ret;
} }
static int lo_discard(struct loop_device *lo, struct request *rq, loff_t pos) static int lo_discard(struct loop_device *lo, struct request *rq, loff_t pos)
...@@ -464,10 +403,17 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq) ...@@ -464,10 +403,17 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq)
ret = lo_req_flush(lo, rq); ret = lo_req_flush(lo, rq);
else if (rq->cmd_flags & REQ_DISCARD) else if (rq->cmd_flags & REQ_DISCARD)
ret = lo_discard(lo, rq, pos); ret = lo_discard(lo, rq, pos);
else if (lo->transfer)
ret = lo_write_transfer(lo, rq, pos);
else else
ret = lo_send(lo, rq, pos); ret = lo_write_simple(lo, rq, pos);
} else
ret = lo_receive(lo, rq, lo->lo_blocksize, pos); } else {
if (lo->transfer)
ret = lo_read_transfer(lo, rq, pos);
else
ret = lo_read_simple(lo, rq, pos);
}
return ret; return ret;
} }
...@@ -788,7 +734,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, ...@@ -788,7 +734,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
lo->lo_device = bdev; lo->lo_device = bdev;
lo->lo_flags = lo_flags; lo->lo_flags = lo_flags;
lo->lo_backing_file = file; lo->lo_backing_file = file;
lo->transfer = transfer_none; lo->transfer = NULL;
lo->ioctl = NULL; lo->ioctl = NULL;
lo->lo_sizelimit = 0; lo->lo_sizelimit = 0;
lo->old_gfp_mask = mapping_gfp_mask(mapping); lo->old_gfp_mask = mapping_gfp_mask(mapping);
...@@ -1007,7 +953,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) ...@@ -1007,7 +953,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
memcpy(lo->lo_encrypt_key, info->lo_encrypt_key, memcpy(lo->lo_encrypt_key, info->lo_encrypt_key,
info->lo_encrypt_key_size); info->lo_encrypt_key_size);
lo->lo_key_owner = uid; lo->lo_key_owner = uid;
} }
return 0; return 0;
} }
......
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