From 2a25e2e8de0b61206d22ab5e81ad1fc619a28808 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 17 Feb 2020 13:54:51 -0800 Subject: [PATCH] iomap: refactor dio ->end_io handling Pass an (opaque) iomap_dio argument to the ->end_io handler an perform the common end I/O action in a new helper that needs be called from ->end_io if implemented. This will allow to move unlocking of i_rwsem or other lock schemes like the proposed range locks from the ->end_io handler. Signed-off-by: Christoph Hellwig --- fs/ext4/file.c | 16 +++++--------- fs/iomap/direct-io.c | 51 ++++++++++++++++++++++++------------------- fs/xfs/xfs_file.c | 14 +++++++----- fs/zonefs/super.c | 14 +++++++----- include/linux/iomap.h | 6 +++-- 5 files changed, 54 insertions(+), 47 deletions(-) diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 5f225881176b..3dc210db3386 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -361,20 +361,16 @@ truncate: return written; } -static int ext4_dio_write_end_io(struct kiocb *iocb, ssize_t size, - int error, unsigned int flags) +static ssize_t ext4_dio_write_end_io(struct kiocb *iocb, struct iomap_dio *dio, + ssize_t size, int error, unsigned int flags) { loff_t offset = iocb->ki_pos; struct inode *inode = file_inode(iocb->ki_filp); + ssize_t ret = 0; - if (error) - return error; - - if (size && flags & IOMAP_DIO_UNWRITTEN) - return ext4_convert_unwritten_extents(NULL, inode, - offset, size); - - return 0; + if (!error && size && (flags & IOMAP_DIO_UNWRITTEN)) + ret = ext4_convert_unwritten_extents(NULL, inode, offset, size); + return iomap_dio_end_io(dio, ret); } static const struct iomap_dio_ops ext4_dio_write_ops = { diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 23837926c0c5..945dc48e9bdf 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -70,25 +70,20 @@ static void iomap_dio_submit_bio(struct iomap_dio *dio, struct iomap *iomap, dio->submit.cookie = submit_bio(bio); } -static ssize_t iomap_dio_complete(struct iomap_dio *dio) +ssize_t iomap_dio_end_io(struct iomap_dio *dio, int error) { - const struct iomap_dio_ops *dops = dio->dops; struct kiocb *iocb = dio->iocb; struct inode *inode = file_inode(iocb->ki_filp); loff_t offset = iocb->ki_pos; - ssize_t ret = dio->error; + ssize_t ret = dio->size; - if (dops && dops->end_io) - ret = dops->end_io(iocb, dio->size, ret, dio->flags); - - if (likely(!ret)) { - ret = dio->size; - /* check for short read */ - if (offset + ret > dio->i_size && - !(dio->flags & IOMAP_DIO_WRITE)) - ret = dio->i_size - offset; - iocb->ki_pos += ret; - } + if (unlikely(error)) + return error; + + /* check for short read */ + if (!(dio->flags & IOMAP_DIO_WRITE) && offset + ret > dio->i_size) + ret = dio->i_size - offset; + iocb->ki_pos += ret; /* * Try again to invalidate clean pages which might have been cached by @@ -102,13 +97,10 @@ static ssize_t iomap_dio_complete(struct iomap_dio *dio) * ->end_io() when necessary, otherwise a racing buffer read would cache * zeros from unwritten extents. */ - if (!dio->error && - (dio->flags & IOMAP_DIO_WRITE) && inode->i_mapping->nrpages) { - int err; - err = invalidate_inode_pages2_range(inode->i_mapping, + if ((dio->flags & IOMAP_DIO_WRITE) && inode->i_mapping->nrpages) { + if (invalidate_inode_pages2_range(inode->i_mapping, offset >> PAGE_SHIFT, - (offset + dio->size - 1) >> PAGE_SHIFT); - if (err) + (offset + dio->size - 1) >> PAGE_SHIFT)) dio_warn_stale_pagecache(iocb->ki_filp); } @@ -116,10 +108,23 @@ static ssize_t iomap_dio_complete(struct iomap_dio *dio) * If this is a DSYNC write, make sure we push it to stable storage now * that we've written data. */ - if (ret > 0 && (dio->flags & IOMAP_DIO_NEED_SYNC)) - ret = generic_write_sync(iocb, ret); + if ((dio->flags & IOMAP_DIO_NEED_SYNC) && ret > 0) + return generic_write_sync(iocb, ret); + return ret; +} + +static ssize_t iomap_dio_complete(struct iomap_dio *dio) +{ + const struct iomap_dio_ops *dops = dio->dops; + ssize_t ret; + + if (dops && dops->end_io) + ret = dops->end_io(dio->iocb, dio, dio->size, dio->error, + dio->flags); + else + ret = iomap_dio_end_io(dio, dio->error); - inode_dio_end(file_inode(iocb->ki_filp)); + inode_dio_end(file_inode(dio->iocb->ki_filp)); kfree(dio); return ret; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index b8a4a3f29b36..7e3b77e1aec5 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -372,9 +372,10 @@ restart: return file_modified(file); } -static int +static ssize_t xfs_dio_write_end_io( struct kiocb *iocb, + struct iomap_dio *dio, ssize_t size, int error, unsigned flags) @@ -392,7 +393,7 @@ xfs_dio_write_end_io( if (error) return error; if (!size) - return 0; + goto done; /* * Capture amount written on completion as we can't reliably account @@ -410,7 +411,7 @@ xfs_dio_write_end_io( if (flags & IOMAP_DIO_COW) { error = xfs_reflink_end_cow(ip, offset, size); if (error) - goto out; + goto out_restore_nofs; } /* @@ -421,7 +422,7 @@ xfs_dio_write_end_io( */ if (flags & IOMAP_DIO_UNWRITTEN) { error = xfs_iomap_write_unwritten(ip, offset, size, true); - goto out; + goto out_restore_nofs; } /* @@ -444,9 +445,10 @@ xfs_dio_write_end_io( spin_unlock(&ip->i_flags_lock); } -out: +out_restore_nofs: memalloc_nofs_restore(nofs_flag); - return error; +done: + return iomap_dio_end_io(dio, error); } static const struct iomap_dio_ops xfs_dio_write_ops = { diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index 8bc6ef82d693..bd8341cab684 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -548,8 +548,9 @@ static loff_t zonefs_file_llseek(struct file *file, loff_t offset, int whence) return generic_file_llseek_size(file, offset, whence, isize, isize); } -static int zonefs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, - int error, unsigned int flags) +static ssize_t zonefs_file_write_dio_end_io(struct kiocb *iocb, + struct iomap_dio *dio, ssize_t size, int error, + unsigned int flags) { struct inode *inode = file_inode(iocb->ki_filp); struct zonefs_inode_info *zi = ZONEFS_I(inode); @@ -575,7 +576,7 @@ static int zonefs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, mutex_unlock(&zi->i_truncate_mutex); } - return 0; + return iomap_dio_end_io(dio, 0); } static const struct iomap_dio_ops zonefs_write_dio_ops = { @@ -715,15 +716,16 @@ static ssize_t zonefs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) return zonefs_file_buffered_write(iocb, from); } -static int zonefs_file_read_dio_end_io(struct kiocb *iocb, ssize_t size, - int error, unsigned int flags) +static ssize_t zonefs_file_read_dio_end_io(struct kiocb *iocb, + struct iomap_dio *dio, ssize_t size, int error, + unsigned int flags) { if (error) { zonefs_io_error(file_inode(iocb->ki_filp), false); return error; } - return 0; + return iomap_dio_end_io(dio, 0); } static const struct iomap_dio_ops zonefs_read_dio_ops = { diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 8b09463dae0d..e7ace7a46c65 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -13,6 +13,7 @@ struct address_space; struct fiemap_extent_info; struct inode; +struct iomap_dio; struct iomap_writepage_ctx; struct iov_iter; struct kiocb; @@ -250,14 +251,15 @@ int iomap_writepages(struct address_space *mapping, #define IOMAP_DIO_COW (1 << 1) /* covers COW extent(s) */ struct iomap_dio_ops { - int (*end_io)(struct kiocb *iocb, ssize_t size, int error, - unsigned flags); + ssize_t (*end_io)(struct kiocb *iocb, struct iomap_dio *dio, ssize_t size, + int error, unsigned flags); }; ssize_t iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, const struct iomap_ops *ops, const struct iomap_dio_ops *dops, bool wait_for_completion); int iomap_dio_iopoll(struct kiocb *kiocb, bool spin); +ssize_t iomap_dio_end_io(struct iomap_dio *dio, int error); #ifdef CONFIG_SWAP struct file; -- 2.51.0