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,
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.
#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
#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
static bool
set_inobtcount(
- struct xfs_mount *mp)
+ struct xfs_mount *mp,
+ struct xfs_sb *new_sb)
{
if (!xfs_has_crc(mp)) {
printf(
}
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(
}
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;