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>
This commit is contained in:
Родитель
8ececffa12
Коммит
cfe057f7db
24
fs/iomap.c
24
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче