From: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx> Most filesystems such as ext4 and btrfs fallback to buffered I/O in case direct write's fail. In case direct I/O is short, fallback to buffered write to complete the I/O. Make sure the data is on disk by performing a filemap_write_and_wait_range() and invalidating the pages in the range. For reads, call xfs_file_buffered_aio_read() in case of short I/O. Signed-off-by: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx> --- fs/xfs/xfs_file.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 4b8bdecc3863..786391228dea 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -31,6 +31,10 @@ #include <linux/fadvise.h> static const struct vm_operations_struct xfs_file_vm_ops; +STATIC ssize_t xfs_file_buffered_aio_write(struct kiocb *iocb, + struct iov_iter *from); +STATIC ssize_t xfs_file_buffered_aio_read(struct kiocb *iocb, + struct iov_iter *to); int xfs_update_prealloc_flags( @@ -169,6 +173,7 @@ xfs_file_dio_aio_read( struct xfs_inode *ip = XFS_I(file_inode(iocb->ki_filp)); size_t count = iov_iter_count(to); ssize_t ret; + ssize_t buffered_read = 0; trace_xfs_file_direct_read(ip, count, iocb->ki_pos); @@ -187,7 +192,13 @@ xfs_file_dio_aio_read( is_sync_kiocb(iocb)); xfs_iunlock(ip, XFS_IOLOCK_SHARED); - return ret; + if (ret < 0 || ret == count) + return ret; + + iocb->ki_flags &= ~IOCB_DIRECT; + buffered_read = xfs_file_buffered_aio_read(iocb, to); + + return ret + buffered_read; } static noinline ssize_t @@ -483,6 +494,9 @@ xfs_file_dio_aio_write( int iolock; size_t count = iov_iter_count(from); struct xfs_buftarg *target = xfs_inode_buftarg(ip); + loff_t offset, end; + ssize_t buffered_write = 0; + int err; /* DIO must be aligned to device logical sector size */ if ((iocb->ki_pos | count) & target->bt_logical_sectormask) @@ -552,12 +566,25 @@ xfs_file_dio_aio_write( out: xfs_iunlock(ip, iolock); - /* - * No fallback to buffered IO on errors for XFS, direct IO will either - * complete fully or fail. - */ - ASSERT(ret < 0 || ret == count); - return ret; + if (ret < 0 || ret == count) + return ret; + + /* Fallback to buffered write */ + + offset = iocb->ki_pos; + + buffered_write = xfs_file_buffered_aio_write(iocb, from); + if (buffered_write < 0) + return ret; + + end = offset + buffered_write - 1; + + err = filemap_write_and_wait_range(mapping, offset, end); + if (!err) + invalidate_mapping_pages(mapping, offset >> PAGE_SHIFT, + end >> PAGE_SHIFT); + + return ret + buffered_write; } static noinline ssize_t -- 2.25.0