@@ -294,6 +294,7 @@
#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
@@ -223,14 +223,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;
}
@@ -511,6 +516,63 @@ reserve_rtrmap_inode(
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,
@@ -610,6 +672,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);
}
/*
@@ -643,6 +717,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;
+ }
}
/*