From: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx> The IOMAP_DAX_COW is a iomap type which performs copy of edges of data while performing a write if start/end are not page aligned. The source address is expected in iomap->inline_data. dax_copy_edges() is a helper functions performs a copy from one part of the device to another for data not page aligned. If iomap->inline_data is NULL, it memset's the area to zero. Signed-off-by: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx> --- fs/dax.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- include/linux/iomap.h | 1 + 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/fs/dax.c b/fs/dax.c index e5e54da1715f..610bfa861a28 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1084,6 +1084,42 @@ int __dax_zero_page_range(struct block_device *bdev, } EXPORT_SYMBOL_GPL(__dax_zero_page_range); +/* + * dax_copy_edges - Copies the part of the pages not included in + * the write, but required for CoW because + * offset/offset+length are not page aligned. + */ +static int dax_copy_edges(struct inode *inode, loff_t pos, loff_t length, + struct iomap *iomap, void *daddr) +{ + unsigned offset = pos & (PAGE_SIZE - 1); + loff_t end = pos + length; + loff_t pg_end = round_up(end, PAGE_SIZE); + void *saddr = iomap->inline_data; + int ret = 0; + /* + * Copy the first part of the page + * Note: we pass offset as length + */ + if (offset) { + if (saddr) + ret = memcpy_mcsafe(daddr, saddr, offset); + else + memset(daddr, 0, offset); + } + + /* Copy the last part of the range */ + if (end < pg_end) { + if (saddr) + ret = memcpy_mcsafe(daddr + offset + length, + saddr + offset + length, pg_end - end); + else + memset(daddr + offset + length, 0, + pg_end - end); + } + return ret; +} + static loff_t dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data, struct iomap *iomap) @@ -1105,9 +1141,11 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data, return iov_iter_zero(min(length, end - pos), iter); } - if (WARN_ON_ONCE(iomap->type != IOMAP_MAPPED)) + if (WARN_ON_ONCE(iomap->type != IOMAP_MAPPED + && iomap->type != IOMAP_DAX_COW)) return -EIO; + /* * Write can allocate block for an area which has a hole page mapped * into page tables. We have to tear down these mappings so that data @@ -1144,6 +1182,12 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data, break; } + if (iomap->type == IOMAP_DAX_COW) { + ret = dax_copy_edges(inode, pos, length, iomap, kaddr); + if (ret) + break; + } + map_len = PFN_PHYS(map_len); kaddr += offset; map_len -= offset; diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 0fefb5455bda..6e885c5a38a3 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -25,6 +25,7 @@ struct vm_fault; #define IOMAP_MAPPED 0x03 /* blocks allocated at @addr */ #define IOMAP_UNWRITTEN 0x04 /* blocks allocated at @addr in unwritten state */ #define IOMAP_INLINE 0x05 /* data inline in the inode */ +#define IOMAP_DAX_COW 0x06 /* Copy data pointed by inline_data before write*/ /* * Flags for all iomap mappings: -- 2.16.4