]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: online repair of realtime file bmaps
authorDarrick J. Wong <djwong@kernel.org>
Thu, 21 Nov 2024 00:20:39 +0000 (16:20 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 23 Dec 2024 21:06:08 +0000 (13:06 -0800)
Now that we have a reverse-mapping index of the realtime device, we can
rebuild the data fork forward-mappings of any realtime file.  Enhance
the existing bmbt repair code to walk the rtrmap btrees to gather this
information.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/scrub/bmap_repair.c
fs/xfs/scrub/repair.c
fs/xfs/scrub/repair.h

index 141d36f1da9a7157e3e2309c983bd191e4d912cd..fd64bdf4e13887036e5bb1101d44ff8794a8fdf3 100644 (file)
 #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"
 #include "xfs_ag.h"
 #include "xfs_reflink.h"
+#include "xfs_rtgroup.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
@@ -359,6 +361,112 @@ xrep_bmap_scan_ag(
        return error;
 }
 
+#ifdef CONFIG_XFS_RT
+/* Check for any obvious errors or conflicts in the file mapping. */
+STATIC int
+xrep_bmap_check_rtfork_rmap(
+       struct xfs_scrub                *sc,
+       struct xfs_btree_cur            *cur,
+       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 is within the rtgroup. */
+       if (!xfs_verify_rgbext(to_rtg(cur->bc_group), 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, cur, 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,
+                       xfs_rgbno_to_rtb(to_rtg(cur->bc_group),
+                               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_rtgroup(
+       struct xrep_bmap        *rb,
+       struct xfs_rtgroup      *rtg)
+{
+       struct xfs_scrub        *sc = rb->sc;
+       int                     error;
+
+       if (!xfs_has_rtrmapbt(sc->mp))
+               return 0;
+
+       error = xrep_rtgroup_init(sc, rtg, &sc->sr,
+                       XFS_RTGLOCK_RMAP | XFS_RTGLOCK_BITMAP_SHARED);
+       if (error)
+               return error;
+
+       error = xfs_rmap_query_all(sc->sr.rmap_cur, xrep_bmap_walk_rtrmap, rb);
+       xchk_rtgroup_btcur_free(&sc->sr);
+       xchk_rtgroup_free(sc, &sc->sr);
+       return error;
+}
+#else
+static inline int
+xrep_bmap_scan_rtgroup(struct xrep_bmap *rb, struct xfs_rtgroup *rtg)
+{
+       return -EFSCORRUPTED;
+}
+#endif
+
 /* Find the delalloc extents from the old incore extent tree. */
 STATIC int
 xrep_bmap_find_delalloc(
@@ -410,6 +518,22 @@ xrep_bmap_find_mappings(
        struct xfs_perag        *pag = NULL;
        int                     error = 0;
 
+       /*
+        * Iterate the rtrmaps for extents.  Metadata files never have content
+        * on the realtime device, so there's no need to scan them.
+        */
+       if (!xfs_is_metadir_inode(sc->ip)) {
+               struct xfs_rtgroup      *rtg = NULL;
+
+               while ((rtg = xfs_rtgroup_next(sc->mp, rtg))) {
+                       error = xrep_bmap_scan_rtgroup(rb, rtg);
+                       if (error) {
+                               xfs_rtgroup_rele(rtg);
+                               return error;
+                       }
+               }
+       }
+
        /* Iterate the rmaps for extents. */
        while ((pag = xfs_perag_next(sc->mp, pag))) {
                error = xrep_bmap_scan_ag(rb, pag);
@@ -754,10 +878,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;
 }
 
index e788e3032f8e335eaf6dbe39506434f86c224f93..18946dd46fa7458268390d614e86d969ded010e2 100644 (file)
@@ -37,6 +37,9 @@
 #include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_dir2.h"
+#include "xfs_rtrmap_btree.h"
+#include "xfs_rtbitmap.h"
+#include "xfs_rtgroup.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
@@ -955,6 +958,22 @@ xrep_ag_init(
 }
 
 #ifdef CONFIG_XFS_RT
+/* Initialize all the btree cursors for a RT repair. */
+static void
+xrep_rtgroup_btcur_init(
+       struct xfs_scrub        *sc,
+       struct xchk_rt          *sr)
+{
+       struct xfs_mount        *mp = sc->mp;
+
+       ASSERT(sr->rtg != NULL);
+
+       if (sc->sm->sm_type != XFS_SCRUB_TYPE_RTRMAPBT &&
+           (sr->rtlock_flags & XFS_RTGLOCK_RMAP) &&
+           xfs_has_rtrmapbt(mp))
+               sr->rmap_cur = xfs_rtrmapbt_init_cursor(sc->tp, sr->rtg);
+}
+
 /*
  * Given a reference to a rtgroup structure, lock rtgroup btree inodes and
  * create btree cursors.  Must only be called to repair a regular rt file.
@@ -973,6 +992,33 @@ xrep_rtgroup_init(
 
        /* Grab our own passive reference from the caller's ref. */
        sr->rtg = xfs_rtgroup_hold(rtg);
+       xrep_rtgroup_btcur_init(sc, sr);
+       return 0;
+}
+
+/* Ensure that all rt blocks in the given range are not marked free. */
+int
+xrep_require_rtext_inuse(
+       struct xfs_scrub        *sc,
+       xfs_rgblock_t           rgbno,
+       xfs_filblks_t           len)
+{
+       struct xfs_mount        *mp = sc->mp;
+       xfs_rtxnum_t            startrtx;
+       xfs_rtxnum_t            endrtx;
+       bool                    is_free = false;
+       int                     error;
+
+       startrtx = xfs_rgbno_to_rtx(mp, rgbno);
+       endrtx = xfs_rgbno_to_rtx(mp, rgbno + len - 1);
+
+       error = xfs_rtalloc_extent_is_free(sc->sr.rtg, sc->tp, startrtx,
+                       endrtx - startrtx + 1, &is_free);
+       if (error)
+               return error;
+       if (is_free)
+               return -EFSCORRUPTED;
+
        return 0;
 }
 #endif /* CONFIG_XFS_RT */
index b649da1a93eb8c07e9116f4e65c8d208b4a44d22..584135042d9aa9957e864a91af65d3532791d0fd 100644 (file)
@@ -110,6 +110,8 @@ int xrep_ag_init(struct xfs_scrub *sc, struct xfs_perag *pag,
 #ifdef CONFIG_XFS_RT
 int xrep_rtgroup_init(struct xfs_scrub *sc, struct xfs_rtgroup *rtg,
                struct xchk_rt *sr, unsigned int rtglock_flags);
+int xrep_require_rtext_inuse(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
+               xfs_filblks_t len);
 #else
 # define xrep_rtgroup_init(sc, rtg, sr, lockflags)     (-ENOSYS)
 #endif /* CONFIG_XFS_RT */