[PATCH 05/12] gfs2: fix O_SYNC write handling

From: Christoph Hellwig
Date: Tue Jan 14 2020 - 11:12:45 EST


Don't ignore the return value from generic_write_sync for the direct to
buffered I/O callback case when written is non-zero. Also don't bother
to call generic_write_sync for the pure direct I/O case, as iomap_dio_rw
already takes care of that.

Signed-off-by: Christoph Hellwig <hch@xxxxxx>
---
fs/gfs2/file.c | 51 +++++++++++++++++++++++++-------------------------
1 file changed, 25 insertions(+), 26 deletions(-)

diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 21d032c4b077..86c0e61407b6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -847,7 +847,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct gfs2_inode *ip = GFS2_I(inode);
- ssize_t written = 0, ret;
+ ssize_t ret = 0;

ret = gfs2_rsqa_alloc(ip);
if (ret)
@@ -882,52 +882,51 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
loff_t pos, endbyte;
ssize_t buffered;

- written = gfs2_file_direct_write(iocb, from);
- if (written < 0 || !iov_iter_count(from))
+ ret = gfs2_file_direct_write(iocb, from);
+ if (ret < 0 || !iov_iter_count(from))
goto out_unlock;

current->backing_dev_info = inode_to_bdi(inode);
- ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops);
+ buffered = iomap_file_buffered_write(iocb, from,
+ &gfs2_iomap_ops);
current->backing_dev_info = NULL;
- if (unlikely(ret < 0))
+ if (unlikely(buffered <= 0)) {
+ if (buffered < 0)
+ ret = buffered;
goto out_unlock;
- buffered = ret;
+ }

/*
* We need to ensure that the page cache pages are written to
* disk and invalidated to preserve the expected O_DIRECT
- * semantics.
+ * semantics. If the writeback or invalidate fails only report
+ * the direct I/O range as we don't know if the buffered pages
+ * made it to disk.
*/
pos = iocb->ki_pos;
endbyte = pos + buffered - 1;
ret = filemap_write_and_wait_range(mapping, pos, endbyte);
- if (!ret) {
- iocb->ki_pos += buffered;
- written += buffered;
- invalidate_mapping_pages(mapping,
- pos >> PAGE_SHIFT,
- endbyte >> PAGE_SHIFT);
- } else {
- /*
- * We don't know how much we wrote, so just return
- * the number of bytes which were direct-written
- */
- }
+ if (ret)
+ goto out_unlock;
+
+ invalidate_mapping_pages(mapping, pos >> PAGE_SHIFT,
+ endbyte >> PAGE_SHIFT);
+ ret += buffered;
} else {
current->backing_dev_info = inode_to_bdi(inode);
ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops);
current->backing_dev_info = NULL;
- if (likely(ret > 0))
- iocb->ki_pos += ret;
+ if (unlikely(ret <= 0))
+ goto out_unlock;
}

+ iocb->ki_pos += ret;
+ inode_unlock(inode);
+ return generic_write_sync(iocb, ret);
+
out_unlock:
inode_unlock(inode);
- if (likely(ret > 0)) {
- /* Handle various SYNC-type writes */
- ret = generic_write_sync(iocb, ret);
- }
- return written ? written : ret;
+ return ret;
}

static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
--
2.24.1