[3/5] xfs: use atomic extent swapping to repair rt metadata
diff mbox series

Message ID 158812839291.169849.5779915521845398306.stgit@magnolia
State New
Headers show
Series
  • xfs: atomic file metadata repairs
Related show

Commit Message

Darrick J. Wong April 29, 2020, 2:46 a.m. UTC
From: Darrick J. Wong <darrick.wong@oracle.com>

When repairing realtime volume metadata online, stage the new directory
contents in a temporary file and use the atomic extent swapping
mechanism to commit the results in bulk.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/repair.c           |   34 ++++++++++++++++++++--------
 fs/xfs/scrub/rtbitmap.c         |   12 ++++++++++
 fs/xfs/scrub/rtbitmap_repair.c  |   48 +++++++++++++++++++++++++++++++++++++--
 fs/xfs/scrub/rtsummary.c        |   12 ++++++++++
 fs/xfs/scrub/rtsummary_repair.c |   46 ++++++++++++++++++++++++++++++++++++-
 5 files changed, 137 insertions(+), 15 deletions(-)

Patch
diff mbox series

diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c
index 0ec483d511cd..5b876b02b9f4 100644
--- a/fs/xfs/scrub/repair.c
+++ b/fs/xfs/scrub/repair.c
@@ -1636,13 +1636,13 @@  xrep_fallocate(
 	xfs_filblks_t		len)
 {
 	struct xfs_bmbt_irec	map;
+	struct xfs_inode	*ip = sc->tempip;
 	xfs_fileoff_t		end = off + len;
 	int			nmaps;
 	int			error = 0;
 
-	error = xrep_ino_dqattach(sc);
-	if (error)
-		return error;
+	ASSERT(sc->tempip != NULL);
+	ASSERT(!XFS_NOT_DQATTACHED(sc->mp, ip));
 
 	while (off < len) {
 		/*
@@ -1650,7 +1650,7 @@  xrep_fallocate(
 		 * in ok shape.
 		 */
 		nmaps = 1;
-		error = xfs_bmapi_read(sc->ip, off, end - off, &map, &nmaps,
+		error = xfs_bmapi_read(ip, off, end - off, &map, &nmaps,
 				XFS_DATA_FORK);
 		if (error)
 			break;
@@ -1672,15 +1672,21 @@  xrep_fallocate(
 		 * allocated to it.
 		 */
 		nmaps = 1;
-		error = xfs_bmapi_write(sc->tp, sc->ip, off, end - off,
+		error = xfs_bmapi_write(sc->tp, ip, off, end - off,
 				XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO, 0, &map,
 				&nmaps);
 		if (error)
 			break;
 
-		error = xfs_trans_roll_inode(&sc->tp, sc->ip);
+		/*
+		 * Roll the transaction with the inode we're fixing and the
+		 * temp inode, so that neither can pin the log.
+		 */
+		xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
+		error = xfs_trans_roll_inode(&sc->tp, ip);
 		if (error)
 			break;
+		xfs_trans_ijoin(sc->tp, sc->ip, 0);
 		off += map.br_startblock;
 	}
 
@@ -1701,6 +1707,7 @@  xrep_set_file_contents(
 {
 	struct list_head	buffers_list;
 	struct xfs_mount	*mp = sc->mp;
+	struct xfs_inode	*ip = sc->tempip;
 	struct xfs_buf		*bp;
 	xfs_rtblock_t		off = 0;
 	loff_t			pos = 0;
@@ -1744,12 +1751,19 @@  xrep_set_file_contents(
 	}
 
 	/* Set the new inode size, if needed. */
-	if (sc->ip->i_d.di_size != isize) {
-		sc->ip->i_d.di_size = isize;
-		xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
+	if (ip->i_d.di_size != isize) {
+		ip->i_d.di_size = isize;
+		xfs_trans_log_inode(sc->tp, ip, XFS_ILOG_CORE);
 	}
 
-	return xfs_trans_roll_inode(&sc->tp, sc->ip);
+	/*
+	 * Roll transaction, being careful to keep the tempfile and the
+	 * metadata inode joined.
+	 */
+	xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
+	error = xfs_trans_roll_inode(&sc->tp, ip);
+	xfs_trans_ijoin(sc->tp, sc->ip, 0);
+	return error;
 out:
 	xfs_buf_delwri_cancel(&buffers_list);
 	return error;
diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
index 8488d137bf92..c3396d9ead49 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -20,6 +20,7 @@ 
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/btree.h"
+#include "scrub/repair.h"
 
 /* Set us up with the realtime metadata locked. */
 int
@@ -29,6 +30,17 @@  xchk_setup_rt(
 {
 	int			error;
 
+#ifdef CONFIG_XFS_ONLINE_REPAIR
+	if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
+		if (!xfs_sb_version_hasatomicswap(&sc->mp->m_sb))
+			return -EOPNOTSUPP;
+
+		error = xrep_create_tempfile(sc, S_IFREG);
+		if (error)
+			return error;
+	}
+#endif
+
 	error = xchk_setup_fs(sc, ip);
 	if (error)
 		return error;
diff --git a/fs/xfs/scrub/rtbitmap_repair.c b/fs/xfs/scrub/rtbitmap_repair.c
index 229dd23d9d3e..d812efe8dd2a 100644
--- a/fs/xfs/scrub/rtbitmap_repair.c
+++ b/fs/xfs/scrub/rtbitmap_repair.c
@@ -18,6 +18,7 @@ 
 #include "xfs_bmap.h"
 #include "xfs_rmap.h"
 #include "xfs_rtrmap_btree.h"
+#include "xfs_swapext.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
@@ -207,7 +208,29 @@  xrep_rtbitmap_get_buf(
 	xfs_fileoff_t		off,
 	struct xfs_buf		**bpp)
 {
-	return xfs_rtbuf_get(sc->mp, sc->tp, off, 0, bpp);
+	struct xfs_bmbt_irec	map;
+	struct xfs_buf		*bp;
+	struct xfs_mount	*mp = sc->mp;
+	int			nmap = 1;
+	int			error;
+
+	error = xfs_bmapi_read(sc->tempip, off, 1, &map, &nmap,
+			XFS_DATA_FORK);
+	if (error)
+		return error;
+
+	if (nmap == 0 || !xfs_bmap_is_real_extent(&map))
+		return -EFSCORRUPTED;
+
+	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
+			XFS_FSB_TO_DADDR(mp, map.br_startblock),
+			mp->m_bsize, 0, &bp, &xfs_rtbuf_ops);
+	if (error)
+		return error;
+
+	xfs_trans_buf_set_type(sc->tp, bp, XFS_BLFT_RTBITMAP_BUF);
+	*bpp = bp;
+	return 0;
 }
 
 /* Repair the realtime bitmap. */
@@ -221,8 +244,12 @@  xrep_rtbitmap(
 	xfs_fileoff_t		bmp_bytes;
 	int			error;
 
-	/* We require the realtime rmapbt to rebuild anything. */
-	if (!xfs_sb_version_hasrtrmapbt(&sc->mp->m_sb))
+	/*
+	 * We require the realtime rmapbt and atomic file updates to rebuild
+	 * anything.
+	 */
+	if (!xfs_sb_version_hasrtrmapbt(&sc->mp->m_sb) ||
+	    !xfs_sb_version_hasatomicswap(&sc->mp->m_sb))
 		return -EOPNOTSUPP;
 
 	bmp_bytes = XFS_FSB_TO_B(sc->mp, sc->mp->m_sb.sb_rbmblocks);
@@ -240,8 +267,17 @@  xrep_rtbitmap(
 	if (error)
 		goto out;
 
+	/*
+	 * Trylock the temporary file.  We had better be the only ones holding
+	 * onto this inode...
+	 */
+	if (!xfs_ilock_nowait(sc->tempip, XFS_ILOCK_EXCL))
+		return -EAGAIN;
+	sc->temp_ilock_flags = XFS_ILOCK_EXCL;
+
 	/* Make sure we have space allocated for the entire bitmap file. */
 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
+	xfs_trans_ijoin(sc->tp, sc->tempip, 0);
 	error = xrep_fallocate(sc, 0, sc->mp->m_sb.sb_rbmblocks);
 	if (error)
 		goto out;
@@ -249,6 +285,12 @@  xrep_rtbitmap(
 	/* Copy the bitmap file that we generated. */
 	error = xrep_set_file_contents(sc, xrep_rtbitmap_get_buf, rb.bmpfile,
 			bmp_bytes);
+	if (error)
+		goto out;
+
+	/* Now swap the extents. */
+	error = xfs_swapext_atomic(&sc->tp, sc->ip, sc->tempip, XFS_DATA_FORK,
+			0, 0, sc->mp->m_sb.sb_rbmblocks, 0);
 out:
 	fput(rb.bmpfile);
 	return error;
diff --git a/fs/xfs/scrub/rtsummary.c b/fs/xfs/scrub/rtsummary.c
index e2b4638fa7cc..ccb220c184f1 100644
--- a/fs/xfs/scrub/rtsummary.c
+++ b/fs/xfs/scrub/rtsummary.c
@@ -20,6 +20,7 @@ 
 #include "scrub/common.h"
 #include "scrub/trace.h"
 #include "scrub/xfile.h"
+#include "scrub/repair.h"
 
 /*
  * Realtime Summary
@@ -61,6 +62,17 @@  xchk_setup_rtsummary(
 	struct xfs_mount	*mp = sc->mp;
 	int			error;
 
+#ifdef CONFIG_XFS_ONLINE_REPAIR
+	if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
+		if (!xfs_sb_version_hasatomicswap(&sc->mp->m_sb))
+			return -EOPNOTSUPP;
+
+		error = xrep_create_tempfile(sc, S_IFREG);
+		if (error)
+			return error;
+	}
+#endif
+
 	error = xchk_setup_fs(sc, ip);
 	if (error)
 		return error;
diff --git a/fs/xfs/scrub/rtsummary_repair.c b/fs/xfs/scrub/rtsummary_repair.c
index 78814b6a9c71..9c1fd759b730 100644
--- a/fs/xfs/scrub/rtsummary_repair.c
+++ b/fs/xfs/scrub/rtsummary_repair.c
@@ -16,6 +16,7 @@ 
 #include "xfs_inode.h"
 #include "xfs_bit.h"
 #include "xfs_bmap.h"
+#include "xfs_swapext.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
@@ -28,7 +29,29 @@  xrep_rtsum_get_buf(
 	xfs_fileoff_t		off,
 	struct xfs_buf		**bpp)
 {
-	return xfs_rtbuf_get(sc->mp, sc->tp, off, 1, bpp);
+	struct xfs_bmbt_irec	map;
+	struct xfs_buf		*bp;
+	struct xfs_mount	*mp = sc->mp;
+	int			nmap = 1;
+	int			error;
+
+	error = xfs_bmapi_read(sc->tempip, off, 1, &map, &nmap,
+			XFS_DATA_FORK);
+	if (error)
+		return error;
+
+	if (nmap == 0 || !xfs_bmap_is_real_extent(&map))
+		return -EFSCORRUPTED;
+
+	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
+			XFS_FSB_TO_DADDR(mp, map.br_startblock),
+			mp->m_bsize, 0, &bp, &xfs_rtbuf_ops);
+	if (error)
+		return error;
+
+	xfs_trans_buf_set_type(sc->tp, bp, XFS_BLFT_RTSUMMARY_BUF);
+	*bpp = bp;
+	return 0;
 }
 
 /* Repair the realtime summary. */
@@ -38,18 +61,37 @@  xrep_rtsummary(
 {
 	int			error;
 
+	/* We require atomic file swap to be able to fix rt summaries. */
+	if (!xfs_sb_version_hasatomicswap(&sc->mp->m_sb))
+		return -EOPNOTSUPP;
+
 	/* Make sure any problems with the fork are fixed. */
 	error = xrep_metadata_inode_forks(sc);
 	if (error)
 		return error;
 
+	/*
+	 * Trylock the temporary file.  We had better be the only ones holding
+	 * onto this inode...
+	 */
+	if (!xfs_ilock_nowait(sc->tempip, XFS_ILOCK_EXCL))
+		return -EAGAIN;
+	sc->temp_ilock_flags = XFS_ILOCK_EXCL;
+
 	/* Make sure we have space allocated for the entire summary file. */
 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
+	xfs_trans_ijoin(sc->tp, sc->tempip, 0);
 	error = xrep_fallocate(sc, 0, XFS_B_TO_FSB(sc->mp, sc->mp->m_rsumsize));
 	if (error)
 		return error;
 
 	/* Copy the rtsummary file that we generated. */
-	return xrep_set_file_contents(sc, xrep_rtsum_get_buf, sc->xfile,
+	error = xrep_set_file_contents(sc, xrep_rtsum_get_buf, sc->xfile,
 			sc->mp->m_rsumsize);
+	if (error)
+		return error;
+
+	/* Now swap the extents. */
+	return xfs_swapext_atomic(&sc->tp, sc->ip, sc->tempip, XFS_DATA_FORK,
+			0, 0, XFS_B_TO_FSB(sc->mp, sc->mp->m_rsumsize), 0);
 }