xfs: fix inode_cluster_size rounding mayhem
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 5 Jun 2019 18:19:35 +0000 (11:19 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Wed, 12 Jun 2019 15:37:40 +0000 (08:37 -0700)
inode_cluster_size is supposed to represent the size (in bytes) of an
inode cluster buffer.  We avoid having to handle multiple clusters per
filesystem block on filesystems with large blocks by openly rounding
this value up to 1 FSB when necessary.  However, we never reset
inode_cluster_size to reflect this new rounded value, which adds to the
potential for mistakes in calculating geometries.

Fix this by setting inode_cluster_size to reflect the rounded-up size if
needed, and special-case the few places in the sparse inodes code where
we actually need the smaller value to validate on-disk metadata.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/libxfs/xfs_format.h
fs/xfs/libxfs/xfs_ialloc.c
fs/xfs/libxfs/xfs_trans_resv.c
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_mount.c

index 0e831f04725cc62cd258c57e07d80395644e5c9c..5729474e362fb3d0a26984b79830bace19253848 100644 (file)
@@ -1698,11 +1698,16 @@ struct xfs_ino_geometry {
        /* Maximum inode count in this filesystem. */
        uint64_t        maxicount;
 
+       /* Actual inode cluster buffer size, in bytes. */
+       unsigned int    inode_cluster_size;
+
        /*
         * Desired inode cluster buffer size, in bytes.  This value is not
-        * rounded up to at least one filesystem block.
+        * rounded up to at least one filesystem block, which is necessary for
+        * the sole purpose of validating sb_spino_align.  Runtime code must
+        * only ever use inode_cluster_size.
         */
-       unsigned int    inode_cluster_size;
+       unsigned int    inode_cluster_size_raw;
 
        /* Inode cluster sizes, adjusted to be at least 1 fsb. */
        unsigned int    inodes_per_cluster;
index fdfcc03a35b9bd57f6a01349736d072641d39e67..0f5ff2a4b0b80b76f0411156fed221e2642543af 100644 (file)
@@ -2805,21 +2805,32 @@ xfs_ialloc_setup_geometry(
                igeo->maxicount = 0;
        }
 
-       igeo->inode_cluster_size = XFS_INODE_BIG_CLUSTER_SIZE;
+       /*
+        * Compute the desired size of an inode cluster buffer size, which
+        * starts at 8K and (on v5 filesystems) scales up with larger inode
+        * sizes.
+        *
+        * Preserve the desired inode cluster size because the sparse inodes
+        * feature uses that desired size (not the actual size) to compute the
+        * sparse inode alignment.  The mount code validates this value, so we
+        * cannot change the behavior.
+        */
+       igeo->inode_cluster_size_raw = XFS_INODE_BIG_CLUSTER_SIZE;
        if (xfs_sb_version_hascrc(&mp->m_sb)) {
-               int     new_size = igeo->inode_cluster_size;
+               int     new_size = igeo->inode_cluster_size_raw;
 
                new_size *= mp->m_sb.sb_inodesize / XFS_DINODE_MIN_SIZE;
                if (mp->m_sb.sb_inoalignmt >= XFS_B_TO_FSBT(mp, new_size))
-                       igeo->inode_cluster_size = new_size;
+                       igeo->inode_cluster_size_raw = new_size;
        }
 
        /* Calculate inode cluster ratios. */
-       if (igeo->inode_cluster_size > mp->m_sb.sb_blocksize)
+       if (igeo->inode_cluster_size_raw > mp->m_sb.sb_blocksize)
                igeo->blocks_per_cluster = XFS_B_TO_FSBT(mp,
-                               igeo->inode_cluster_size);
+                               igeo->inode_cluster_size_raw);
        else
                igeo->blocks_per_cluster = 1;
+       igeo->inode_cluster_size = XFS_FSB_TO_B(mp, igeo->blocks_per_cluster);
        igeo->inodes_per_cluster = XFS_FSB_TO_INO(mp, igeo->blocks_per_cluster);
 
        /* Calculate inode cluster alignment. */
index 2663dd7975a57b3d46895123f1ffbcefa89ed47e..9d1326d14af914a20f47a63db175beef738aa790 100644 (file)
@@ -308,8 +308,7 @@ xfs_calc_iunlink_remove_reservation(
        struct xfs_mount        *mp)
 {
        return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
-              2 * max_t(uint, XFS_FSB_TO_B(mp, 1),
-                        M_IGEO(mp)->inode_cluster_size);
+              2 * M_IGEO(mp)->inode_cluster_size;
 }
 
 /*
@@ -347,8 +346,7 @@ STATIC uint
 xfs_calc_iunlink_add_reservation(xfs_mount_t *mp)
 {
        return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
-                       max_t(uint, XFS_FSB_TO_B(mp, 1),
-                             M_IGEO(mp)->inode_cluster_size);
+                       M_IGEO(mp)->inode_cluster_size;
 }
 
 /*
index 1557304f3d6850dae4998437f3f003b9ef08fb34..f7c062df29bfc188bfe5943f487528b13879c7b8 100644 (file)
@@ -2893,8 +2893,7 @@ xlog_recover_buffer_pass2(
         */
        if (XFS_DINODE_MAGIC ==
            be16_to_cpu(*((__be16 *)xfs_buf_offset(bp, 0))) &&
-           (BBTOB(bp->b_io_length) != max(log->l_mp->m_sb.sb_blocksize,
-                       M_IGEO(log->l_mp)->inode_cluster_size))) {
+           (BBTOB(bp->b_io_length) != M_IGEO(log->l_mp)->inode_cluster_size)) {
                xfs_buf_stale(bp);
                error = xfs_bwrite(bp);
        } else {
index 81d6535b24b46f9c80bee6e4b9205365a94c6ccd..544fa469aca46a3862b3831fb7f11da1307e0497 100644 (file)
@@ -746,11 +746,11 @@ xfs_mountfs(
         */
        if (xfs_sb_version_hassparseinodes(&mp->m_sb) &&
            mp->m_sb.sb_spino_align !=
-                       XFS_B_TO_FSBT(mp, igeo->inode_cluster_size)) {
+                       XFS_B_TO_FSBT(mp, igeo->inode_cluster_size_raw)) {
                xfs_warn(mp,
        "Sparse inode block alignment (%u) must match cluster size (%llu).",
                         mp->m_sb.sb_spino_align,
-                        XFS_B_TO_FSBT(mp, igeo->inode_cluster_size));
+                        XFS_B_TO_FSBT(mp, igeo->inode_cluster_size_raw));
                error = -EINVAL;
                goto out_remove_uuid;
        }