From: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx>
In case of a IOMAP_F_COW, read a page from the srcmap before
performing a write on the page.
Signed-off-by: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx>
Reviewed-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
fs/iomap/buffered-io.c | 30 +++++++++++++++++++++---------
include/linux/iomap.h | 3 +++
2 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index f27756c0b31c..560459df75e4 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -581,7 +581,7 @@ __iomap_write_begin(struct inode *inode, loff_t pos, unsigned len,
static int
iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, unsigned flags,
- struct page **pagep, struct iomap *iomap)
+ struct page **pagep, struct iomap *iomap, struct iomap *srcmap)
{
const struct iomap_page_ops *page_ops = iomap->page_ops;
pgoff_t index = pos >> PAGE_SHIFT;
@@ -605,12 +605,24 @@ iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, unsigned flags,
goto out_no_page;
}
- if (iomap->type == IOMAP_INLINE)
+ if (iomap->type == IOMAP_INLINE) {
iomap_read_inline_data(inode, page, iomap);
- else if (iomap->flags & IOMAP_F_BUFFER_HEAD)
+ } else if (iomap->flags & IOMAP_F_COW) {
+ if (WARN_ON_ONCE(iomap->flags & IOMAP_F_BUFFER_HEAD)) {
+ status = -EIO;
+ goto out_no_page;
+ }
+ if (WARN_ON_ONCE(srcmap->type == IOMAP_HOLE &&
+ srcmap->addr != IOMAP_NULL_ADDR)) {
+ status = -EIO;
+ goto out_no_page;
+ }
+ status = __iomap_write_begin(inode, pos, len, page, srcmap);
+ } else if (iomap->flags & IOMAP_F_BUFFER_HEAD) {
status = __block_write_begin_int(page, pos, len, NULL, iomap);
- else
+ } else {
status = __iomap_write_begin(inode, pos, len, page, iomap);
+ }
if (unlikely(status))
goto out_unlock;
@@ -772,7 +784,7 @@ iomap_write_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
}
status = iomap_write_begin(inode, pos, bytes, flags, &page,
- iomap);
+ iomap, srcmap);
if (unlikely(status))
break;
@@ -871,7 +883,7 @@ iomap_dirty_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
return PTR_ERR(rpage);
status = iomap_write_begin(inode, pos, bytes,
- AOP_FLAG_NOFS, &page, iomap);
+ AOP_FLAG_NOFS, &page, iomap, srcmap);
put_page(rpage);
if (unlikely(status))
return status;
@@ -917,13 +929,13 @@ 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, struct iomap *iomap, struct iomap *srcmap)
{
struct page *page;
int status;
status = iomap_write_begin(inode, pos, bytes, AOP_FLAG_NOFS, &page,
- iomap);
+ iomap, srcmap);
if (status)
return status;
@@ -961,7 +973,7 @@ 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, iomap, srcmap);
if (status < 0)
return status;
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 9782a79dde59..7fdb09925740 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -44,6 +44,9 @@ struct vm_fault;
#define IOMAP_F_MERGED 0x10 /* contains multiple blocks/extents */
#define IOMAP_F_SHARED 0x20 /* block shared with another file */
+/* Flags for CoW */
+#define IOMAP_F_COW 0x100 /* copy from srcmap before write */
+
/*
* Flags from 0x1000 up are for file system specific usage:
*/
--
2.16.4