On 7/12/22 8:10 PM, Darrick J. Wong wrote: > From: Darrick J. Wong <djwong@xxxxxxxxxx> > > Currently, the two V5 feature upgrades permitted by xfs_repair do not > affect filesystem space usage, so we haven't needed to verify the > geometry. > > However, this will change once we start to allow the sysadmin to add the > large extent count feature to existing filesystems. Add all the > infrastructure we need to ensure that the log will still be large > enough, and the root inode will still be where we expect it to be after > the upgrade. > > Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> > Signed-off-by: Chandan Babu R <chandan.babu@xxxxxxxxxx> > [david: Recompute transaction reservation values; Exit with error if upgrade fails] > Signed-off-by: Dave Chinner <david@xxxxxxxxxxxxx> Reviewed-by: Eric Sandeen <sandeen@xxxxxxxxxx> > --- > include/xfs_mount.h | 1 > libxfs/init.c | 24 +++++++--- > libxfs/libxfs_api_defs.h | 3 + > repair/phase2.c | 113 ++++++++++++++++++++++++++++++++++++++++++---- > 4 files changed, 124 insertions(+), 17 deletions(-) > > > diff --git a/include/xfs_mount.h b/include/xfs_mount.h > index ba80aa79..24b1d873 100644 > --- a/include/xfs_mount.h > +++ b/include/xfs_mount.h > @@ -259,6 +259,7 @@ __XFS_UNSUPP_OPSTATE(shutdown) > > #define LIBXFS_BHASHSIZE(sbp) (1<<10) > > +void libxfs_compute_all_maxlevels(struct xfs_mount *mp); > struct xfs_mount *libxfs_mount(struct xfs_mount *mp, struct xfs_sb *sb, > dev_t dev, dev_t logdev, dev_t rtdev, unsigned int flags); > int libxfs_flush_mount(struct xfs_mount *mp); > diff --git a/libxfs/init.c b/libxfs/init.c > index a01a41b2..15052696 100644 > --- a/libxfs/init.c > +++ b/libxfs/init.c > @@ -728,6 +728,21 @@ xfs_agbtree_compute_maxlevels( > mp->m_agbtree_maxlevels = max(levels, mp->m_refc_maxlevels); > } > > +/* Compute maximum possible height of all btrees. */ > +void > +libxfs_compute_all_maxlevels( > + struct xfs_mount *mp) > +{ > + xfs_alloc_compute_maxlevels(mp); > + xfs_bmap_compute_maxlevels(mp, XFS_DATA_FORK); > + xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK); > + xfs_ialloc_setup_geometry(mp); > + xfs_rmapbt_compute_maxlevels(mp); > + xfs_refcountbt_compute_maxlevels(mp); > + > + xfs_agbtree_compute_maxlevels(mp); > +} > + > /* > * Mount structure initialization, provides a filled-in xfs_mount_t > * such that the numerous XFS_* macros can be used. If dev is zero, > @@ -772,14 +787,7 @@ libxfs_mount( > mp->m_swidth = sbp->sb_width; > } > > - xfs_alloc_compute_maxlevels(mp); > - xfs_bmap_compute_maxlevels(mp, XFS_DATA_FORK); > - xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK); > - xfs_ialloc_setup_geometry(mp); > - xfs_rmapbt_compute_maxlevels(mp); > - xfs_refcountbt_compute_maxlevels(mp); > - > - xfs_agbtree_compute_maxlevels(mp); > + libxfs_compute_all_maxlevels(mp); > > /* > * Check that the data (and log if separate) are an ok size. > diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h > index 370ad8b3..824f2c4d 100644 > --- a/libxfs/libxfs_api_defs.h > +++ b/libxfs/libxfs_api_defs.h > @@ -21,6 +21,8 @@ > > #define xfs_ag_init_headers libxfs_ag_init_headers > #define xfs_ag_block_count libxfs_ag_block_count > +#define xfs_ag_resv_init libxfs_ag_resv_init > +#define xfs_ag_resv_free libxfs_ag_resv_free > > #define xfs_alloc_ag_max_usable libxfs_alloc_ag_max_usable > #define xfs_allocbt_maxlevels_ondisk libxfs_allocbt_maxlevels_ondisk > @@ -112,6 +114,7 @@ > #define xfs_highbit64 libxfs_highbit64 > #define xfs_ialloc_calc_rootino libxfs_ialloc_calc_rootino > #define xfs_iallocbt_maxlevels_ondisk libxfs_iallocbt_maxlevels_ondisk > +#define xfs_ialloc_read_agi libxfs_ialloc_read_agi > #define xfs_idata_realloc libxfs_idata_realloc > #define xfs_idestroy_fork libxfs_idestroy_fork > #define xfs_iext_lookup_extent libxfs_iext_lookup_extent > diff --git a/repair/phase2.c b/repair/phase2.c > index 13832701..70365620 100644 > --- a/repair/phase2.c > +++ b/repair/phase2.c > @@ -133,7 +133,8 @@ zero_log( > > static bool > set_inobtcount( > - struct xfs_mount *mp) > + struct xfs_mount *mp, > + struct xfs_sb *new_sb) > { > if (!xfs_has_crc(mp)) { > printf( > @@ -153,14 +154,15 @@ set_inobtcount( > } > > printf(_("Adding inode btree counts to filesystem.\n")); > - mp->m_sb.sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_INOBTCNT; > - mp->m_sb.sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR; > + new_sb->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_INOBTCNT; > + new_sb->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR; > return true; > } > > static bool > set_bigtime( > - struct xfs_mount *mp) > + struct xfs_mount *mp, > + struct xfs_sb *new_sb) > { > if (!xfs_has_crc(mp)) { > printf( > @@ -174,28 +176,121 @@ set_bigtime( > } > > printf(_("Adding large timestamp support to filesystem.\n")); > - mp->m_sb.sb_features_incompat |= (XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | > - XFS_SB_FEAT_INCOMPAT_BIGTIME); > + new_sb->sb_features_incompat |= (XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | > + XFS_SB_FEAT_INCOMPAT_BIGTIME); > return true; > } > > +struct check_state { > + struct xfs_sb sb; > + uint64_t features; > + bool finobt_nores; > +}; > + > +static inline void > +capture_old_state( > + struct check_state *old_state, > + const struct xfs_mount *mp) > +{ > + memcpy(&old_state->sb, &mp->m_sb, sizeof(struct xfs_sb)); > + old_state->finobt_nores = mp->m_finobt_nores; > + old_state->features = mp->m_features; > +} > + > +static inline void > +restore_old_state( > + struct xfs_mount *mp, > + const struct check_state *old_state) > +{ > + memcpy(&mp->m_sb, &old_state->sb, sizeof(struct xfs_sb)); > + mp->m_finobt_nores = old_state->finobt_nores; > + mp->m_features = old_state->features; > + libxfs_compute_all_maxlevels(mp); > + libxfs_trans_init(mp); > +} > + > +static inline void > +install_new_state( > + struct xfs_mount *mp, > + struct xfs_sb *new_sb) > +{ > + memcpy(&mp->m_sb, new_sb, sizeof(struct xfs_sb)); > + mp->m_features |= libxfs_sb_version_to_features(new_sb); > + libxfs_compute_all_maxlevels(mp); > + libxfs_trans_init(mp); > +} > + > +/* > + * Make sure we can actually upgrade this (v5) filesystem without running afoul > + * of root inode or log size requirements that would prevent us from mounting > + * the filesystem. If everything checks out, commit the new geometry. > + */ > +static void > +install_new_geometry( > + struct xfs_mount *mp, > + struct xfs_sb *new_sb) > +{ > + struct check_state old; > + xfs_ino_t rootino; > + int min_logblocks; > + > + capture_old_state(&old, mp); > + install_new_state(mp, new_sb); > + > + /* > + * The existing log must be large enough to satisfy the new minimum log > + * size requirements. > + */ > + min_logblocks = libxfs_log_calc_minimum_size(mp); > + if (old.sb.sb_logblocks < min_logblocks) { > + printf( > + _("Filesystem log too small to upgrade filesystem; need %u blocks, have %u.\n"), > + min_logblocks, old.sb.sb_logblocks); > + exit(1); > + } > + > + /* > + * The root inode must be where xfs_repair will expect it to be with > + * the new geometry. > + */ > + rootino = libxfs_ialloc_calc_rootino(mp, new_sb->sb_unit); > + if (old.sb.sb_rootino != rootino) { > + printf( > + _("Cannot upgrade filesystem, root inode (%llu) cannot be moved to %llu.\n"), > + (unsigned long long)old.sb.sb_rootino, > + (unsigned long long)rootino); > + exit(1); > + } > + > + /* > + * Restore the old state to get everything back to a clean state, > + * upgrade the featureset one more time, and recompute the btree max > + * levels for this filesystem. > + */ > + restore_old_state(mp, &old); > + install_new_state(mp, new_sb); > +} > + > /* Perform the user's requested upgrades on filesystem. */ > static void > upgrade_filesystem( > struct xfs_mount *mp) > { > + struct xfs_sb new_sb; > struct xfs_buf *bp; > bool dirty = false; > int error; > > + memcpy(&new_sb, &mp->m_sb, sizeof(struct xfs_sb)); > + > if (add_inobtcount) > - dirty |= set_inobtcount(mp); > + dirty |= set_inobtcount(mp, &new_sb); > if (add_bigtime) > - dirty |= set_bigtime(mp); > + dirty |= set_bigtime(mp, &new_sb); > if (!dirty) > return; > > - mp->m_features |= libxfs_sb_version_to_features(&mp->m_sb); > + install_new_geometry(mp, &new_sb); > if (no_modify) > return; > >