xfs: create subordinate scrub contexts for xchk_metadata_inode_subtype
authorDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:55:06 +0000 (14:55 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:59:00 +0000 (14:59 -0700)
When a file-based metadata structure is being scrubbed in
xchk_metadata_inode_subtype, we should create an entirely new scrub
context so that each scrubber doesn't trip over another's buffers.

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

index a2da2bef509aaad3c7fb4b067f3c49137b838d3e..48302532d10d136e099a05c7d2cf60987788d1f1 100644 (file)
@@ -1203,27 +1203,12 @@ xchk_metadata_inode_subtype(
        struct xfs_scrub        *sc,
        unsigned int            scrub_type)
 {
-       __u32                   smtype = sc->sm->sm_type;
-       unsigned int            sick_mask = sc->sick_mask;
+       struct xfs_scrub_subord *sub;
        int                     error;
 
-       sc->sm->sm_type = scrub_type;
-
-       switch (scrub_type) {
-       case XFS_SCRUB_TYPE_INODE:
-               error = xchk_inode(sc);
-               break;
-       case XFS_SCRUB_TYPE_BMBTD:
-               error = xchk_bmap_data(sc);
-               break;
-       default:
-               ASSERT(0);
-               error = -EFSCORRUPTED;
-               break;
-       }
-
-       sc->sick_mask = sick_mask;
-       sc->sm->sm_type = smtype;
+       sub = xchk_scrub_create_subord(sc, scrub_type);
+       error = sub->sc.ops->scrub(&sub->sc);
+       xchk_scrub_free_subord(sub);
        return error;
 }
 
index 369f0430e4ba1805d29861b3ca5874a9c5b9a9c1..b6aff89679d587afa03f2036ea99bddc8bd848a7 100644 (file)
@@ -1009,55 +1009,27 @@ xrep_metadata_inode_subtype(
        struct xfs_scrub        *sc,
        unsigned int            scrub_type)
 {
-       __u32                   smtype = sc->sm->sm_type;
-       __u32                   smflags = sc->sm->sm_flags;
-       unsigned int            sick_mask = sc->sick_mask;
+       struct xfs_scrub_subord *sub;
        int                     error;
 
        /*
-        * Let's see if the inode needs repair.  We're going to open-code calls
-        * to the scrub and repair functions so that we can hang on to the
+        * Let's see if the inode needs repair.  Use a subordinate scrub context
+        * to call the scrub and repair functions so that we can hang on to the
         * resources that we already acquired instead of using the standard
         * setup/teardown routines.
         */
-       sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
-       sc->sm->sm_type = scrub_type;
-
-       switch (scrub_type) {
-       case XFS_SCRUB_TYPE_INODE:
-               error = xchk_inode(sc);
-               break;
-       case XFS_SCRUB_TYPE_BMBTD:
-               error = xchk_bmap_data(sc);
-               break;
-       case XFS_SCRUB_TYPE_BMBTA:
-               error = xchk_bmap_attr(sc);
-               break;
-       default:
-               ASSERT(0);
-               error = -EFSCORRUPTED;
-       }
+       sub = xchk_scrub_create_subord(sc, scrub_type);
+       error = sub->sc.ops->scrub(&sub->sc);
        if (error)
                goto out;
-
-       if (!xrep_will_attempt(sc))
+       if (!xrep_will_attempt(&sub->sc))
                goto out;
 
        /*
         * Repair some part of the inode.  This will potentially join the inode
         * to the transaction.
         */
-       switch (scrub_type) {
-       case XFS_SCRUB_TYPE_INODE:
-               error = xrep_inode(sc);
-               break;
-       case XFS_SCRUB_TYPE_BMBTD:
-               error = xrep_bmap(sc, XFS_DATA_FORK, false);
-               break;
-       case XFS_SCRUB_TYPE_BMBTA:
-               error = xrep_bmap(sc, XFS_ATTR_FORK, false);
-               break;
-       }
+       error = sub->sc.ops->repair(&sub->sc);
        if (error)
                goto out;
 
@@ -1066,10 +1038,10 @@ xrep_metadata_inode_subtype(
         * that the inode will not be joined to the transaction when we exit
         * the function.
         */
-       error = xfs_defer_finish(&sc->tp);
+       error = xfs_defer_finish(&sub->sc.tp);
        if (error)
                goto out;
-       error = xfs_trans_roll(&sc->tp);
+       error = xfs_trans_roll(&sub->sc.tp);
        if (error)
                goto out;
 
@@ -1077,31 +1049,18 @@ xrep_metadata_inode_subtype(
         * Clear the corruption flags and re-check the metadata that we just
         * repaired.
         */
-       sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
-
-       switch (scrub_type) {
-       case XFS_SCRUB_TYPE_INODE:
-               error = xchk_inode(sc);
-               break;
-       case XFS_SCRUB_TYPE_BMBTD:
-               error = xchk_bmap_data(sc);
-               break;
-       case XFS_SCRUB_TYPE_BMBTA:
-               error = xchk_bmap_attr(sc);
-               break;
-       }
+       sub->sc.sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
+       error = sub->sc.ops->scrub(&sub->sc);
        if (error)
                goto out;
 
        /* If corruption persists, the repair has failed. */
-       if (xchk_needs_repair(sc->sm)) {
+       if (xchk_needs_repair(sub->sc.sm)) {
                error = -EFSCORRUPTED;
                goto out;
        }
 out:
-       sc->sick_mask = sick_mask;
-       sc->sm->sm_type = smtype;
-       sc->sm->sm_flags = smflags;
+       xchk_scrub_free_subord(sub);
        return error;
 }
 
index 301d5b753fdd54207a10014b6f8527f95beed086..ebb06838c31be34fb7bb4f6eb5d1127e2ea983d4 100644 (file)
@@ -177,6 +177,39 @@ xchk_fsgates_disable(
 }
 #undef FSGATES_MASK
 
+/* Free the resources associated with a scrub subtype. */
+void
+xchk_scrub_free_subord(
+       struct xfs_scrub_subord *sub)
+{
+       struct xfs_scrub        *sc = sub->parent_sc;
+
+       ASSERT(sc->ip == sub->sc.ip);
+       ASSERT(sc->orphanage == sub->sc.orphanage);
+       ASSERT(sc->tempip == sub->sc.tempip);
+
+       sc->sm->sm_type = sub->old_smtype;
+       sc->sm->sm_flags = sub->old_smflags |
+                               (sc->sm->sm_flags & XFS_SCRUB_FLAGS_OUT);
+       sc->tp = sub->sc.tp;
+
+       if (sub->sc.buf) {
+               if (sub->sc.buf_cleanup)
+                       sub->sc.buf_cleanup(sub->sc.buf);
+               kvfree(sub->sc.buf);
+       }
+       if (sub->sc.xmbtp)
+               xmbuf_free(sub->sc.xmbtp);
+       if (sub->sc.xfile)
+               xfile_destroy(sub->sc.xfile);
+
+       sc->ilock_flags = sub->sc.ilock_flags;
+       sc->orphanage_ilock_flags = sub->sc.orphanage_ilock_flags;
+       sc->temp_ilock_flags = sub->sc.temp_ilock_flags;
+
+       kfree(sub);
+}
+
 /* Free all the resources and finish the transactions. */
 STATIC int
 xchk_teardown(
@@ -505,6 +538,36 @@ static inline void xchk_postmortem(struct xfs_scrub *sc)
 }
 #endif /* CONFIG_XFS_ONLINE_REPAIR */
 
+/*
+ * Create a new scrub context from an existing one, but with a different scrub
+ * type.
+ */
+struct xfs_scrub_subord *
+xchk_scrub_create_subord(
+       struct xfs_scrub        *sc,
+       unsigned int            subtype)
+{
+       struct xfs_scrub_subord *sub;
+
+       sub = kzalloc(sizeof(*sub), XCHK_GFP_FLAGS);
+       if (!sub)
+               return ERR_PTR(-ENOMEM);
+
+       sub->old_smtype = sc->sm->sm_type;
+       sub->old_smflags = sc->sm->sm_flags;
+       sub->parent_sc = sc;
+       memcpy(&sub->sc, sc, sizeof(struct xfs_scrub));
+       sub->sc.ops = &meta_scrub_ops[subtype];
+       sub->sc.sm->sm_type = subtype;
+       sub->sc.sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
+       sub->sc.buf = NULL;
+       sub->sc.buf_cleanup = NULL;
+       sub->sc.xfile = NULL;
+       sub->sc.xmbtp = NULL;
+
+       return sub;
+}
+
 /* Dispatch metadata scrubbing. */
 int
 xfs_scrub_metadata(
index 7abe498f7a4615de31a05701d6f29dc54993bfb2..54a4242bc79cf358c4b5f5cf829cabcf97c166a4 100644 (file)
@@ -156,6 +156,17 @@ struct xfs_scrub {
  */
 #define XREP_FSGATES_ALL       (XREP_FSGATES_EXCHANGE_RANGE)
 
+struct xfs_scrub_subord {
+       struct xfs_scrub        sc;
+       struct xfs_scrub        *parent_sc;
+       unsigned int            old_smtype;
+       unsigned int            old_smflags;
+};
+
+struct xfs_scrub_subord *xchk_scrub_create_subord(struct xfs_scrub *sc,
+               unsigned int subtype);
+void xchk_scrub_free_subord(struct xfs_scrub_subord *sub);
+
 /* Metadata scrubbers */
 int xchk_tester(struct xfs_scrub *sc);
 int xchk_superblock(struct xfs_scrub *sc);