]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs_repair: allow sysadmins to add realtime reflink
authorDarrick J. Wong <djwong@kernel.org>
Fri, 15 Jul 2022 21:34:50 +0000 (14:34 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 22 Nov 2023 23:03:42 +0000 (15:03 -0800)
Allow the sysadmin to use xfs_repair to upgrade an existing filesystem
to support the realtime reference count btree, and therefore reflink on
realtime volumes.

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

index 650fda3dcb9172cbe0eecd2e4da65f1a09d611e6..91722de39e0f7ccb0c0ddd1cb071905514374b32 100644 (file)
 #define xfs_rtgroup_update_super       libxfs_rtgroup_update_super
 
 #define xfs_rtrefcountbt_absolute_maxlevels    libxfs_rtrefcountbt_absolute_maxlevels
+#define xfs_rtrefcountbt_calc_reserves libxfs_rtrefcountbt_calc_reserves
 #define xfs_rtrefcountbt_calc_size             libxfs_rtrefcountbt_calc_size
 #define xfs_rtrefcountbt_commit_staged_btree   libxfs_rtrefcountbt_commit_staged_btree
 #define xfs_rtrefcountbt_create                libxfs_rtrefcountbt_create
index d2a5a62400f90f20f99131f1c1b940380542ad32..bd0530cb6e02082b52c462fe4206c9a46cff3898 100644 (file)
@@ -244,14 +244,19 @@ set_reflink(
                exit(0);
        }
 
-       if (xfs_has_realtime(mp)) {
-               printf(_("Reflink feature not supported with realtime.\n"));
+       if (xfs_has_realtime(mp) && !xfs_has_rtgroups(mp)) {
+               printf(_("Reference count btree requires realtime groups.\n"));
                exit(0);
        }
 
        printf(_("Adding reflink support to filesystem.\n"));
        new_sb->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_REFLINK;
        new_sb->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR;
+
+       /* Quota counts will be wrong once we add the refcount inodes. */
+       if (xfs_has_realtime(mp))
+               quotacheck_skip();
+
        return true;
 }
 
@@ -532,6 +537,63 @@ out_path:
        return error;
 }
 
+/*
+ * Reserve space to handle rt refcount btree expansion.
+ *
+ * If the refcount 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.  If the inode does not exist, the amount of space
+ * needed to handle a new maximally sized refcount btree is added to @new_resv.
+ */
+static int
+reserve_rtrefcount_inode(
+       struct xfs_rtgroup      *rtg,
+       xfs_rfsblock_t          *new_resv)
+{
+       struct xfs_mount        *mp = rtg->rtg_mount;
+       struct xfs_imeta_path   *path;
+       struct xfs_trans        *tp;
+       xfs_ino_t               ino;
+       xfs_filblks_t           ask;
+       int                     error;
+
+       if (!xfs_has_rtreflink(mp))
+               return 0;
+
+       error = -libxfs_rtrefcountbt_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_rtrefcountbt_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_refcountip);
+       if (error)
+               goto out_trans;
+
+       error = -libxfs_imeta_resv_init_inode(rtg->rtg_refcountip, 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,
@@ -631,6 +693,18 @@ _("Not enough free space would remain for rtgroup %u rmap inode.\n"),
                        do_error(
 _("Error %d while checking rtgroup %u rmap inode space reservation.\n"),
                                        error, rtg->rtg_rgno);
+
+               error = reserve_rtrefcount_inode(rtg, &new_resv);
+               if (error == ENOSPC) {
+                       printf(
+_("Not enough free space would remain for rtgroup %u refcount inode.\n"),
+                                       rtg->rtg_rgno);
+                       exit(0);
+               }
+               if (error)
+                       do_error(
+_("Error %d while checking rtgroup %u refcount inode space reservation.\n"),
+                                       error, rtg->rtg_rgno);
        }
 
        /*
@@ -664,6 +738,11 @@ _("Error %d while checking rtgroup %u rmap inode space reservation.\n"),
                        libxfs_imeta_irele(rtg->rtg_rmapip);
                        rtg->rtg_rmapip = NULL;
                }
+               if (rtg->rtg_refcountip) {
+                       libxfs_imeta_resv_free_inode(rtg->rtg_refcountip);
+                       libxfs_imeta_irele(rtg->rtg_refcountip);
+                       rtg->rtg_refcountip = NULL;
+               }
        }
 
        /*