From: Darrick J. Wong <djwong@xxxxxxxxxx> In anticipation of enabling reflink on the realtime volume where the allocation unit is larger than a page, create an iomap function to dirty arbitrary parts of a file's page cache so that when we dirty part of a file that could undergo a COW extent, we can dirty an entire allocation unit's worth of pages. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/iomap/buffered-io.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/iomap.h | 2 ++ 2 files changed, 57 insertions(+) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 356193e44cf0..da5a5d28e2ee 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1141,6 +1141,61 @@ iomap_file_unshare(struct inode *inode, loff_t pos, loff_t len, } EXPORT_SYMBOL_GPL(iomap_file_unshare); +static loff_t iomap_dirty_iter(struct iomap_iter *iter) +{ + loff_t pos = iter->pos; + loff_t length = iomap_length(iter); + long status = 0; + loff_t written = 0; + + do { + unsigned long offset = offset_in_page(pos); + unsigned long bytes = min_t(loff_t, PAGE_SIZE - offset, length); + struct folio *folio; + + status = iomap_write_begin(iter, pos, bytes, &folio); + if (unlikely(status)) + return status; + + folio_mark_accessed(folio); + + status = iomap_write_end(iter, pos, bytes, bytes, folio); + if (WARN_ON_ONCE(status == 0)) + return -EIO; + + cond_resched(); + + pos += status; + written += status; + length -= status; + + balance_dirty_pages_ratelimited(iter->inode->i_mapping); + } while (length); + + return written; +} + +int +iomap_dirty_range(struct inode *inode, loff_t pos, u64 len, + const struct iomap_ops *ops) +{ + struct iomap_iter iter = { + .inode = inode, + .pos = pos, + .len = len, + .flags = IOMAP_WRITE, + }; + int ret; + + if (IS_DAX(inode)) + return -EINVAL; + + while ((ret = iomap_iter(&iter, ops)) > 0) + iter.processed = iomap_dirty_iter(&iter); + return ret; +} +EXPORT_SYMBOL_GPL(iomap_dirty_range); + static loff_t iomap_zero_iter(struct iomap_iter *iter, bool *did_zero) { const struct iomap *srcmap = iomap_iter_srcmap(iter); diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 0983dfc9a203..4d911d780165 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -264,6 +264,8 @@ bool iomap_release_folio(struct folio *folio, gfp_t gfp_flags); void iomap_invalidate_folio(struct folio *folio, size_t offset, size_t len); int iomap_file_unshare(struct inode *inode, loff_t pos, loff_t len, const struct iomap_ops *ops); +int iomap_dirty_range(struct inode *inode, loff_t pos, u64 len, + const struct iomap_ops *ops); int iomap_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero, const struct iomap_ops *ops); int iomap_truncate_page(struct inode *inode, loff_t pos, bool *did_zero,