]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs_repair: allow sysadmins to add realtime reverse mapping indexes
authorDarrick J. Wong <djwong@kernel.org>
Tue, 9 Jan 2024 17:40:21 +0000 (09:40 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 10 Apr 2024 00:21:45 +0000 (17:21 -0700)
Allow the sysadmin to use xfs_repair to upgrade an existing filesystem
to support the reverse mapping btree index for realtime volumes.  This
is needed for online fsck.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
libxfs/libxfs_api_defs.h
repair/phase2.c

index e61572faa1b7f57a2affd109e0b43bc822ae6c95..c8352881edf08552749aa805214ddddebfae0f1a 100644 (file)
@@ -73,6 +73,7 @@
 #define xfs_btree_bload                        libxfs_btree_bload
 #define xfs_btree_bload_compute_geometry libxfs_btree_bload_compute_geometry
 #define xfs_btree_calc_size            libxfs_btree_calc_size
+#define xfs_btree_compute_maxlevels    libxfs_btree_compute_maxlevels
 #define xfs_btree_decrement            libxfs_btree_decrement
 #define xfs_btree_del_cursor           libxfs_btree_del_cursor
 #define xfs_btree_delete               libxfs_btree_delete
 #define xfs_imeta_link_space_res       libxfs_imeta_link_space_res
 #define xfs_imeta_lookup               libxfs_imeta_lookup
 #define xfs_imeta_mount                        libxfs_imeta_mount
+#define xfs_imeta_resv_free_inode      libxfs_imeta_resv_free_inode
+#define xfs_imeta_resv_init_inode      libxfs_imeta_resv_init_inode
 #define xfs_imeta_set_iflag            libxfs_imeta_set_iflag
 #define xfs_imeta_start_create         libxfs_imeta_start_create
 #define xfs_imeta_start_link           libxfs_imeta_start_link
 #define xfs_rtgroup_put                        libxfs_rtgroup_put
 #define xfs_rtgroup_update_secondary_sbs       libxfs_rtgroup_update_secondary_sbs
 #define xfs_rtgroup_update_super       libxfs_rtgroup_update_super
+#define xfs_rtrmapbt_calc_reserves     libxfs_rtrmapbt_calc_reserves
 #define xfs_rtrmapbt_calc_size         libxfs_rtrmapbt_calc_size
 #define xfs_rtrmapbt_commit_staged_btree       libxfs_rtrmapbt_commit_staged_btree
 #define xfs_rtrmapbt_create            libxfs_rtrmapbt_create
index 5331b9f214ffad73275a5fb773525accd131d526..aed37bc60600443059e39ab6db4819e2159a2c18 100644 (file)
@@ -278,9 +278,8 @@ set_rmapbt(
                exit(0);
        }
 
-       if (xfs_has_realtime(mp)) {
-               printf(
-       _("Reverse mapping btree feature not supported with realtime.\n"));
+       if (xfs_has_realtime(mp) && !xfs_has_rtgroups(mp)) {
+               printf(_("Reverse mapping btree requires realtime groups.\n"));
                exit(0);
        }
 
@@ -293,6 +292,11 @@ set_rmapbt(
        printf(_("Adding reverse mapping btrees to filesystem.\n"));
        new_sb->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_RMAPBT;
        new_sb->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR;
+
+       /* Quota counts will be wrong once we add the rmap inodes. */
+       if (xfs_has_realtime(mp))
+               quotacheck_skip();
+
        return true;
 }
 
@@ -478,6 +482,63 @@ check_free_space(
        return avail > GIGABYTES(10, mp->m_sb.sb_blocklog);
 }
 
+/*
+ * Reserve space to handle rt rmap btree expansion.
+ *
+ * If the rmap inode for this group already exists, we assume that we're adding
+ * some other feature.  Note that we have not validated the metadata directory
+ * tree, so we must perform the lookup by hand and abort the upgrade if there
+ * are errors.  Otherwise, the amount of space needed to handle a new maximally
+ * sized rmap btree is added to @new_resv.
+ */
+static int
+reserve_rtrmap_inode(
+       struct xfs_rtgroup      *rtg,
+       xfs_rfsblock_t          *new_resv)
+{
+       struct xfs_mount        *mp = rtg->rtg_mount;
+       struct xfs_trans        *tp;
+       struct xfs_imeta_path   *path;
+       xfs_ino_t               ino;
+       xfs_filblks_t           ask;
+       int                     error;
+
+       if (!xfs_has_rtrmapbt(mp))
+               return 0;
+
+       error = -libxfs_rtrmapbt_create_path(mp, rtg->rtg_rgno, &path);
+       if (error)
+               return error;
+
+       error = -libxfs_trans_alloc_empty(mp, &tp);
+       if (error)
+               goto out_path;
+
+       ask = libxfs_rtrmapbt_calc_reserves(mp);
+
+       error = -libxfs_imeta_lookup(tp, path, &ino);
+       if (error)
+               goto out_trans;
+
+       if (ino == NULLFSINO) {
+               *new_resv += ask;
+               goto out_trans;
+       }
+
+       error = -libxfs_imeta_iget(tp, ino, XFS_DIR3_FT_REG_FILE,
+                       &rtg->rtg_rmapip);
+       if (error)
+               goto out_trans;
+
+       error = -libxfs_imeta_resv_init_inode(rtg->rtg_rmapip, ask);
+
+out_trans:
+       libxfs_trans_cancel(tp);
+out_path:
+       libxfs_imeta_free_path(path);
+       return error;
+}
+
 static void
 check_fs_free_space(
        struct xfs_mount                *mp,
@@ -485,7 +546,10 @@ check_fs_free_space(
        struct xfs_sb                   *new_sb)
 {
        struct xfs_perag                *pag;
+       struct xfs_rtgroup              *rtg;
+       xfs_rfsblock_t                  new_resv = 0;
        xfs_agnumber_t                  agno;
+       xfs_rgnumber_t                  rgno;
        int                             error;
 
        /* Make sure we have enough space for per-AG reservations. */
@@ -561,6 +625,21 @@ check_fs_free_space(
                libxfs_trans_cancel(tp);
        }
 
+       /* Realtime metadata btree inodes */
+       for_each_rtgroup(mp, rgno, rtg) {
+               error = reserve_rtrmap_inode(rtg, &new_resv);
+               if (error == ENOSPC) {
+                       printf(
+_("Not enough free space would remain for rtgroup %u rmap inode.\n"),
+                                       rtg->rtg_rgno);
+                       exit(0);
+               }
+               if (error)
+                       do_error(
+_("Error %d while checking rtgroup %u rmap inode space reservation.\n"),
+                                       error, rtg->rtg_rgno);
+       }
+
        /*
         * If we're adding parent pointers, we need at least 25% free since
         * scanning the entire filesystem to guesstimate the overhead is
@@ -576,13 +655,24 @@ check_fs_free_space(
 
        /*
         * Would the post-upgrade filesystem have enough free space on the data
-        * device after making per-AG reservations?
+        * device after making per-AG reservations and reserving rt metadata
+        * inode blocks?
         */
-       if (!check_free_space(mp, mp->m_sb.sb_fdblocks, mp->m_sb.sb_dblocks)) {
+       if (new_resv > mp->m_sb.sb_fdblocks ||
+           !check_free_space(mp, mp->m_sb.sb_fdblocks, mp->m_sb.sb_dblocks)) {
                printf(_("Filesystem will be low on space after upgrade.\n"));
                exit(1);
        }
 
+       /* Unreserve the realtime metadata reservations. */
+       for_each_rtgroup(mp, rgno, rtg) {
+               if (rtg->rtg_rmapip) {
+                       libxfs_imeta_resv_free_inode(rtg->rtg_rmapip);
+                       libxfs_imeta_irele(rtg->rtg_rmapip);
+                       rtg->rtg_rmapip = NULL;
+               }
+       }
+
        /*
         * Release the per-AG reservations and mark the per-AG structure as
         * uninitialized so that we don't trip over stale cached counters