aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@ZenIV.linux.org.uk>2017-09-11 16:17:09 -0400
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,
848 struct bio *bio; 848 struct bio *bio;
849 bool need_zeroout = false; 849 bool need_zeroout = false;
850 int nr_pages, ret; 850 int nr_pages, ret;
851 size_t copied = 0;
851 852
852 if ((pos | length | align) & ((1 << blkbits) - 1)) 853 if ((pos | length | align) & ((1 << blkbits) - 1))
853 return -EINVAL; 854 return -EINVAL;
@@ -859,7 +860,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
859 /*FALLTHRU*/ 860 /*FALLTHRU*/
860 case IOMAP_UNWRITTEN: 861 case IOMAP_UNWRITTEN:
861 if (!(dio->flags & IOMAP_DIO_WRITE)) { 862 if (!(dio->flags & IOMAP_DIO_WRITE)) {
862 iov_iter_zero(length, dio->submit.iter); 863 length = iov_iter_zero(length, dio->submit.iter);
863 dio->size += length; 864 dio->size += length;
864 return length; 865 return length;
865 } 866 }
@@ -896,8 +897,11 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
896 } 897 }
897 898
898 do { 899 do {
899 if (dio->error) 900 size_t n;
901 if (dio->error) {
902 iov_iter_revert(dio->submit.iter, copied);
900 return 0; 903 return 0;
904 }
901 905
902 bio = bio_alloc(GFP_KERNEL, nr_pages); 906 bio = bio_alloc(GFP_KERNEL, nr_pages);
903 bio_set_dev(bio, iomap->bdev); 907 bio_set_dev(bio, iomap->bdev);
@@ -910,20 +914,24 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
910 ret = bio_iov_iter_get_pages(bio, &iter); 914 ret = bio_iov_iter_get_pages(bio, &iter);
911 if (unlikely(ret)) { 915 if (unlikely(ret)) {
912 bio_put(bio); 916 bio_put(bio);
913 return ret; 917 return copied ? copied : ret;
914 } 918 }
915 919
920 n = bio->bi_iter.bi_size;
916 if (dio->flags & IOMAP_DIO_WRITE) { 921 if (dio->flags & IOMAP_DIO_WRITE) {
917 bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_SYNC | REQ_IDLE); 922 bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_SYNC | REQ_IDLE);
918 task_io_account_write(bio->bi_iter.bi_size); 923 task_io_account_write(n);
919 } else { 924 } else {
920 bio_set_op_attrs(bio, REQ_OP_READ, 0); 925 bio_set_op_attrs(bio, REQ_OP_READ, 0);
921 if (dio->flags & IOMAP_DIO_DIRTY) 926 if (dio->flags & IOMAP_DIO_DIRTY)
922 bio_set_pages_dirty(bio); 927 bio_set_pages_dirty(bio);
923 } 928 }
924 929
925 dio->size += bio->bi_iter.bi_size; 930 iov_iter_advance(dio->submit.iter, n);
926 pos += bio->bi_iter.bi_size; 931
932 dio->size += n;
933 pos += n;
934 copied += n;
927 935
928 nr_pages = iov_iter_npages(&iter, BIO_MAX_PAGES); 936 nr_pages = iov_iter_npages(&iter, BIO_MAX_PAGES);
929 937
@@ -939,9 +947,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
939 if (pad) 947 if (pad)
940 iomap_dio_zero(dio, iomap, pos, fs_block_size - pad); 948 iomap_dio_zero(dio, iomap, pos, fs_block_size - pad);
941 } 949 }
942 950 return copied;
943 iov_iter_advance(dio->submit.iter, length);
944 return length;
945} 951}
946 952
947ssize_t 953ssize_t