summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@ZenIV.linux.org.uk>2017-09-11 21:17:09 +0100
committerAl Viro <viro@zeniv.linux.org.uk>2017-10-11 22:38:02 -0400
commitcfe057f7db1ff026c8db75469a3f9ba9736e1975 (patch)
tree95f92a36480525b3918b6e1a6d9b26d236160565
parent8ececffa12f5555171075ce52e1226f570836b26 (diff)
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: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/iomap.c24
1 files changed, 15 insertions, 9 deletions
diff --git a/fs/iomap.c b/fs/iomap.c
index be61cf742b5e..9c41008833ac 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -848,6 +848,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
struct bio *bio;
bool need_zeroout = false;
int nr_pages, ret;
+ size_t copied = 0;
if ((pos | length | align) & ((1 << blkbits) - 1))
return -EINVAL;
@@ -859,7 +860,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
/*FALLTHRU*/
case IOMAP_UNWRITTEN:
if (!(dio->flags & IOMAP_DIO_WRITE)) {
- iov_iter_zero(length, dio->submit.iter);
+ length = iov_iter_zero(length, dio->submit.iter);
dio->size += length;
return length;
}
@@ -896,8 +897,11 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
}
do {
- if (dio->error)
+ size_t n;
+ if (dio->error) {
+ iov_iter_revert(dio->submit.iter, copied);
return 0;
+ }
bio = bio_alloc(GFP_KERNEL, nr_pages);
bio_set_dev(bio, iomap->bdev);
@@ -910,20 +914,24 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
ret = bio_iov_iter_get_pages(bio, &iter);
if (unlikely(ret)) {
bio_put(bio);
- return ret;
+ return copied ? copied : ret;
}
+ n = bio->bi_iter.bi_size;
if (dio->flags & IOMAP_DIO_WRITE) {
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 {
bio_set_op_attrs(bio, REQ_OP_READ, 0);
if (dio->flags & IOMAP_DIO_DIRTY)
bio_set_pages_dirty(bio);
}
- dio->size += bio->bi_iter.bi_size;
- pos += bio->bi_iter.bi_size;
+ iov_iter_advance(dio->submit.iter, n);
+
+ dio->size += n;
+ pos += n;
+ copied += n;
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,
if (pad)
iomap_dio_zero(dio, iomap, pos, fs_block_size - pad);
}
-
- iov_iter_advance(dio->submit.iter, length);
- return length;
+ return copied;
}
ssize_t