Add write_begin and write_end operations to struct iomap_ops to provide a way of overriding the default behavior of iomap_write_begin and iomap_write_end. This is needed for implementing data journaling: in the data journaling case, pages are written into the journal before being written back to their proper on-disk locations. Signed-off-by: Andreas Gruenbacher <agruenba@xxxxxxxxxx> Cc: Dave Chinner <dchinner@xxxxxxxxxx> --- fs/ext2/inode.c | 2 ++ fs/ext4/inode.c | 2 ++ fs/iomap.c | 62 ++++++++++++++++++++++++++++++------------- fs/xfs/xfs_iomap.c | 2 ++ include/linux/iomap.h | 22 +++++++++++++++ 5 files changed, 71 insertions(+), 19 deletions(-) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 71635909df3b..3da481f5e36e 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -848,6 +848,8 @@ ext2_iomap_end(struct inode *inode, loff_t offset, loff_t length, const struct iomap_ops ext2_iomap_ops = { .iomap_begin = ext2_iomap_begin, + .write_begin = iomap_write_begin, + .write_end = iomap_write_end, .iomap_end = ext2_iomap_end, }; #else diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 1e50c5efae67..05c8e8015f13 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3604,6 +3604,8 @@ static int ext4_iomap_end(struct inode *inode, loff_t offset, loff_t length, const struct iomap_ops ext4_iomap_ops = { .iomap_begin = ext4_iomap_begin, + .write_begin = iomap_write_begin, + .write_end = iomap_write_end, .iomap_end = ext4_iomap_end, }; diff --git a/fs/iomap.c b/fs/iomap.c index afd163586aa0..27d97a290623 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -108,7 +108,7 @@ iomap_write_failed(struct inode *inode, loff_t pos, unsigned len) truncate_pagecache_range(inode, max(pos, i_size), pos + len); } -static int +int iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, unsigned flags, struct page **pagep, struct iomap *iomap) { @@ -137,8 +137,9 @@ iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, unsigned flags, *pagep = page; return status; } +EXPORT_SYMBOL_GPL(iomap_write_begin); -static int +int iomap_write_end(struct inode *inode, loff_t pos, unsigned len, unsigned copied, struct page *page) { @@ -150,12 +151,19 @@ iomap_write_end(struct inode *inode, loff_t pos, unsigned len, iomap_write_failed(inode, pos, len); return ret; } +EXPORT_SYMBOL_GPL(iomap_write_end); + +struct iomap_write_args { + struct iov_iter *iter; + const struct iomap_ops *ops; +}; static loff_t iomap_write_actor(struct inode *inode, loff_t pos, loff_t length, void *data, struct iomap *iomap) { - struct iov_iter *i = data; + struct iomap_write_args *args = data; + struct iov_iter *i = args->iter; long status = 0; ssize_t written = 0; unsigned int flags = AOP_FLAG_NOFS; @@ -188,7 +196,7 @@ iomap_write_actor(struct inode *inode, loff_t pos, loff_t length, void *data, break; } - status = iomap_write_begin(inode, pos, bytes, flags, &page, + status = args->ops->write_begin(inode, pos, bytes, flags, &page, iomap); if (unlikely(status)) break; @@ -200,7 +208,7 @@ iomap_write_actor(struct inode *inode, loff_t pos, loff_t length, void *data, flush_dcache_page(page); - status = iomap_write_end(inode, pos, bytes, copied, page); + status = args->ops->write_end(inode, pos, bytes, copied, page); if (unlikely(status < 0)) break; copied = status; @@ -237,10 +245,14 @@ iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *iter, { struct inode *inode = iocb->ki_filp->f_mapping->host; loff_t pos = iocb->ki_pos, ret = 0, written = 0; + struct iomap_write_args args = { + .iter = iter, + .ops = ops, + }; while (iov_iter_count(iter)) { ret = iomap_apply(inode, pos, iov_iter_count(iter), - IOMAP_WRITE, ops, iter, iomap_write_actor); + IOMAP_WRITE, ops, &args, iomap_write_actor); if (ret <= 0) break; pos += ret; @@ -271,6 +283,7 @@ static loff_t iomap_dirty_actor(struct inode *inode, loff_t pos, loff_t length, void *data, struct iomap *iomap) { + const struct iomap_ops *ops = data; long status = 0; ssize_t written = 0; @@ -286,15 +299,15 @@ iomap_dirty_actor(struct inode *inode, loff_t pos, loff_t length, void *data, if (IS_ERR(rpage)) return PTR_ERR(rpage); - status = iomap_write_begin(inode, pos, bytes, - AOP_FLAG_NOFS, &page, iomap); + status = ops->write_begin(inode, pos, bytes, + AOP_FLAG_NOFS, &page, iomap); put_page(rpage); if (unlikely(status)) return status; WARN_ON_ONCE(!PageUptodate(page)); - status = iomap_write_end(inode, pos, bytes, bytes, page); + status = ops->write_end(inode, pos, bytes, bytes, page); if (unlikely(status <= 0)) { if (WARN_ON_ONCE(status == 0)) return -EIO; @@ -320,7 +333,7 @@ iomap_file_dirty(struct inode *inode, loff_t pos, loff_t len, loff_t ret; while (len) { - ret = iomap_apply(inode, pos, len, IOMAP_WRITE, ops, NULL, + ret = iomap_apply(inode, pos, len, IOMAP_WRITE, ops, (void *)ops, iomap_dirty_actor); if (ret <= 0) return ret; @@ -333,20 +346,21 @@ iomap_file_dirty(struct inode *inode, loff_t pos, loff_t len, EXPORT_SYMBOL_GPL(iomap_file_dirty); static int iomap_zero(struct inode *inode, loff_t pos, unsigned offset, - unsigned bytes, struct iomap *iomap) + unsigned bytes, const struct iomap_ops *ops, + struct iomap *iomap) { struct page *page; int status; - status = iomap_write_begin(inode, pos, bytes, AOP_FLAG_NOFS, &page, - iomap); + status = ops->write_begin(inode, pos, bytes, AOP_FLAG_NOFS, &page, + iomap); if (status) return status; zero_user(page, offset, bytes); mark_page_accessed(page); - return iomap_write_end(inode, pos, bytes, bytes, page); + return ops->write_end(inode, pos, bytes, bytes, page); } static int iomap_dax_zero(loff_t pos, unsigned offset, unsigned bytes, @@ -359,11 +373,16 @@ static int iomap_dax_zero(loff_t pos, unsigned offset, unsigned bytes, offset, bytes); } +struct iomap_zero_range_args { + const struct iomap_ops *ops; + bool *did_zero; +}; + static loff_t iomap_zero_range_actor(struct inode *inode, loff_t pos, loff_t count, void *data, struct iomap *iomap) { - bool *did_zero = data; + struct iomap_zero_range_args *args = data; loff_t written = 0; int status; @@ -380,15 +399,16 @@ iomap_zero_range_actor(struct inode *inode, loff_t pos, loff_t count, if (IS_DAX(inode)) status = iomap_dax_zero(pos, offset, bytes, iomap); else - status = iomap_zero(inode, pos, offset, bytes, iomap); + status = iomap_zero(inode, pos, offset, bytes, + (void *)args->ops, iomap); if (status < 0) return status; pos += bytes; count -= bytes; written += bytes; - if (did_zero) - *did_zero = true; + if (args->did_zero) + *args->did_zero = true; } while (count > 0); return written; @@ -398,11 +418,15 @@ int iomap_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero, const struct iomap_ops *ops) { + struct iomap_zero_range_args args = { + .ops = ops, + .did_zero = did_zero, + }; loff_t ret; while (len > 0) { ret = iomap_apply(inode, pos, len, IOMAP_ZERO, - ops, did_zero, iomap_zero_range_actor); + ops, &args, iomap_zero_range_actor); if (ret <= 0) return ret; diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 046469fcc1b8..2eec725cf989 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -1199,6 +1199,8 @@ xfs_file_iomap_end( const struct iomap_ops xfs_iomap_ops = { .iomap_begin = xfs_file_iomap_begin, + .write_begin = iomap_write_begin, + .write_end = iomap_write_end, .iomap_end = xfs_file_iomap_end, }; diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 19a07de28212..423f7ecc1231 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -61,6 +61,8 @@ struct iomap { #define IOMAP_DIRECT (1 << 4) /* direct I/O */ #define IOMAP_NOWAIT (1 << 5) /* Don't wait for writeback */ +struct page; + struct iomap_ops { /* * Return the existing mapping at pos, or reserve space starting at @@ -70,6 +72,21 @@ struct iomap_ops { int (*iomap_begin)(struct inode *inode, loff_t pos, loff_t length, unsigned flags, struct iomap *iomap); + /* + * Begin writing to a page, similar to block_write_begin but in a + * mapping returned by iomap_begin. Usually initialized to + * iomap_write_begin. + */ + int (*write_begin)(struct inode *inode, loff_t pos, unsigned len, + unsigned flags, struct page **pagep, + struct iomap *iomap); + + /* + * End writing to a page. Usually initialized to iomap_write_end. + */ + int (*write_end)(struct inode *inode, loff_t pos, unsigned len, + unsigned copied, struct page *page); + /* * Commit and/or unreserve space previous allocated using iomap_begin. * Written indicates the length of the successful write operation which @@ -80,6 +97,11 @@ struct iomap_ops { ssize_t written, unsigned flags, struct iomap *iomap); }; +int iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, + unsigned flags, struct page **pagep, struct iomap *iomap); +int iomap_write_end(struct inode *inode, loff_t pos, unsigned len, + unsigned copied, struct page *page); + ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from, const struct iomap_ops *ops); int iomap_file_dirty(struct inode *inode, loff_t pos, loff_t len, -- 2.17.0