aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:19:49 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-12-15 17:29:26 -0800
commit7ce91d85f50da47ca729f30c17d6db3883d71807 (patch)
treeb352be266ef7b5946ccab73da21d5cf1e1503ae9
parent74b282013965f04b503e533845eb5483da7c9521 (diff)
downloadxfs-linux-7ce91d85f50da47ca729f30c17d6db3883d71807.tar.gz
iomap: set up for COWing around pages
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@kernel.org>
-rw-r--r--fs/iomap/buffered-io.c55
-rw-r--r--include/linux/iomap.h2
2 files changed, 57 insertions, 0 deletions
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 71a36ae120ee8..76a879dc2aebf 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -876,6 +876,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 page *page;
+
+ status = iomap_write_begin(iter, pos, bytes, &page);
+ if (unlikely(status))
+ return status;
+
+ mark_page_accessed(page);
+
+ status = iomap_write_end(iter, pos, bytes, bytes, page);
+ 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 s64 __iomap_zero_iter(struct iomap_iter *iter, loff_t pos, u64 length)
{
struct page *page;
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 6d1b08d0ae930..3c7c939d394ee 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -235,6 +235,8 @@ int iomap_migrate_page(struct address_space *mapping, struct page *newpage,
#endif
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,