[PATCH 63/76] xfs: cancel pending CoW reservations when destroying inodes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When destroying the inode, cancel all pending reservations in the CoW
fork so that all the reserved blocks go back to the free pile.  Also
free the reservations whenever we truncate or punch the entire file.

Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
 fs/xfs/xfs_bmap_util.c |    1 +
 fs/xfs/xfs_inode.c     |    5 +++-
 fs/xfs/xfs_reflink.c   |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_reflink.h   |    1 +
 fs/xfs/xfs_super.c     |   10 ++++++++
 5 files changed, 80 insertions(+), 1 deletion(-)


diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index dd5a2f7..3e274f6 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1393,6 +1393,7 @@ xfs_free_file_space(
 		 * Clear the reflink flag if we freed everything.
 		 */
 		if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) {
+			xfs_reflink_cancel_pending_cow(ip);
 			ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
 			xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 		}
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index a323631..59964c3 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -48,6 +48,7 @@
 #include "xfs_trans_priv.h"
 #include "xfs_log.h"
 #include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"
 
 kmem_zone_t *xfs_inode_zone;
 
@@ -1615,8 +1616,10 @@ xfs_itruncate_extents(
 	/*
 	 * Clear the reflink flag if we truncated everything.
 	 */
-	if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip))
+	if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) {
+		xfs_reflink_cancel_pending_cow(ip);
 		ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+	}
 
 	/*
 	 * Always re-log the inode so that our permanent transaction can keep
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 8594bc4..dcc71b9 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -658,3 +658,67 @@ out:
 	trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_);
 	return error;
 }
+
+/**
+ * xfs_reflink_cancel_pending_cow() -- Cancel all pending CoW fork reservations.
+ * @ip: The XFS inode.
+ */
+int
+xfs_reflink_cancel_pending_cow(
+	struct xfs_inode	*ip)
+{
+	struct xfs_bmbt_irec	irec;
+	struct xfs_ifork	*ifp;
+	xfs_extnum_t		idx;
+	struct xfs_bmbt_rec_host	*gotp;
+	int			error;
+
+	/*
+	 * If this isn't a reflink inode there had better not be anything
+	 * in the CoW fork!
+	 */
+	if (!xfs_is_reflink_inode(ip)) {
+		ASSERT(ip->i_cowfp == NULL || ip->i_cowfp->if_bytes == 0);
+		return 0;
+	}
+
+	trace_xfs_reflink_cancel_pending_cow(ip);
+
+	ASSERT(ip->i_cowfp != NULL);
+	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+
+	/* Go find the old extent in the CoW fork. */
+	ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+	gotp = xfs_iext_bno_to_ext(ifp, 0, &idx);
+	while (gotp) {
+		xfs_bmbt_get_all(gotp, &irec);
+
+		/*
+		 * We should never be asked to cancel allocated CoW extents
+		 * unless the FS is broken, because doing so prevents the
+		 * block remapping after the write finishes.
+		 */
+		ASSERT(isnullstartblock(irec.br_startblock) ||
+		       XFS_FORCED_SHUTDOWN(ip->i_mount));
+		if (!isnullstartblock(irec.br_startblock))
+			goto advloop;
+
+		trace_xfs_reflink_cancel_cow(ip, &irec);
+
+		/* Give the blocks back. */
+		xfs_mod_fdblocks(ip->i_mount, irec.br_blockcount, false);
+		ip->i_delayed_blks -= irec.br_blockcount;
+		error = xfs_bunmapi_cow(ip, &idx, &irec);
+		if (error)
+			break;
+
+		/* Roll on... */
+advloop:
+		idx++;
+		if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+			break;
+		gotp = xfs_iext_get_ext(ifp, idx);
+	}
+
+	return 0;
+}
diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h
index 2d2832b..cf4b43b 100644
--- a/fs/xfs/xfs_reflink.h
+++ b/fs/xfs/xfs_reflink.h
@@ -32,6 +32,7 @@ extern int xfs_reflink_end_cow_failed(struct xfs_inode *ip, xfs_off_t offset,
 		size_t count);
 extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset,
 		size_t count);
+extern int xfs_reflink_cancel_pending_cow(struct xfs_inode *ip);
 
 int	xfs_map_cow_blocks(struct inode *inode, xfs_off_t offset,
 			   struct xfs_bmbt_irec	*imap);
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 85e582d..36df40b 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -45,6 +45,7 @@
 #include "xfs_filestream.h"
 #include "xfs_quota.h"
 #include "xfs_sysfs.h"
+#include "xfs_reflink.h"
 
 #include <linux/namei.h>
 #include <linux/init.h>
@@ -920,11 +921,20 @@ xfs_fs_destroy_inode(
 	struct inode		*inode)
 {
 	struct xfs_inode	*ip = XFS_I(inode);
+	int			error;
 
 	trace_xfs_destroy_inode(ip);
 
 	XFS_STATS_INC(ip->i_mount, vn_reclaim);
 
+	/* Cancel the CoW reservations. */
+	xfs_ilock(ip, XFS_ILOCK_EXCL);
+	error = xfs_reflink_cancel_pending_cow(ip);
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	if (error)
+		xfs_warn(ip->i_mount, "Error %d destroying inode %llu.\n",
+				error, ip->i_ino);
+
 	ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
 
 	/*

_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs



[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux