xfs_repair: allow sysadmins to add realtime reflink
authorDarrick J. Wong <djwong@kernel.org>
Wed, 3 Jul 2024 21:22:35 +0000 (14:22 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 9 Jul 2024 22:37:24 +0000 (15:37 -0700)
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 381e94793085a802c521a1c680b2994bd3936450..fe03424f4bbb483036996ee021d42ebe95d03efb 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 6f7ede1da3408fa26518462e84b8a759f5792a82..868da40b4c78e439053880db4763af62fe39ef2a 100644 (file)
@@ -251,14 +251,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;
 }
 
@@ -579,6 +584,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;
+
+       path = xfs_rtrefcountbt_create_path(mp, rtg->rtg_rgno);
+       if (!path)
+               return ENOMEM;
+
+       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;
+               error = 0;
+               goto out_trans;
+       }
+
+       error = -libxfs_imeta_iget(tp, ino, S_IFREG, &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,
@@ -678,6 +740,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);
        }
 
        /*
@@ -711,6 +785,11 @@ _("Error %d while checking rtgroup %u rmap inode space reservation.\n"),
                        libxfs_irele(rtg->rtg_rmapip);
                        rtg->rtg_rmapip = NULL;
                }
+               if (rtg->rtg_refcountip) {
+                       libxfs_imeta_resv_free_inode(rtg->rtg_refcountip);
+                       libxfs_irele(rtg->rtg_refcountip);
+                       rtg->rtg_refcountip = NULL;
+               }
        }
 
        /*