]> www.infradead.org Git - users/hch/misc.git/commitdiff
xfs: scrub the realtime group superblock
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:11:23 +0000 (21:11 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 24 Jul 2024 05:33:39 +0000 (22:33 -0700)
Enable scrubbing of realtime group superblocks.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/Makefile
fs/xfs/libxfs/xfs_fs.h
fs/xfs/scrub/common.c
fs/xfs/scrub/common.h
fs/xfs/scrub/health.c
fs/xfs/scrub/rgsuper.c [new file with mode: 0644]
fs/xfs/scrub/scrub.c
fs/xfs/scrub/scrub.h
fs/xfs/scrub/stats.c
fs/xfs/scrub/trace.h

index 1bc5c69dce2f2b33173d698e1bcb209bc944e02e..8df2bb43a81bd01b10b2d6ffe301be65cd3ee270 100644 (file)
@@ -189,6 +189,7 @@ xfs-y                               += $(addprefix scrub/, \
 xfs-$(CONFIG_XFS_ONLINE_SCRUB_STATS) += scrub/stats.o
 
 xfs-$(CONFIG_XFS_RT)           += $(addprefix scrub/, \
+                                  rgsuper.o \
                                   rtbitmap.o \
                                   rtsummary.o \
                                   )
index 7f457ee948fe3f6cb6d15e46a736e5b0525a5334..0dfc277d664992387654163032b436037cb76d29 100644 (file)
@@ -736,9 +736,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_HEALTHY 27      /* everything checked out ok */
 #define XFS_SCRUB_TYPE_DIRTREE 28      /* directory tree structure */
 #define XFS_SCRUB_TYPE_METAPATH        29      /* metadata directory tree paths */
+#define XFS_SCRUB_TYPE_RGSUPER 30      /* realtime superblock */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR      30
+#define XFS_SCRUB_TYPE_NR      31
 
 /*
  * This special type code only applies to the vectored scrub implementation.
index 79267c79e7a4f0ca94a1462dda1533ff40b090ee..86200f1a6fa0052bbb6009b0f94a67a2c0dfa8a6 100644 (file)
@@ -34,6 +34,7 @@
 #include "xfs_quota.h"
 #include "xfs_exchmaps.h"
 #include "xfs_rtbitmap.h"
+#include "xfs_rtgroup.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
@@ -121,6 +122,17 @@ xchk_process_error(
                        XFS_SCRUB_OFLAG_CORRUPT, __return_address);
 }
 
+bool
+xchk_process_rt_error(
+       struct xfs_scrub        *sc,
+       xfs_rgnumber_t          rgno,
+       xfs_rgblock_t           rgbno,
+       int                     *error)
+{
+       return __xchk_process_error(sc, rgno, rgbno, error,
+                       XFS_SCRUB_OFLAG_CORRUPT, __return_address);
+}
+
 bool
 xchk_xref_process_error(
        struct xfs_scrub        *sc,
@@ -132,6 +144,17 @@ xchk_xref_process_error(
                        XFS_SCRUB_OFLAG_XFAIL, __return_address);
 }
 
+bool
+xchk_xref_process_rt_error(
+       struct xfs_scrub        *sc,
+       xfs_rgnumber_t          rgno,
+       xfs_rgblock_t           rgbno,
+       int                     *error)
+{
+       return __xchk_process_error(sc, rgno, rgbno, error,
+                       XFS_SCRUB_OFLAG_XFAIL, __return_address);
+}
+
 /* Check for operational errors for a file offset. */
 static bool
 __xchk_fblock_process_error(
@@ -703,6 +726,7 @@ xchk_rt_init(
                                         XCHK_RTLOCK_BITMAP_SHARED)) < 2);
        ASSERT(hweight32(rtlock_flags & (XCHK_RTLOCK_SUMMARY |
                                         XCHK_RTLOCK_SUMMARY_SHARED)) < 2);
+       ASSERT(sr->rtg == NULL);
 
        if (rtlock_flags & XCHK_RTLOCK_BITMAP)
                xfs_ilock(sc->mp->m_rbmip, XFS_ILOCK_EXCL);
@@ -726,6 +750,8 @@ xchk_rt_unlock(
        struct xfs_scrub        *sc,
        struct xchk_rt          *sr)
 {
+       ASSERT(sr->rtg == NULL);
+
        if (!sr->rtlock_flags)
                return;
 
@@ -753,6 +779,68 @@ xchk_rt_unlock_rtbitmap(
        sc->sr.rtlock_flags &= ~XCHK_RTLOCK_BITMAP_SHARED;
 }
 
+#ifdef CONFIG_XFS_RT
+/*
+ * For scrubbing a realtime group, grab all the in-core resources we'll need to
+ * check the metadata, which means taking the ILOCK of the realtime group's
+ * metadata inodes.  Callers must not join these inodes to the transaction with
+ * non-zero lockflags or concurrency problems will result.  The @rtglock_flags
+ * argument takes XFS_RTGLOCK_* flags.
+ */
+int
+xchk_rtgroup_init(
+       struct xfs_scrub        *sc,
+       xfs_rgnumber_t          rgno,
+       struct xchk_rt          *sr,
+       unsigned int            rtglock_flags)
+{
+       ASSERT(sr->rtg == NULL);
+       ASSERT(sr->rtlock_flags == 0);
+
+       sr->rtg = xfs_rtgroup_get(sc->mp, rgno);
+       if (!sr->rtg)
+               return -ENOENT;
+
+       xfs_rtgroup_lock(NULL, sr->rtg, rtglock_flags);
+       sr->rtlock_flags = rtglock_flags;
+       return 0;
+}
+
+/*
+ * Unlock the realtime group.  This must be done /after/ committing (or
+ * cancelling) the scrub transaction.
+ */
+void
+xchk_rtgroup_unlock(
+       struct xfs_scrub        *sc,
+       struct xchk_rt          *sr)
+{
+       ASSERT(sr->rtg != NULL);
+
+       if (sr->rtlock_flags) {
+               xfs_rtgroup_unlock(sr->rtg, sr->rtlock_flags);
+               sr->rtlock_flags = 0;
+       }
+}
+
+/*
+ * Unlock the realtime group and release its resources.  This must be done
+ * /after/ committing (or cancelling) the scrub transaction.
+ */
+void
+xchk_rtgroup_free(
+       struct xfs_scrub        *sc,
+       struct xchk_rt          *sr)
+{
+       ASSERT(sr->rtg != NULL);
+
+       xchk_rtgroup_unlock(sc, sr);
+
+       xfs_rtgroup_put(sr->rtg);
+       sr->rtg = NULL;
+}
+#endif /* CONFIG_XFS_RT */
+
 /* Per-scrubber setup functions */
 
 void
index a5defe0defbdf66ecc15ff9781f60d74cd654d6b..df8388696176b5be26eba3b630a74fb1b77e9ab9 100644 (file)
@@ -12,11 +12,15 @@ void xchk_trans_cancel(struct xfs_scrub *sc);
 
 bool xchk_process_error(struct xfs_scrub *sc, xfs_agnumber_t agno,
                xfs_agblock_t bno, int *error);
+bool xchk_process_rt_error(struct xfs_scrub *sc, xfs_rgnumber_t rgno,
+               xfs_rgblock_t rgbno, int *error);
 bool xchk_fblock_process_error(struct xfs_scrub *sc, int whichfork,
                xfs_fileoff_t offset, int *error);
 
 bool xchk_xref_process_error(struct xfs_scrub *sc,
                xfs_agnumber_t agno, xfs_agblock_t bno, int *error);
+bool xchk_xref_process_rt_error(struct xfs_scrub *sc,
+               xfs_rgnumber_t rgno, xfs_rgblock_t rgbno, int *error);
 bool xchk_fblock_xref_process_error(struct xfs_scrub *sc,
                int whichfork, xfs_fileoff_t offset, int *error);
 
@@ -53,6 +57,11 @@ int xchk_checkpoint_log(struct xfs_mount *mp);
 bool xchk_should_check_xref(struct xfs_scrub *sc, int *error,
                           struct xfs_btree_cur **curpp);
 
+static inline int xchk_setup_nothing(struct xfs_scrub *sc)
+{
+       return -ENOENT;
+}
+
 /* Setup functions */
 int xchk_setup_agheader(struct xfs_scrub *sc);
 int xchk_setup_fs(struct xfs_scrub *sc);
@@ -72,17 +81,11 @@ int xchk_setup_metapath(struct xfs_scrub *sc);
 #ifdef CONFIG_XFS_RT
 int xchk_setup_rtbitmap(struct xfs_scrub *sc);
 int xchk_setup_rtsummary(struct xfs_scrub *sc);
+int xchk_setup_rgsuperblock(struct xfs_scrub *sc);
 #else
-static inline int
-xchk_setup_rtbitmap(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
-static inline int
-xchk_setup_rtsummary(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
+# define xchk_setup_rtbitmap           xchk_setup_nothing
+# define xchk_setup_rtsummary          xchk_setup_nothing
+# define xchk_setup_rgsuperblock       xchk_setup_nothing
 #endif
 #ifdef CONFIG_XFS_QUOTA
 int xchk_ino_dqattach(struct xfs_scrub *sc);
@@ -94,16 +97,8 @@ xchk_ino_dqattach(struct xfs_scrub *sc)
 {
        return 0;
 }
-static inline int
-xchk_setup_quota(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
-static inline int
-xchk_setup_quotacheck(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
+# define xchk_setup_quota              xchk_setup_nothing
+# define xchk_setup_quotacheck         xchk_setup_nothing
 #endif
 int xchk_setup_fscounters(struct xfs_scrub *sc);
 int xchk_setup_nlinks(struct xfs_scrub *sc);
@@ -147,6 +142,17 @@ void xchk_rt_init(struct xfs_scrub *sc, struct xchk_rt *sr,
                unsigned int xchk_rtlock_flags);
 void xchk_rt_unlock(struct xfs_scrub *sc, struct xchk_rt *sr);
 void xchk_rt_unlock_rtbitmap(struct xfs_scrub *sc);
+
+#ifdef CONFIG_XFS_RT
+int xchk_rtgroup_init(struct xfs_scrub *sc, xfs_rgnumber_t rgno,
+               struct xchk_rt *sr, unsigned int rtglock_flags);
+void xchk_rtgroup_unlock(struct xfs_scrub *sc, struct xchk_rt *sr);
+void xchk_rtgroup_free(struct xfs_scrub *sc, struct xchk_rt *sr);
+#else
+# define xchk_rtgroup_init(sc, rgno, sr, lockflags)    (-ENOSYS)
+# define xchk_rtgroup_free(sc, sr)                     ((void)0)
+#endif /* CONFIG_XFS_RT */
+
 int xchk_ag_read_headers(struct xfs_scrub *sc, xfs_agnumber_t agno,
                struct xchk_ag *sa);
 void xchk_ag_btcur_free(struct xchk_ag *sa);
index c1e87cc00531b5c362d6d9ed861db1887f5cb86e..83c52bb0dc7db5baf700a60eff916dd6c47c4b9a 100644 (file)
@@ -112,6 +112,7 @@ static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = {
        [XFS_SCRUB_TYPE_NLINKS]         = { XHG_FS,  XFS_SICK_FS_NLINKS },
        [XFS_SCRUB_TYPE_DIRTREE]        = { XHG_INO, XFS_SICK_INO_DIRTREE },
        [XFS_SCRUB_TYPE_METAPATH]       = { XHG_FS,  XFS_SICK_FS_METAPATH },
+       [XFS_SCRUB_TYPE_RGSUPER]        = { XHG_RTGROUP, XFS_SICK_RG_SUPER },
 };
 
 /* Return the health status mask for this scrub type. */
diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c
new file mode 100644 (file)
index 0000000..187155a
--- /dev/null
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_rtgroup.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+
+/* Set us up with a transaction and an empty context. */
+int
+xchk_setup_rgsuperblock(
+       struct xfs_scrub        *sc)
+{
+       return xchk_trans_alloc(sc, 0);
+}
+
+/* Cross-reference with the other rt metadata. */
+STATIC void
+xchk_rgsuperblock_xref(
+       struct xfs_scrub        *sc)
+{
+       struct xfs_mount        *mp = sc->mp;
+       xfs_rgnumber_t          rgno = sc->sr.rtg->rtg_rgno;
+       xfs_rtblock_t           rtbno;
+
+       if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+               return;
+
+       rtbno = xfs_rgbno_to_rtb(mp, rgno, 0);
+       xchk_xref_is_used_rt_space(sc, rtbno, 1);
+}
+
+int
+xchk_rgsuperblock(
+       struct xfs_scrub        *sc)
+{
+       xfs_rgnumber_t          rgno = sc->sm->sm_agno;
+       int                     error;
+
+       /*
+        * Only rtgroup 0 has a superblock.  We may someday want to use higher
+        * rgno for other functions, similar to what we do with the primary
+        * super scrub function.
+        */
+       if (rgno != 0)
+               return -ENOENT;
+
+       /*
+        * Grab an active reference to the rtgroup structure.  If we can't get
+        * it, we're racing with something that's tearing down the group, so
+        * signal that the group no longer exists.  Take the rtbitmap in shared
+        * mode so that the group can't change while we're doing things.
+        */
+       error = xchk_rtgroup_init(sc, rgno, &sc->sr, XFS_RTGLOCK_BITMAP_SHARED);
+       if (error)
+               return error;
+
+       /*
+        * Since we already validated the rt superblock at mount time, we don't
+        * need to check its contents again.  All we need is to cross-reference.
+        */
+       xchk_rgsuperblock_xref(sc);
+       return 0;
+}
index 00f545ea4458cc43774964037d45be3c32bb81eb..f341603278c61e381b8bcbb8c3190d7340318f8a 100644 (file)
@@ -225,7 +225,10 @@ xchk_teardown(
                        xfs_trans_cancel(sc->tp);
                sc->tp = NULL;
        }
-       xchk_rt_unlock(sc, &sc->sr);
+       if (sc->sr.rtg)
+               xchk_rtgroup_free(sc, &sc->sr);
+       else
+               xchk_rt_unlock(sc, &sc->sr);
        if (sc->ip) {
                if (sc->ilock_flags)
                        xchk_iunlock(sc, sc->ilock_flags);
@@ -450,6 +453,13 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
                .has    = xfs_has_metadir,
                .repair = xrep_metapath,
        },
+       [XFS_SCRUB_TYPE_RGSUPER] = {    /* realtime group superblock */
+               .type   = ST_RTGROUP,
+               .setup  = xchk_setup_rgsuperblock,
+               .scrub  = xchk_rgsuperblock,
+               .has    = xfs_has_rtsb,
+               .repair = xrep_notsupported,
+       },
 };
 
 static int
@@ -499,6 +509,14 @@ xchk_validate_inputs(
                break;
        case ST_GENERIC:
                break;
+       case ST_RTGROUP:
+               if (sm->sm_ino || sm->sm_gen)
+                       goto out;
+               if (!xfs_has_rtgroups(mp) && sm->sm_agno != 0)
+                       goto out;
+               if (xfs_has_rtgroups(mp) && sm->sm_agno >= mp->m_sb.sb_rgcount)
+                       goto out;
+               break;
        default:
                goto out;
        }
index f8fc1d707b6645091600af029e4a2efc7eaa93bb..e37fe1cda6b389b9c7bfea2af8e37c40b4088377 100644 (file)
@@ -74,6 +74,7 @@ enum xchk_type {
        ST_FS,          /* per-FS metadata */
        ST_INODE,       /* per-inode metadata */
        ST_GENERIC,     /* determined by the scrubber */
+       ST_RTGROUP,     /* rtgroup metadata */
 };
 
 struct xchk_meta_ops {
@@ -120,7 +121,13 @@ struct xchk_ag {
 
 /* Inode lock state for the RT volume. */
 struct xchk_rt {
-       /* XCHK_RTLOCK_* lock state */
+       /* incore rtgroup, if applicable */
+       struct xfs_rtgroup      *rtg;
+
+       /*
+        * XCHK_RTLOCK_* lock state if rtg == NULL, or XFS_RTGLOCK_* lock state
+        * if rtg != NULL.
+        */
        unsigned int            rtlock_flags;
 };
 
@@ -241,6 +248,11 @@ xchk_should_terminate(
        return false;
 }
 
+static inline int xchk_nothing(struct xfs_scrub *sc)
+{
+       return -ENOENT;
+}
+
 /* Metadata scrubbers */
 int xchk_tester(struct xfs_scrub *sc);
 int xchk_superblock(struct xfs_scrub *sc);
@@ -264,32 +276,18 @@ int xchk_metapath(struct xfs_scrub *sc);
 #ifdef CONFIG_XFS_RT
 int xchk_rtbitmap(struct xfs_scrub *sc);
 int xchk_rtsummary(struct xfs_scrub *sc);
+int xchk_rgsuperblock(struct xfs_scrub *sc);
 #else
-static inline int
-xchk_rtbitmap(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
-static inline int
-xchk_rtsummary(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
+# define xchk_rtbitmap         xchk_nothing
+# define xchk_rtsummary                xchk_nothing
+# define xchk_rgsuperblock     xchk_nothing
 #endif
 #ifdef CONFIG_XFS_QUOTA
 int xchk_quota(struct xfs_scrub *sc);
 int xchk_quotacheck(struct xfs_scrub *sc);
 #else
-static inline int
-xchk_quota(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
-static inline int
-xchk_quotacheck(struct xfs_scrub *sc)
-{
-       return -ENOENT;
-}
+# define xchk_quota            xchk_nothing
+# define xchk_quotacheck       xchk_nothing
 #endif
 int xchk_fscounters(struct xfs_scrub *sc);
 int xchk_nlinks(struct xfs_scrub *sc);
index edcd02dc2e62c0dd465fe94095bbc0e9f2d26f96..a476c7b2ab75973ba5bd6754bebd7f493181ecf7 100644 (file)
@@ -81,6 +81,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = {
        [XFS_SCRUB_TYPE_NLINKS]         = "nlinks",
        [XFS_SCRUB_TYPE_DIRTREE]        = "dirtree",
        [XFS_SCRUB_TYPE_METAPATH]       = "metapath",
+       [XFS_SCRUB_TYPE_RGSUPER]        = "rgsuper",
 };
 
 /* Format the scrub stats into a text buffer, similar to pcp style. */
index fe901b9138b4bd97608701fdf903d25b51bca507..d4d0e8ceeeb7b8ebb4a9b9975a34d2ebde514ad4 100644 (file)
@@ -71,6 +71,7 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_HEALTHY);
 TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_DIRTREE);
 TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_BARRIER);
 TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_METAPATH);
+TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_RGSUPER);
 
 #define XFS_SCRUB_TYPE_STRINGS \
        { XFS_SCRUB_TYPE_PROBE,         "probe" }, \
@@ -103,7 +104,8 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_METAPATH);
        { XFS_SCRUB_TYPE_HEALTHY,       "healthy" }, \
        { XFS_SCRUB_TYPE_DIRTREE,       "dirtree" }, \
        { XFS_SCRUB_TYPE_BARRIER,       "barrier" }, \
-       { XFS_SCRUB_TYPE_METAPATH,      "metapath" }
+       { XFS_SCRUB_TYPE_METAPATH,      "metapath" }, \
+       { XFS_SCRUB_TYPE_RGSUPER,       "rgsuper" }
 
 #define XFS_SCRUB_FLAG_STRINGS \
        { XFS_SCRUB_IFLAG_REPAIR,               "repair" }, \