Message ID | 20230321032024.165992-1-joseph.qi@linux.alibaba.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | ocfs2: fix data corruption after failed write | expand |
On Tue, Mar 21, 2023 at 11:20:24AM +0800, Joseph Qi wrote: > From: Jan Kara via Ocfs2-devel <ocfs2-devel@oss.oracle.com> > > commit 90410bcf873cf05f54a32183afff0161f44f9715 upstream. > > When buffered write fails to copy data into underlying page cache page, > ocfs2_write_end_nolock() just zeroes out and dirties the page. This can > leave dirty page beyond EOF and if page writeback tries to write this page > before write succeeds and expands i_size, page gets into inconsistent > state where page dirty bit is clear but buffer dirty bits stay set > resulting in page data never getting written and so data copied to the > page is lost. Fix the problem by invalidating page beyond EOF after > failed write. > > Link: https://lkml.kernel.org/r/20230302153843.18499-1-jack@suse.cz > Fixes: 6dbf7bb55598 ("fs: Don't invalidate page buffers in block_write_full_page()") > Signed-off-by: Jan Kara <jack@suse.cz> > Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> > Cc: Mark Fasheh <mark@fasheh.com> > Cc: Joel Becker <jlbec@evilplan.org> > Cc: Junxiao Bi <junxiao.bi@oracle.com> > Cc: Changwei Ge <gechangwei@live.cn> > Cc: Gang He <ghe@suse.com> > Cc: Jun Piao <piaojun@huawei.com> > Cc: <stable@vger.kernel.org> > Signed-off-by: Andrew Morton <akpm@linux-foundation.org> > [ replace block_invalidate_folio to block_invalidatepage ] > Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> > --- > fs/ocfs2/aops.c | 18 ++++++++++++++++-- > 1 file changed, 16 insertions(+), 2 deletions(-) > Now queued up, thanks. greg k-h
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index b6948813eb06..1353db3f7f48 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -2003,11 +2003,25 @@ int ocfs2_write_end_nolock(struct address_space *mapping, } if (unlikely(copied < len) && wc->w_target_page) { + loff_t new_isize; + if (!PageUptodate(wc->w_target_page)) copied = 0; - ocfs2_zero_new_buffers(wc->w_target_page, start+copied, - start+len); + new_isize = max_t(loff_t, i_size_read(inode), pos + copied); + if (new_isize > page_offset(wc->w_target_page)) + ocfs2_zero_new_buffers(wc->w_target_page, start+copied, + start+len); + else { + /* + * When page is fully beyond new isize (data copy + * failed), do not bother zeroing the page. Invalidate + * it instead so that writeback does not get confused + * put page & buffer dirty bits into inconsistent + * state. + */ + block_invalidatepage(wc->w_target_page, 0, PAGE_SIZE); + } } if (wc->w_target_page) flush_dcache_page(wc->w_target_page);