From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxxx> --- libxfs/libxfs_api_defs.h | 1 + repair/phase2.c | 83 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index bc34ce9caad..4e7b3caba4b 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -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 diff --git a/repair/phase2.c b/repair/phase2.c index 22458aee4cd..cd3aa30eecd 100644 --- a/repair/phase2.c +++ b/repair/phase2.c @@ -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; + } } /*