Commit cfe057f7 authored by Al Viro's avatar Al Viro Committed by Al Viro

iomap_dio_actor(): fix iov_iter bugs

1) Ignoring return value from iov_iter_zero() is wrong
for iovec-backed case as well as for pipes - it can fail.

2) Failure to fault destination pages in 25Mb into a 50Mb iovec
should not act as if nothing in the area had been read, nevermind
that the first 25Mb might have *already* been read by that point.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 8ececffa
...@@ -848,6 +848,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length, ...@@ -848,6 +848,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
struct bio *bio; struct bio *bio;
bool need_zeroout = false; bool need_zeroout = false;
int nr_pages, ret; int nr_pages, ret;
size_t copied = 0;
if ((pos | length | align) & ((1 << blkbits) - 1)) if ((pos | length | align) & ((1 << blkbits) - 1))
return -EINVAL; return -EINVAL;
...@@ -859,7 +860,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length, ...@@ -859,7 +860,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
/*FALLTHRU*/ /*FALLTHRU*/
case IOMAP_UNWRITTEN: case IOMAP_UNWRITTEN:
if (!(dio->flags & IOMAP_DIO_WRITE)) { if (!(dio->flags & IOMAP_DIO_WRITE)) {
iov_iter_zero(length, dio->submit.iter); length = iov_iter_zero(length, dio->submit.iter);
dio->size += length; dio->size += length;
return length; return length;
} }
...@@ -896,8 +897,11 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length, ...@@ -896,8 +897,11 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
} }
do { do {
if (dio->error) size_t n;
if (dio->error) {
iov_iter_revert(dio->submit.iter, copied);
return 0; return 0;
}
bio = bio_alloc(GFP_KERNEL, nr_pages); bio = bio_alloc(GFP_KERNEL, nr_pages);
bio_set_dev(bio, iomap->bdev); bio_set_dev(bio, iomap->bdev);
...@@ -910,20 +914,24 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length, ...@@ -910,20 +914,24 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
ret = bio_iov_iter_get_pages(bio, &iter); ret = bio_iov_iter_get_pages(bio, &iter);
if (unlikely(ret)) { if (unlikely(ret)) {
bio_put(bio); bio_put(bio);
return ret; return copied ? copied : ret;
} }
n = bio->bi_iter.bi_size;
if (dio->flags & IOMAP_DIO_WRITE) { if (dio->flags & IOMAP_DIO_WRITE) {
bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_SYNC | REQ_IDLE); bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_SYNC | REQ_IDLE);
task_io_account_write(bio->bi_iter.bi_size); task_io_account_write(n);
} else { } else {
bio_set_op_attrs(bio, REQ_OP_READ, 0); bio_set_op_attrs(bio, REQ_OP_READ, 0);
if (dio->flags & IOMAP_DIO_DIRTY) if (dio->flags & IOMAP_DIO_DIRTY)
bio_set_pages_dirty(bio); bio_set_pages_dirty(bio);
} }
dio->size += bio->bi_iter.bi_size; iov_iter_advance(dio->submit.iter, n);
pos += bio->bi_iter.bi_size;
dio->size += n;
pos += n;
copied += n;
nr_pages = iov_iter_npages(&iter, BIO_MAX_PAGES); nr_pages = iov_iter_npages(&iter, BIO_MAX_PAGES);
...@@ -939,9 +947,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length, ...@@ -939,9 +947,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
if (pad) if (pad)
iomap_dio_zero(dio, iomap, pos, fs_block_size - pad); iomap_dio_zero(dio, iomap, pos, fs_block_size - pad);
} }
return copied;
iov_iter_advance(dio->submit.iter, length);
return length;
} }
ssize_t ssize_t
......
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