From: "Matthew Wilcox (Oracle)" willy@infradead.org
mainline inclusion from mainline-v5.10 commit 14284fedf59f1647264f4603d64418cf1fcd3eb0 category: bugfix bugzilla: 43547 CVE: NA
-----------------------------------------------
When bringing (portions of) a page uptodate, we were marking blocks that were zeroed as being uptodate, but not blocks that were read from storage.
Like the previous commit, this problem was found with generic/127 and a kernel which failed readahead I/Os. This bug causes writes to be silently lost when working with flaky storage.
Fixes: 9dc55f1389f9 ("iomap: add support for sub-pagesize buffered I/O without buffer heads") Signed-off-by: Matthew Wilcox (Oracle) willy@infradead.org Reviewed-by: Darrick J. Wong darrick.wong@oracle.com Signed-off-by: Darrick J. Wong darrick.wong@oracle.com Reviewed-by: Christoph Hellwig hch@lst.de
conflicts: fs/iomap.c
Signed-off-by: Ye Bin yebin10@huawei.com Reviewed-by: Zhang Yi yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/iomap.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/fs/iomap.c b/fs/iomap.c index 57164400077d3..7078add7bbf99 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -649,7 +649,6 @@ __iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, loff_t block_start = pos & ~(block_size - 1); loff_t block_end = (pos + len + block_size - 1) & ~(block_size - 1); unsigned from = offset_in_page(pos), to = from + len, poff, plen; - int status = 0;
if (PageUptodate(page)) return 0; @@ -668,14 +667,14 @@ __iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, if (iomap->type != IOMAP_MAPPED || block_start >= i_size_read(inode)) { zero_user_segments(page, poff, from, to, poff + plen); - iomap_set_range_uptodate(page, poff, plen); - continue; + } else { + int status = iomap_read_page_sync(block_start, page, + poff, plen, iomap); + if (status) + return status; } - status = iomap_read_page_sync(block_start, page, poff, plen, - iomap); - if (status) - return status;
+ iomap_set_range_uptodate(page, poff, plen); } while ((block_start += plen) < block_end);
return 0;