diff mbox series

[17/18] xfs: make atomic extent swapping support realtime files

Message ID 161723942188.3149451.1970633936998780647.stgit@magnolia (mailing list archive)
State New, archived
Headers show
Series xfs: atomic file updates | expand

Commit Message

Darrick J. Wong April 1, 2021, 1:10 a.m. UTC
From: Darrick J. Wong <djwong@kernel.org>

Now that bmap items support the realtime device, we can add the
necessary pieces to the atomic extent swapping code to support such
things.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/xfs/libxfs/xfs_format.h  |    7 ++--
 fs/xfs/libxfs/xfs_swapext.c |   67 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 68 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index e81a7b12a0e3..15d967414500 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -611,13 +611,12 @@  static inline bool xfs_sb_version_needsrepair(struct xfs_sb *sbp)
 /*
  * Decide if this filesystem can use log-assisted ("atomic") extent swapping.
  * The atomic swap log intent items depend on the block mapping log intent
- * items introduced with reflink and rmap.  Realtime is not supported yet.
+ * items introduced with reflink and rmap.
  */
 static inline bool xfs_sb_version_canatomicswap(struct xfs_sb *sbp)
 {
-	return (xfs_sb_version_hasreflink(sbp) ||
-		xfs_sb_version_hasrmapbt(sbp)) &&
-		!xfs_sb_version_hasrealtime(sbp);
+	return  xfs_sb_version_hasreflink(sbp) ||
+		xfs_sb_version_hasrmapbt(sbp);
 }
 
 static inline bool xfs_sb_version_hasatomicswap(struct xfs_sb *sbp)
diff --git a/fs/xfs/libxfs/xfs_swapext.c b/fs/xfs/libxfs/xfs_swapext.c
index 41042ee05e40..995b59d86d79 100644
--- a/fs/xfs/libxfs/xfs_swapext.c
+++ b/fs/xfs/libxfs/xfs_swapext.c
@@ -279,7 +279,14 @@  xfs_swapext_check_extents(
 	struct xfs_mount		*mp,
 	const struct xfs_swapext_req	*req)
 {
+	struct xfs_bmbt_irec		irec1, irec2;
 	struct xfs_ifork		*ifp1, *ifp2;
+	xfs_fileoff_t			startoff1 = req->startoff1;
+	xfs_fileoff_t			startoff2 = req->startoff2;
+	xfs_filblks_t			blockcount = req->blockcount;
+	uint32_t			mod;
+	int				nimaps;
+	int				error;
 
 	/* No fork? */
 	ifp1 = XFS_IFORK_PTR(req->ip1, req->whichfork);
@@ -292,12 +299,68 @@  xfs_swapext_check_extents(
 	    ifp2->if_format == XFS_DINODE_FMT_LOCAL)
 		return -EINVAL;
 
-	/* We don't support realtime data forks yet. */
+	/*
+	 * There may be partially written rt extents lurking in the ranges to
+	 * be swapped.  If we support atomic swapext log intent items, we can
+	 * guarantee that operations will always finish and never leave an rt
+	 * extent partially mapped to two files, and can move on.  If we don't
+	 * have that coordination, we have to scan both ranges to ensure that
+	 * there are no partially written extents.
+	 */
 	if (!XFS_IS_REALTIME_INODE(req->ip1))
 		return 0;
 	if (req->whichfork == XFS_ATTR_FORK)
 		return 0;
-	return -EINVAL;
+	if (xfs_sb_version_hasatomicswap(&mp->m_sb))
+		return 0;
+	if (mp->m_sb.sb_rextsize == 1)
+		return 0;
+
+	while (blockcount > 0) {
+		/* Read extent from the first file */
+		nimaps = 1;
+		error = xfs_bmapi_read(req->ip1, startoff1, blockcount,
+				&irec1, &nimaps, 0);
+		if (error)
+			return error;
+		ASSERT(nimaps == 1);
+
+		/* Read extent from the second file */
+		nimaps = 1;
+		error = xfs_bmapi_read(req->ip2, startoff2,
+				irec1.br_blockcount, &irec2, &nimaps,
+				0);
+		if (error)
+			return error;
+		ASSERT(nimaps == 1);
+
+		/*
+		 * We can only swap as many blocks as the smaller of the two
+		 * extent maps.
+		 */
+		irec1.br_blockcount = min(irec1.br_blockcount,
+					  irec2.br_blockcount);
+
+		/*
+		 * Both mappings must be aligned to the realtime extent size
+		 * if either mapping comes from the realtime volume.
+		 */
+		div_u64_rem(irec1.br_startoff, mp->m_sb.sb_rextsize, &mod);
+		if (mod)
+			return -EINVAL;
+		div_u64_rem(irec2.br_startoff, mp->m_sb.sb_rextsize, &mod);
+		if (mod)
+			return -EINVAL;
+		div_u64_rem(irec1.br_blockcount, mp->m_sb.sb_rextsize, &mod);
+		if (mod)
+			return -EINVAL;
+
+		startoff1 += irec1.br_blockcount;
+		startoff2 += irec1.br_blockcount;
+		blockcount -= irec1.br_blockcount;
+	}
+
+	return 0;
 }
 
 #ifdef CONFIG_XFS_QUOTA