Thread (5 messages) 5 messages, 3 authors, 2017-09-21

Re: [PATCH] xfs: update i_size after unwritten conversion in dio completion

From: Eryu Guan <hidden>
Date: 2017-09-21 15:40:29

On Thu, Sep 21, 2017 at 07:33:08AM -0700, Christoph Hellwig wrote:
On Thu, Sep 21, 2017 at 06:38:28PM +0800, Eryu Guan wrote:
quoted
Since commit d531d91d6990 ("xfs: always use unwritten extents for
direct I/O writes"), we start allocating unwritten extents for all
direct writes to allow appending aio in XFS.

But for dio writes that could extend file size we update the in-core
inode size first, then convert the unwritten extents to real
allocations at dio completion time in xfs_dio_write_end_io(). Thus a
racing direct read could see the new i_size and find the unwritten
extents first and read zeros instead of actual data, if the direct
writer also takes a shared iolock.

Fix it by updating the in-core inode size after the unwritten extent
conversion. To do this, introduce a new boolean argument to
xfs_iomap_write_unwritten() to tell if we want to update in-core
i_size or not.

Suggested-by: Brian Foster <redacted>
Signed-off-by: Eryu Guan <redacted>
---

Patch passed the test posted by Eric[1] and a locally modified aio
version of the test.

I also ran fstests with config xfs_4k_crc, xfs_2k_reflink, xfs_1k_rmap
and xfs_512, and aio-dio tests from ltp, I don't see any new failures
introduced.

[1] http://www.spinics.net/lists/fstests/msg06978.html

 fs/xfs/xfs_aops.c  |  9 ++++++++-
 fs/xfs/xfs_file.c  | 34 ++++++++++++++++++++--------------
 fs/xfs/xfs_iomap.c |  7 ++++++-
 fs/xfs/xfs_iomap.h |  2 +-
 fs/xfs/xfs_pnfs.c  |  2 +-
 5 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 29172609f2a3..f937968e9515 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -343,7 +343,14 @@ xfs_end_io(
 		error = xfs_reflink_end_cow(ip, offset, size);
 		break;
 	case XFS_IO_UNWRITTEN:
-		error = xfs_iomap_write_unwritten(ip, offset, size);
+		/*
+		 * The correct in-core inode size should have been updated by
+		 * generic_write_end, and the 'size' here is buffer head
+		 * granularity size of the ioend, which could be larger than
+		 * the actual bytes written. So skip in-core i_size update in
+		 * xfs_iomap_write_unwritten()
+		 */
+		error = xfs_iomap_write_unwritten(ip, offset, size, false);
 		break;
 	default:
 		ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 350b6d43ba23..d4796c5a88fe 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -434,7 +434,6 @@ xfs_dio_write_end_io(
 	struct inode		*inode = file_inode(iocb->ki_filp);
 	struct xfs_inode	*ip = XFS_I(inode);
 	loff_t			offset = iocb->ki_pos;
-	bool			update_size = false;
 	int			error = 0;
 
 	trace_xfs_end_io_direct_write(ip, offset, size);
@@ -445,6 +444,22 @@ xfs_dio_write_end_io(
 	if (size <= 0)
 		return size;
 
+	if (flags & IOMAP_DIO_COW) {
+		error = xfs_reflink_end_cow(ip, offset, size);
+		if (error)
+			return error;
+	}
So this should be ok, but without digging in the details I wonder
if we have proper tests for COWing the last block.

e.g. something like

write data from bytes 0 to 4000 to file a
clone file a to file b
write bytes 4000 to 4008 on file b
I took a rough look at generic tests in 'clone' group, it seems that
generic/134 and generic/20[23] could cover this scenario.
quoted
@@ -900,6 +902,9 @@ xfs_iomap_write_unwritten(
 		if (i_size > offset + count)
 			i_size = offset + count;
 
+		if (update_isize && (i_size > i_size_read(inode)))
no need for the inner braces here.
OK, will fix in v2.

Thanks for reviewing!

Eryu
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help