|
@@ -31,6 +31,7 @@
|
|
|
#include "xfs_bmap.h"
|
|
|
#include "xfs_bmap_util.h"
|
|
|
#include "xfs_bmap_btree.h"
|
|
|
+#include "xfs_reflink.h"
|
|
|
#include <linux/gfp.h>
|
|
|
#include <linux/mpage.h>
|
|
|
#include <linux/pagevec.h>
|
|
@@ -341,6 +342,7 @@ xfs_map_blocks(
|
|
|
if (XFS_FORCED_SHUTDOWN(mp))
|
|
|
return -EIO;
|
|
|
|
|
|
+ ASSERT(type != XFS_IO_COW);
|
|
|
if (type == XFS_IO_UNWRITTEN)
|
|
|
bmapi_flags |= XFS_BMAPI_IGSTATE;
|
|
|
|
|
@@ -355,6 +357,13 @@ xfs_map_blocks(
|
|
|
offset_fsb = XFS_B_TO_FSBT(mp, offset);
|
|
|
error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
|
|
|
imap, &nimaps, bmapi_flags);
|
|
|
+ /*
|
|
|
+ * Truncate an overwrite extent if there's a pending CoW
|
|
|
+ * reservation before the end of this extent. This forces us
|
|
|
+ * to come back to writepage to take care of the CoW.
|
|
|
+ */
|
|
|
+ if (nimaps && type == XFS_IO_OVERWRITE)
|
|
|
+ xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb, imap);
|
|
|
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
|
|
|
|
|
if (error)
|
|
@@ -365,8 +374,7 @@ xfs_map_blocks(
|
|
|
error = xfs_iomap_write_allocate(ip, XFS_DATA_FORK, offset,
|
|
|
imap);
|
|
|
if (!error)
|
|
|
- trace_xfs_map_blocks_alloc(ip, offset, count, type,
|
|
|
- imap);
|
|
|
+ trace_xfs_map_blocks_alloc(ip, offset, count, type, imap);
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -739,6 +747,56 @@ out_invalidate:
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+xfs_map_cow(
|
|
|
+ struct xfs_writepage_ctx *wpc,
|
|
|
+ struct inode *inode,
|
|
|
+ loff_t offset,
|
|
|
+ unsigned int *new_type)
|
|
|
+{
|
|
|
+ struct xfs_inode *ip = XFS_I(inode);
|
|
|
+ struct xfs_bmbt_irec imap;
|
|
|
+ bool is_cow = false, need_alloc = false;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we already have a valid COW mapping keep using it.
|
|
|
+ */
|
|
|
+ if (wpc->io_type == XFS_IO_COW) {
|
|
|
+ wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap, offset);
|
|
|
+ if (wpc->imap_valid) {
|
|
|
+ *new_type = XFS_IO_COW;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Else we need to check if there is a COW mapping at this offset.
|
|
|
+ */
|
|
|
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
|
|
|
+ is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
|
|
|
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
|
|
+
|
|
|
+ if (!is_cow)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * And if the COW mapping has a delayed extent here we need to
|
|
|
+ * allocate real space for it now.
|
|
|
+ */
|
|
|
+ if (need_alloc) {
|
|
|
+ error = xfs_iomap_write_allocate(ip, XFS_COW_FORK, offset,
|
|
|
+ &imap);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ }
|
|
|
+
|
|
|
+ wpc->io_type = *new_type = XFS_IO_COW;
|
|
|
+ wpc->imap_valid = true;
|
|
|
+ wpc->imap = imap;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* We implement an immediate ioend submission policy here to avoid needing to
|
|
|
* chain multiple ioends and hence nest mempool allocations which can violate
|
|
@@ -771,6 +829,7 @@ xfs_writepage_map(
|
|
|
int error = 0;
|
|
|
int count = 0;
|
|
|
int uptodate = 1;
|
|
|
+ unsigned int new_type;
|
|
|
|
|
|
bh = head = page_buffers(page);
|
|
|
offset = page_offset(page);
|
|
@@ -791,22 +850,13 @@ xfs_writepage_map(
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- if (buffer_unwritten(bh)) {
|
|
|
- if (wpc->io_type != XFS_IO_UNWRITTEN) {
|
|
|
- wpc->io_type = XFS_IO_UNWRITTEN;
|
|
|
- wpc->imap_valid = false;
|
|
|
- }
|
|
|
- } else if (buffer_delay(bh)) {
|
|
|
- if (wpc->io_type != XFS_IO_DELALLOC) {
|
|
|
- wpc->io_type = XFS_IO_DELALLOC;
|
|
|
- wpc->imap_valid = false;
|
|
|
- }
|
|
|
- } else if (buffer_uptodate(bh)) {
|
|
|
- if (wpc->io_type != XFS_IO_OVERWRITE) {
|
|
|
- wpc->io_type = XFS_IO_OVERWRITE;
|
|
|
- wpc->imap_valid = false;
|
|
|
- }
|
|
|
- } else {
|
|
|
+ if (buffer_unwritten(bh))
|
|
|
+ new_type = XFS_IO_UNWRITTEN;
|
|
|
+ else if (buffer_delay(bh))
|
|
|
+ new_type = XFS_IO_DELALLOC;
|
|
|
+ else if (buffer_uptodate(bh))
|
|
|
+ new_type = XFS_IO_OVERWRITE;
|
|
|
+ else {
|
|
|
if (PageUptodate(page))
|
|
|
ASSERT(buffer_mapped(bh));
|
|
|
/*
|
|
@@ -819,6 +869,17 @@ xfs_writepage_map(
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ if (xfs_is_reflink_inode(XFS_I(inode))) {
|
|
|
+ error = xfs_map_cow(wpc, inode, offset, &new_type);
|
|
|
+ if (error)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wpc->io_type != new_type) {
|
|
|
+ wpc->io_type = new_type;
|
|
|
+ wpc->imap_valid = false;
|
|
|
+ }
|
|
|
+
|
|
|
if (wpc->imap_valid)
|
|
|
wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap,
|
|
|
offset);
|