@@ -77,6 +77,7 @@ struct iomap;
#include "xfs_refcount_btree.h"
#include "xfs_refcount.h"
#include "xfs_btree_staging.h"
+#include "xfs_ag_resv.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -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);
@@ -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.
@@ -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
@@ -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,218 @@ 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;
+ struct xfs_perag *pag;
+ xfs_ino_t rootino;
+ xfs_agnumber_t agno;
+ int min_logblocks;
+ int error;
+
+ 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);
+ }
+
+ /* Make sure we have enough space for per-AG reservations. */
+ for_each_perag(mp, agno, pag) {
+ struct xfs_trans *tp;
+ struct xfs_agf *agf;
+ struct xfs_buf *agi_bp, *agf_bp;
+ unsigned int avail, agblocks;
+
+ /* Put back the old super so that we can read AG headers. */
+ restore_old_state(mp, &old);
+
+ /*
+ * Create a dummy transaction so that we can load the AGI and
+ * AGF buffers in memory with the old fs geometry and pin them
+ * there while we try to make a per-AG reservation with the new
+ * geometry.
+ */
+ error = -libxfs_trans_alloc_empty(mp, &tp);
+ if (error)
+ do_error(
+ _("Cannot reserve resources for upgrade check, err=%d.\n"),
+ error);
+
+ error = -libxfs_ialloc_read_agi(mp, tp, pag->pag_agno,
+ &agi_bp);
+ if (error)
+ do_error(
+ _("Cannot read AGI %u for upgrade check, err=%d.\n"),
+ pag->pag_agno, error);
+
+ error = -libxfs_alloc_read_agf(mp, tp, pag->pag_agno, 0,
+ &agf_bp);
+ if (error)
+ do_error(
+ _("Cannot read AGF %u for upgrade check, err=%d.\n"),
+ pag->pag_agno, error);
+ agf = agf_bp->b_addr;
+ agblocks = be32_to_cpu(agf->agf_length);
+
+ /*
+ * Install the new superblock and try to make a per-AG space
+ * reservation with the new geometry. We pinned the AG header
+ * buffers to the transaction, so we shouldn't hit any
+ * corruption errors on account of the new geometry.
+ */
+ install_new_state(mp, new_sb);
+
+ error = -libxfs_ag_resv_init(pag, tp);
+ if (error == ENOSPC) {
+ printf(
+ _("Not enough free space would remain in AG %u for metadata.\n"),
+ pag->pag_agno);
+ exit(1);
+ }
+ if (error)
+ do_error(
+ _("Error %d while checking AG %u space reservation.\n"),
+ error, pag->pag_agno);
+
+ /*
+ * Would we have at least 10% free space in this AG after
+ * making per-AG reservations?
+ */
+ avail = pag->pagf_freeblks + pag->pagf_flcount;
+ avail -= pag->pag_meta_resv.ar_reserved;
+ avail -= pag->pag_rmapbt_resv.ar_asked;
+ if (avail < agblocks / 10) {
+ printf(
+ _("AG %u will be low on space after upgrade.\n"),
+ pag->pag_agno);
+ exit(1);
+ }
+ libxfs_trans_cancel(tp);
+ }
+
+ /*
+ * Would we have at least 10% free space in the data device after all
+ * the upgrades?
+ */
+ if (mp->m_sb.sb_fdblocks < mp->m_sb.sb_dblocks / 10) {
+ printf(_("Filesystem will be low on space after upgrade.\n"));
+ exit(1);
+ }
+
+ /*
+ * Release the per-AG reservations and mark the per-AG structure as
+ * uninitialized so that we don't trip over stale cached counters
+ * after the upgrade/
+ */
+ for_each_perag(mp, agno, pag) {
+ libxfs_ag_resv_free(pag);
+ pag->pagf_init = 0;
+ pag->pagi_init = 0;
+ }
+
+ /*
+ * 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;