summaryrefslogtreecommitdiff
path: root/fs/xfs/scrub
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:17:59 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-12-15 17:29:17 -0800
commite74c55a50a140b6388185cc62bdcbe115c993cd0 (patch)
tree4c01a3402e5571cf58311298732a2304de3f2fd3 /fs/xfs/scrub
parent09b3c0d5935229e067369692c20a8a57e2581ed4 (diff)
xfs: online repair of realtime file bmaps
Repair the block mappings of realtime files. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs/scrub')
-rw-r--r--fs/xfs/scrub/bmap_repair.c101
-rw-r--r--fs/xfs/scrub/repair.c68
-rw-r--r--fs/xfs/scrub/repair.h9
3 files changed, 174 insertions, 4 deletions
diff --git a/fs/xfs/scrub/bmap_repair.c b/fs/xfs/scrub/bmap_repair.c
index 188370660efc..d3ed1c884353 100644
--- a/fs/xfs/scrub/bmap_repair.c
+++ b/fs/xfs/scrub/bmap_repair.c
@@ -24,6 +24,7 @@
#include "xfs_bmap_btree.h"
#include "xfs_rmap.h"
#include "xfs_rmap_btree.h"
+#include "xfs_rtrmap_btree.h"
#include "xfs_refcount.h"
#include "xfs_quota.h"
#include "xfs_ialloc.h"
@@ -256,6 +257,96 @@ xrep_bmap_scan_ag(
return error;
}
+/* Check for any obvious errors or conflicts in the file mapping. */
+STATIC int
+xrep_bmap_check_rtfork_rmap(
+ struct xfs_scrub *sc,
+ const struct xfs_rmap_irec *rec)
+{
+ /* xattr extents are never stored on realtime devices */
+ if (rec->rm_flags & XFS_RMAP_ATTR_FORK)
+ return -EFSCORRUPTED;
+
+ /* bmbt blocks are never stored on realtime devices */
+ if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
+ return -EFSCORRUPTED;
+
+ /* Data extents for non-rt files are never stored on the rt device. */
+ if (!XFS_IS_REALTIME_INODE(sc->ip))
+ return -EFSCORRUPTED;
+
+ /* Check the file offsets and physical extents. */
+ if (!xfs_verify_fileext(sc->mp, rec->rm_offset, rec->rm_blockcount))
+ return -EFSCORRUPTED;
+
+ /* Check that this fits in the rt volume. */
+ if (!xfs_verify_rtext(sc->mp, rec->rm_startblock, rec->rm_blockcount))
+ return -EFSCORRUPTED;
+
+ /* Make sure this isn't free space. */
+ return xrep_require_rtext_inuse(sc, rec->rm_startblock,
+ rec->rm_blockcount);
+}
+
+/* Record realtime extents that belong to this inode's fork. */
+STATIC int
+xrep_bmap_walk_rtrmap(
+ struct xfs_btree_cur *cur,
+ const struct xfs_rmap_irec *rec,
+ void *priv)
+{
+ struct xrep_bmap *rb = priv;
+ int error = 0;
+
+ if (xchk_should_terminate(rb->sc, &error))
+ return error;
+
+ /* Skip extents which are not owned by this inode and fork. */
+ if (rec->rm_owner != rb->sc->ip->i_ino)
+ return 0;
+
+ error = xrep_bmap_check_rtfork_rmap(rb->sc, rec);
+ if (error)
+ return error;
+
+ /*
+ * Record all blocks allocated to this file even if the extent isn't
+ * for the fork we're rebuilding so that we can reset di_nblocks later.
+ */
+ rb->nblocks += rec->rm_blockcount;
+
+ /* If this rmap isn't for the fork we want, we're done. */
+ if (rb->whichfork == XFS_DATA_FORK &&
+ (rec->rm_flags & XFS_RMAP_ATTR_FORK))
+ return 0;
+ if (rb->whichfork == XFS_ATTR_FORK &&
+ !(rec->rm_flags & XFS_RMAP_ATTR_FORK))
+ return 0;
+
+ return xrep_bmap_from_rmap(rb, rec->rm_offset, rec->rm_startblock,
+ rec->rm_blockcount,
+ rec->rm_flags & XFS_RMAP_UNWRITTEN);
+}
+
+/* Scan the realtime reverse mappings to build the new extent map. */
+STATIC int
+xrep_bmap_scan_rt(
+ struct xrep_bmap *rb)
+{
+ struct xfs_scrub *sc = rb->sc;
+ int error;
+
+ if (xrep_is_rtmeta_ino(sc, sc->ip->i_ino))
+ return 0;
+
+ xchk_rt_lock(sc, &sc->sr);
+ xrep_rt_btcur_init(sc, &sc->sr);
+ error = xfs_rmap_query_all(sc->sr.rmap_cur, xrep_bmap_walk_rtrmap, rb);
+ xchk_rt_btcur_free(&sc->sr);
+ xchk_rt_unlock(sc, &sc->sr);
+ return error;
+}
+
/*
* Collect block mappings for this fork of this inode and decide if we have
* enough space to rebuild. Caller is responsible for cleaning up the list if
@@ -270,6 +361,12 @@ xrep_bmap_find_mappings(
xfs_agnumber_t agno;
int error = 0;
+ if (xfs_has_realtime(sc->mp)) {
+ error = xrep_bmap_scan_rt(rb);
+ if (error)
+ return error;
+ }
+
/* Iterate the rmaps for extents. */
for_each_perag(sc->mp, agno, pag) {
error = xrep_bmap_scan_ag(rb, pag);
@@ -605,10 +702,6 @@ xrep_bmap_check_inputs(
return -EINVAL;
}
- /* Don't know how to rebuild realtime data forks. */
- if (XFS_IS_REALTIME_INODE(sc->ip))
- return -EOPNOTSUPP;
-
return 0;
}
diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c
index e3e7f75778d6..1b9649407fa1 100644
--- a/fs/xfs/scrub/repair.c
+++ b/fs/xfs/scrub/repair.c
@@ -38,6 +38,8 @@
#include "xfs_extfree_item.h"
#include "xfs_reflink.h"
#include "xfs_health.h"
+#include "xfs_rtrmap_btree.h"
+#include "xfs_rtalloc.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
@@ -2047,6 +2049,20 @@ xrep_ag_init(
return 0;
}
+/* Initialize all the btree cursors for a RT repair. */
+void
+xrep_rt_btcur_init(
+ struct xfs_scrub *sc,
+ struct xchk_rt *sr)
+{
+ struct xfs_mount *mp = sc->mp;
+
+ if (sc->sm->sm_type != XFS_SCRUB_TYPE_RTRMAPBT &&
+ xfs_has_rtrmapbt(mp))
+ sr->rmap_cur = xfs_rtrmapbt_init_cursor(mp, sc->tp,
+ mp->m_rrmapip);
+}
+
/* Reinitialize the per-AG block reservation for the AG we just fixed. */
int
xrep_reset_perag_resv(
@@ -2221,3 +2237,55 @@ xrep_dotdot_lookup(
return ino;
}
+
+#ifdef CONFIG_XFS_RT
+/* Ensure that all rt blocks in the given range are not marked free. */
+int
+xrep_require_rtext_inuse(
+ struct xfs_scrub *sc,
+ xfs_rtblock_t rtbno,
+ xfs_filblks_t len)
+{
+ struct xfs_mount *mp = sc->mp;
+ xfs_rtblock_t startext;
+ xfs_rtblock_t endext;
+ uint32_t mod;
+ bool is_free = false;
+ int error;
+
+ /* Round the starting rt extent down and the end rt extent up. */
+ startext = div_u64_rem(rtbno, mp->m_sb.sb_rextsize, &mod);
+ endext = div_u64_rem(rtbno + len - 1, mp->m_sb.sb_rextsize, &mod);
+
+ error = xfs_rtalloc_extent_is_free(mp, sc->tp, startext,
+ endext - startext + 1, &is_free);
+ if (error)
+ return error;
+ if (is_free)
+ return -EFSCORRUPTED;
+
+ return 0;
+}
+#endif
+
+/* Are we looking at a realtime metadata inode? */
+bool
+xrep_is_rtmeta_ino(
+ struct xfs_scrub *sc,
+ xfs_ino_t ino)
+{
+ /*
+ * All filesystems have rt bitmap and summary inodes, even if they
+ * don't have an rt section.
+ */
+ if (ino == sc->mp->m_rbmip->i_ino)
+ return true;
+ if (ino == sc->mp->m_rsumip->i_ino)
+ return true;
+
+ /* Newer rt metadata files are not guaranteed to exist */
+ if (sc->mp->m_rrmapip && ino == sc->mp->m_rrmapip->i_ino)
+ return true;
+
+ return false;
+}
diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h
index c198f7cd3f99..e9ded3aa25d8 100644
--- a/fs/xfs/scrub/repair.h
+++ b/fs/xfs/scrub/repair.h
@@ -89,6 +89,15 @@ int xrep_setup_ag_allocbt(struct xfs_scrub *sc);
void xrep_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa);
int xrep_ag_init(struct xfs_scrub *sc, struct xfs_perag *pag,
struct xchk_ag *sa);
+void xrep_rt_btcur_init(struct xfs_scrub *sc, struct xchk_rt *sr);
+
+#ifdef CONFIG_XFS_RT
+int xrep_require_rtext_inuse(struct xfs_scrub *sc, xfs_rtblock_t rtbno,
+ xfs_filblks_t len);
+#else
+# define xrep_require_rtext_inuse(sc, rtbno, len) (-ENOSYS)
+#endif
+bool xrep_is_rtmeta_ino(struct xfs_scrub *sc, xfs_ino_t ino);
/* Metadata revalidators */