xfs_repair: find and mark the rtrmapbt inodes
authorDarrick J. Wong <djwong@kernel.org>
Wed, 3 Jul 2024 21:22:24 +0000 (14:22 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 16 Jul 2024 22:49:21 +0000 (15:49 -0700)
Make sure that we find the realtime rmapbt inodes and mark them
appropriately, just in case we find a rogue inode claiming to be an
rtrmap, or garbage in the metadata directory tree.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
libfrog/bitmap.c
libfrog/bitmap.h
repair/dino_chunks.c
repair/dinode.c
repair/dir2.c
repair/incore.h
repair/rmap.c
repair/rmap.h
repair/scan.c

index 5af5ab8dd6b3bb853d2ff3e8bdfadff4fb8a850f..0308886d446ff2b554319fb734c73725b1195e85 100644 (file)
@@ -233,10 +233,9 @@ bitmap_set(
        return res;
 }
 
-#if 0  /* Unused, provided for completeness. */
 /* Clear a region of bits. */
-int
-bitmap_clear(
+static int
+__bitmap_clear(
        struct bitmap           *bmap,
        uint64_t                start,
        uint64_t                len)
@@ -251,8 +250,8 @@ bitmap_clear(
        uint64_t                new_length;
        struct avl64node        *node;
        int                     stat;
+       int                     ret = 0;
 
-       pthread_mutex_lock(&bmap->bt_lock);
        /* Find any existing nodes over that range. */
        avl64_findranges(bmap->bt_tree, start, start + len, &firstn, &lastn);
 
@@ -312,10 +311,24 @@ bitmap_clear(
        }
 
 out:
-       pthread_mutex_unlock(&bmap->bt_lock);
        return ret;
 }
-#endif
+
+/* Clear a region of bits. */
+int
+bitmap_clear(
+       struct bitmap           *bmap,
+       uint64_t                start,
+       uint64_t                length)
+{
+       int                     res;
+
+       pthread_mutex_lock(&bmap->bt_lock);
+       res = __bitmap_clear(bmap, start, length);
+       pthread_mutex_unlock(&bmap->bt_lock);
+
+       return res;
+}
 
 /* Iterate the set regions of this bitmap. */
 int
index 043b77eece65b3ffdf64ff0ccee883ca482afce5..47df0ad38467ced0eeb91cdb33e60a552ef09e34 100644 (file)
@@ -14,6 +14,7 @@ struct bitmap {
 int bitmap_alloc(struct bitmap **bmap);
 void bitmap_free(struct bitmap **bmap);
 int bitmap_set(struct bitmap *bmap, uint64_t start, uint64_t length);
+int bitmap_clear(struct bitmap *bmap, uint64_t start, uint64_t length);
 int bitmap_iterate(struct bitmap *bmap, int (*fn)(uint64_t, uint64_t, void *),
                void *arg);
 int bitmap_iterate_range(struct bitmap *bmap, uint64_t start, uint64_t length,
index c09dac20ac1af7c200ec5776f6a148b4e5915b70..fc882a49b94957a5fc55962b01923bda65e5e32a 100644 (file)
@@ -15,6 +15,8 @@
 #include "versions.h"
 #include "prefetch.h"
 #include "progress.h"
+#include "slab.h"
+#include "rmap.h"
 
 /*
  * validates inode block or chunk, returns # of good inodes
@@ -1003,6 +1005,17 @@ next_readbuf:
        _("would clear realtime summary inode %" PRIu64 "\n"),
                                                ino);
                                }
+                       } else if (is_rtrmap_inode(ino)) {
+                               rmap_avoid_check(mp);
+                               if (!no_modify)  {
+                                       do_warn(
+       _("cleared realtime rmap inode %" PRIu64 "\n"),
+                                               ino);
+                               } else  {
+                                       do_warn(
+       _("would clear realtime rmap inode %" PRIu64 "\n"),
+                                               ino);
+                               }
                        } else if (!no_modify)  {
                                do_warn(_("cleared inode %" PRIu64 "\n"),
                                        ino);
index 8d7962720639d27ca33a4dd0496e74262c49f9fd..5565d61e91531d1390ec100d374e03352368d702 100644 (file)
@@ -153,6 +153,9 @@ clear_dinode(xfs_mount_t *mp, struct xfs_dinode *dino, xfs_ino_t ino_num)
        clear_dinode_core(mp, dino, ino_num);
        clear_dinode_unlinked(mp, dino);
 
+       if (is_rtrmap_inode(ino_num))
+               rmap_avoid_check(mp);
+
        /* and clear the forks */
        memset(XFS_DFORK_DPTR(dino), 0, XFS_LITINO(mp));
        return;
@@ -824,13 +827,22 @@ process_rtrmap(
 
        lino = XFS_AGINO_TO_INO(mp, agno, ino);
 
-       /* This rmap btree inode must be a metadata inode. */
+       /*
+        * This rmap btree inode must be a metadata inode reachable via
+        * /realtime/$rgno.rmap in the metadata directory tree.
+        */
        if (!(dip->di_flags2 & be64_to_cpu(XFS_DIFLAG2_METADIR))) {
                do_warn(
 _("rtrmap inode %" PRIu64 " not flagged as metadata\n"),
                        lino);
                return 1;
        }
+       if (type != XR_INO_RTRMAP) {
+               do_warn(
+_("rtrmap inode %" PRIu64 " was not found in the metadata directory tree\n"),
+                       lino);
+               return 1;
+       }
 
        rgno = rtgroup_for_rtrmap_inode(mp, lino);
        if (rgno == NULLRGNUMBER) {
@@ -876,7 +888,7 @@ _("computed size of rtrmapbt root (%zu bytes) is greater than space in "
                error = process_rtrmap_reclist(mp, rp, numrecs,
                                &priv.last_rec, NULL, "rtrmapbt root");
                if (error) {
-                       rmap_avoid_check();
+                       rmap_avoid_check(mp);
                        return 1;
                }
                return 0;
@@ -1840,6 +1852,9 @@ process_check_sb_inodes(
        if (lino == mp->m_sb.sb_rbmino)
                return process_check_rt_inode(mp, dinoc, lino, type, dirty,
                                XR_INO_RTBITMAP, _("realtime bitmap"));
+       if (is_rtrmap_inode(lino))
+               return process_check_rt_inode(mp, dinoc, lino, type, dirty,
+                               XR_INO_RTRMAP, _("realtime rmap btree"));
        return 0;
 }
 
@@ -1937,6 +1952,18 @@ _("realtime summary inode %" PRIu64 " has bad size %" PRId64 " (should be %d)\n"
                }
                break;
 
+       case XR_INO_RTRMAP:
+               /*
+                * if we have no rmapbt, any inode claiming
+                * to be a real-time file is bogus
+                */
+               if (!xfs_has_rmapbt(mp)) {
+                       do_warn(
+_("found inode %" PRIu64 " claiming to be a rtrmapbt file, but rmapbt is disabled\n"), lino);
+                       return 1;
+               }
+               break;
+
        default:
                break;
        }
@@ -1965,6 +1992,14 @@ _("bad attr fork offset %d in dev inode %" PRIu64 ", should be %d\n"),
                        return 1;
                }
                break;
+       case XFS_DINODE_FMT_RMAP:
+               if (!(xfs_has_metadir(mp) && xfs_has_parent(mp))) {
+                       do_warn(
+_("metadata inode %" PRIu64 " type %d cannot have attr fork\n"),
+                               lino, dino->di_format);
+                       return 1;
+               }
+               fallthrough;
        case XFS_DINODE_FMT_LOCAL:
        case XFS_DINODE_FMT_EXTENTS:
        case XFS_DINODE_FMT_BTREE:
@@ -3094,6 +3129,8 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
                        type = XR_INO_GQUOTA;
                else if (lino == mp->m_sb.sb_pquotino)
                        type = XR_INO_PQUOTA;
+               else if (is_rtrmap_inode(lino))
+                       type = XR_INO_RTRMAP;
                else
                        type = XR_INO_DATA;
                break;
@@ -3200,6 +3237,7 @@ _("Bad CoW extent size %u on inode %" PRIu64 ", "),
                case XR_INO_UQUOTA:
                case XR_INO_GQUOTA:
                case XR_INO_PQUOTA:
+               case XR_INO_RTRMAP:
                        /*
                         * This inode was recognized as being filesystem
                         * metadata, so preserve the inode and its contents for
index 493d0f42790d646d6499f59d841b8bdb918758ba..e5f643bb7260d0a7336b0ab2fad9398a12ac7e85 100644 (file)
@@ -15,6 +15,8 @@
 #include "da_util.h"
 #include "prefetch.h"
 #include "progress.h"
+#include "slab.h"
+#include "rmap.h"
 
 /*
  * Known bad inode list.  These are seen when the leaf and node
@@ -274,6 +276,9 @@ process_sf_dir2(
                } else if (lino == mp->m_sb.sb_metadirino)  {
                        junkit = 1;
                        junkreason = _("metadata directory root");
+               } else if (is_rtrmap_inode(lino)) {
+                       junkit = 1;
+                       junkreason = _("realtime rmap");
                } else if ((irec_p = find_inode_rec(mp,
                                        XFS_INO_TO_AGNO(mp, lino),
                                        XFS_INO_TO_AGINO(mp, lino))) != NULL) {
@@ -749,6 +754,8 @@ process_dir2_data(
                        clearreason = _("project quota");
                } else if (ent_ino == mp->m_sb.sb_metadirino)  {
                        clearreason = _("metadata directory root");
+               } else if (is_rtrmap_inode(ent_ino)) {
+                       clearreason = _("realtime rmap");
                } else {
                        irec_p = find_inode_rec(mp,
                                                XFS_INO_TO_AGNO(mp, ent_ino),
index 645cc5317c8d61d1633be18fd4393c71c98134a3..6ee7a6629307c78971687afc587afda5a1b3e42a 100644 (file)
@@ -221,6 +221,7 @@ int         count_bcnt_extents(xfs_agnumber_t);
 #define XR_INO_UQUOTA  12              /* user quota inode */
 #define XR_INO_GQUOTA  13              /* group quota inode */
 #define XR_INO_PQUOTA  14              /* project quota inode */
+#define XR_INO_RTRMAP  15              /* realtime rmap */
 
 /* inode allocation tree */
 
index c6ae0f1b8fb7de3d4bc557f9c5139bb2c7b44bc7..430f32f494c175e455cf54a41edba0e493894f9c 100644 (file)
@@ -37,6 +37,12 @@ struct xfs_ag_rmap {
        /* refcount items, p4-5 */
        struct xfs_slab         *ar_refcount_items;
 
+       /*
+        * inumber of the rmap btree for this rtgroup.  This can be set to
+        * NULLFSINO to signal to phase 6 to link a new inode into the metadir.
+        */
+       xfs_ino_t               rg_rmap_ino;
+
        /* agfl entries from leftover agbt allocations */
        int                     ar_flcount;
 };
@@ -46,6 +52,9 @@ static struct xfs_ag_rmap *rg_rmaps;
 bool rmapbt_suspect;
 static bool refcbt_suspect;
 
+/* Bitmap of rt group rmap inodes reachable via /realtime/$rgno.rmap. */
+static struct bitmap   *rmap_inodes;
+
 static struct xfs_ag_rmap *rmaps_for_group(bool isrt, unsigned int group)
 {
        if (isrt)
@@ -123,6 +132,7 @@ rmaps_init_rt(
        if (error)
                goto nomem;
 
+       ag_rmap->rg_rmap_ino = NULLFSINO;
        return;
 nomem:
        do_error(
@@ -174,10 +184,103 @@ rtgroup_for_rtrmap_inode(
        struct xfs_mount        *mp,
        xfs_ino_t               ino)
 {
-       /* This will be implemented later. */
+       xfs_rgnumber_t          rgno;
+
+       if (!rmap_inodes)
+               return NULLRGNUMBER;
+
+       for (rgno = 0; rgno < mp->m_sb.sb_rgcount; rgno++) {
+               if (rg_rmaps[rgno].rg_rmap_ino == ino)
+                       return rgno;
+       }
+
        return NULLRGNUMBER;
 }
 
+static inline int
+set_rtgroup_rmap_inode(
+       struct xfs_mount        *mp,
+       xfs_rgnumber_t          rgno)
+{
+       struct xfs_imeta_path   *path;
+       struct xfs_ag_rmap      *ar = rmaps_for_group(true, rgno);
+       struct xfs_trans        *tp;
+       xfs_ino_t               ino;
+       int                     error;
+
+       if (!xfs_has_rtrmapbt(mp))
+               return 0;
+
+       path = xfs_rtrmapbt_create_path(mp, rgno);
+       if (!path)
+               return ENOMEM;
+
+       error = -libxfs_trans_alloc_empty(mp, &tp);
+       if (error)
+               goto out_path;
+
+       error = -libxfs_imeta_lookup(tp, path, &ino);
+       if (error)
+               goto out_trans;
+
+       if (!libxfs_verify_ino(mp, ino) || bitmap_test(rmap_inodes, ino, 1)) {
+               error = EFSCORRUPTED;
+               goto out_trans;
+       }
+
+       error = bitmap_set(rmap_inodes, ino, 1);
+       if (error)
+               goto out_trans;
+
+       ar->rg_rmap_ino = ino;
+
+out_trans:
+       libxfs_trans_cancel(tp);
+out_path:
+       libxfs_imeta_free_path(path);
+       return error;
+}
+
+static void
+discover_rtgroup_inodes(
+       struct xfs_mount        *mp)
+{
+       xfs_rgnumber_t          rgno;
+       int                     error;
+
+       error = bitmap_alloc(&rmap_inodes);
+       if (error)
+               goto out;
+
+       for (rgno = 0; rgno < mp->m_sb.sb_rgcount; rgno++) {
+               int err2 = set_rtgroup_rmap_inode(mp, rgno);
+               if (err2 && !error)
+                       error = err2;
+       }
+
+out:
+       if (error == EFSCORRUPTED)
+               do_warn(
+ _("corruption in metadata directory tree while discovering rt group inodes\n"));
+       if (error)
+               do_warn(
+ _("couldn't discover rt group inodes, err %d\n"),
+                               error);
+}
+
+static inline void
+free_rtmeta_inode_bitmaps(void)
+{
+       bitmap_free(&rmap_inodes);
+}
+
+bool is_rtrmap_inode(xfs_ino_t ino)
+{
+       if (!rmap_inodes)
+               return false;
+       return bitmap_test(rmap_inodes, ino, 1);
+}
+
 /*
  * Initialize per-AG reverse map data.
  */
@@ -203,6 +306,8 @@ rmaps_init(
 
        for (i = 0; i < mp->m_sb.sb_rgcount; i++)
                rmaps_init_rt(mp, i, &rg_rmaps[i]);
+
+       discover_rtgroup_inodes(mp);
 }
 
 /*
@@ -217,6 +322,8 @@ rmaps_free(
        if (!rmap_needs_work(mp))
                return;
 
+       free_rtmeta_inode_bitmaps();
+
        for (i = 0; i < mp->m_sb.sb_rgcount; i++)
                rmaps_destroy(mp, &rg_rmaps[i]);
        free(rg_rmaps);
@@ -1144,11 +1251,22 @@ rmap_record_count(
 }
 
 /*
- * Disable the refcount btree check.
+ * Disable the rmap btree check.
  */
 void
-rmap_avoid_check(void)
+rmap_avoid_check(
+       struct xfs_mount        *mp)
 {
+       struct xfs_rtgroup      *rtg;
+       xfs_rgnumber_t          rgno;
+
+       for_each_rtgroup(mp, rgno, rtg) {
+               struct xfs_ag_rmap *ar = rmaps_for_group(true, rtg->rtg_rgno);
+
+               ar->rg_rmap_ino = NULLFSINO;
+       }
+
+       bitmap_clear(rmap_inodes, 0, XFS_MAXINUMBER);
        rmapbt_suspect = true;
 }
 
@@ -1827,3 +1945,13 @@ estimate_refcountbt_blocks(
        return libxfs_refcountbt_calc_size(mp,
                        slab_count(x->ar_refcount_items));
 }
+
+/* Retrieve the rtrmapbt inode number for a given rtgroup. */
+xfs_ino_t
+rtgroup_rmap_ino(
+       struct xfs_rtgroup      *rtg)
+{
+       struct xfs_ag_rmap      *ar = rmaps_for_group(true, rtg->rtg_rgno);
+
+       return ar->rg_rmap_ino;
+}
index 23871e6d60e77485aa6d193f2b47e32fdb13b52e..ee439d690831b266976db4d19ac3692e8050d24a 100644 (file)
@@ -28,7 +28,7 @@ int rmap_commit_agbtree_mappings(struct xfs_mount *mp, xfs_agnumber_t agno);
 
 uint64_t rmap_record_count(struct xfs_mount *mp, bool isrt,
                xfs_agnumber_t agno);
-extern void rmap_avoid_check(void);
+extern void rmap_avoid_check(struct xfs_mount *mp);
 void rmaps_verify_btree(struct xfs_mount *mp, xfs_agnumber_t agno);
 
 extern int64_t rmap_diffkeys(struct xfs_rmap_irec *kp1,
@@ -57,5 +57,7 @@ int rmap_init_mem_cursor(struct xfs_mount *mp, struct xfs_trans *tp,
 int rmap_get_mem_rec(struct xfs_btree_cur *rmcur, struct xfs_rmap_irec *irec);
 
 xfs_rgnumber_t rtgroup_for_rtrmap_inode(struct xfs_mount *mp, xfs_ino_t ino);
+bool is_rtrmap_inode(xfs_ino_t ino);
+xfs_ino_t rtgroup_rmap_ino(struct xfs_rtgroup *rtg);
 
 #endif /* RMAP_H_ */
index d253ae8c14d1016d1348a2597a4ad4e79b41d4ac..dc2e11b76c04f3d38eeb1a9ce69fa4e309e5c435 100644 (file)
@@ -1357,7 +1357,7 @@ _("out of order key %u in %s btree block (%u/%u)\n"),
 
 out:
        if (suspect)
-               rmap_avoid_check();
+               rmap_avoid_check(mp);
 }
 
 int
@@ -1737,7 +1737,7 @@ _("bad %s btree ptr 0x%llx in ino %" PRIu64 "\n"),
 
 out:
        if (hdr_errors || suspect) {
-               rmap_avoid_check();
+               rmap_avoid_check(mp);
                return 1;
        }
        return 0;
@@ -2818,7 +2818,7 @@ validate_agf(
                if (levels == 0 || levels > mp->m_rmap_maxlevels) {
                        do_warn(_("bad levels %u for rmapbt root, agno %d\n"),
                                levels, agno);
-                       rmap_avoid_check();
+                       rmap_avoid_check(mp);
                }
 
                bno = be32_to_cpu(agf->agf_rmap_root);
@@ -2833,7 +2833,7 @@ validate_agf(
                } else {
                        do_warn(_("bad agbno %u for rmapbt root, agno %d\n"),
                                bno, agno);
-                       rmap_avoid_check();
+                       rmap_avoid_check(mp);
                }
        }