xfs: use the actual AG length when reserving blocks
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 4 Jan 2017 02:39:33 +0000 (18:39 -0800)
committerDarrick J. Wong <darrick.wong@oracle.com>
Wed, 4 Jan 2017 02:39:33 +0000 (18:39 -0800)
We need to use the actual AG length when making per-AG reservations,
since we could otherwise end up reserving more blocks out of the last
AG than there are actual blocks.

Complained-about-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_ag_resv.c
fs/xfs/libxfs/xfs_refcount_btree.c
fs/xfs/libxfs/xfs_refcount_btree.h
fs/xfs/libxfs/xfs_rmap_btree.c
fs/xfs/libxfs/xfs_rmap_btree.h
fs/xfs/xfs_fsops.c

index e5ebc37704608a336dd8690d55f06389cd0173bd..d346d42c54d1590250040f0b36c05367287e7bf5 100644 (file)
@@ -256,6 +256,9 @@ xfs_ag_resv_init(
                        goto out;
        }
 
+       ASSERT(xfs_perag_resv(pag, XFS_AG_RESV_METADATA)->ar_reserved +
+              xfs_perag_resv(pag, XFS_AG_RESV_AGFL)->ar_reserved <=
+              pag->pagf_freeblks + pag->pagf_flcount);
 out:
        return error;
 }
index 6fb2215f8ff77bf0342e5f61dd6d060987e13d77..50add5272807e95374c3043819205f68c5ec55fa 100644 (file)
@@ -409,13 +409,14 @@ xfs_refcountbt_calc_size(
  */
 xfs_extlen_t
 xfs_refcountbt_max_size(
-       struct xfs_mount        *mp)
+       struct xfs_mount        *mp,
+       xfs_agblock_t           agblocks)
 {
        /* Bail out if we're uninitialized, which can happen in mkfs. */
        if (mp->m_refc_mxr[0] == 0)
                return 0;
 
-       return xfs_refcountbt_calc_size(mp, mp->m_sb.sb_agblocks);
+       return xfs_refcountbt_calc_size(mp, agblocks);
 }
 
 /*
@@ -430,22 +431,24 @@ xfs_refcountbt_calc_reserves(
 {
        struct xfs_buf          *agbp;
        struct xfs_agf          *agf;
+       xfs_agblock_t           agblocks;
        xfs_extlen_t            tree_len;
        int                     error;
 
        if (!xfs_sb_version_hasreflink(&mp->m_sb))
                return 0;
 
-       *ask += xfs_refcountbt_max_size(mp);
 
        error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
        if (error)
                return error;
 
        agf = XFS_BUF_TO_AGF(agbp);
+       agblocks = be32_to_cpu(agf->agf_length);
        tree_len = be32_to_cpu(agf->agf_refcount_blocks);
        xfs_buf_relse(agbp);
 
+       *ask += xfs_refcountbt_max_size(mp, agblocks);
        *used += tree_len;
 
        return error;
index 3be7768bd51a1c0ebd8c2ccc6e930a730bd39b54..9db008b955b7ed68bb62d8654073da0302668171 100644 (file)
@@ -66,7 +66,8 @@ extern void xfs_refcountbt_compute_maxlevels(struct xfs_mount *mp);
 
 extern xfs_extlen_t xfs_refcountbt_calc_size(struct xfs_mount *mp,
                unsigned long long len);
-extern xfs_extlen_t xfs_refcountbt_max_size(struct xfs_mount *mp);
+extern xfs_extlen_t xfs_refcountbt_max_size(struct xfs_mount *mp,
+               xfs_agblock_t agblocks);
 
 extern int xfs_refcountbt_calc_reserves(struct xfs_mount *mp,
                xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
index de25771764bac313ec514a882d6069de1063e5cc..74e5a54bc428fa27d485d88e871fdd77f8494252 100644 (file)
@@ -550,13 +550,14 @@ xfs_rmapbt_calc_size(
  */
 xfs_extlen_t
 xfs_rmapbt_max_size(
-       struct xfs_mount        *mp)
+       struct xfs_mount        *mp,
+       xfs_agblock_t           agblocks)
 {
        /* Bail out if we're uninitialized, which can happen in mkfs. */
        if (mp->m_rmap_mxr[0] == 0)
                return 0;
 
-       return xfs_rmapbt_calc_size(mp, mp->m_sb.sb_agblocks);
+       return xfs_rmapbt_calc_size(mp, agblocks);
 }
 
 /*
@@ -571,25 +572,24 @@ xfs_rmapbt_calc_reserves(
 {
        struct xfs_buf          *agbp;
        struct xfs_agf          *agf;
-       xfs_extlen_t            pool_len;
+       xfs_agblock_t           agblocks;
        xfs_extlen_t            tree_len;
        int                     error;
 
        if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
                return 0;
 
-       /* Reserve 1% of the AG or enough for 1 block per record. */
-       pool_len = max(mp->m_sb.sb_agblocks / 100, xfs_rmapbt_max_size(mp));
-       *ask += pool_len;
-
        error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
        if (error)
                return error;
 
        agf = XFS_BUF_TO_AGF(agbp);
+       agblocks = be32_to_cpu(agf->agf_length);
        tree_len = be32_to_cpu(agf->agf_rmap_blocks);
        xfs_buf_relse(agbp);
 
+       /* Reserve 1% of the AG or enough for 1 block per record. */
+       *ask += max(agblocks / 100, xfs_rmapbt_max_size(mp, agblocks));
        *used += tree_len;
 
        return error;
index 2a9ac472fb15a2408ca6d58addc6a5d439b61fe9..19c08e93304954d62c1c1dcdf35a36a49f38ee71 100644 (file)
@@ -60,7 +60,8 @@ extern void xfs_rmapbt_compute_maxlevels(struct xfs_mount *mp);
 
 extern xfs_extlen_t xfs_rmapbt_calc_size(struct xfs_mount *mp,
                unsigned long long len);
-extern xfs_extlen_t xfs_rmapbt_max_size(struct xfs_mount *mp);
+extern xfs_extlen_t xfs_rmapbt_max_size(struct xfs_mount *mp,
+               xfs_agblock_t agblocks);
 
 extern int xfs_rmapbt_calc_reserves(struct xfs_mount *mp,
                xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
index 93d12fa2670d53bf8b6bf23c51325d48467b8ab2..242e8091296daff7a029b4233d53db8241ecbcd7 100644 (file)
@@ -631,6 +631,20 @@ xfs_growfs_data_private(
        xfs_set_low_space_thresholds(mp);
        mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
 
+       /*
+        * If we expanded the last AG, free the per-AG reservation
+        * so we can reinitialize it with the new size.
+        */
+       if (new) {
+               struct xfs_perag        *pag;
+
+               pag = xfs_perag_get(mp, agno);
+               error = xfs_ag_resv_free(pag);
+               xfs_perag_put(pag);
+               if (error)
+                       goto out;
+       }
+
        /* Reserve AG metadata blocks. */
        error = xfs_fs_reserve_ag_blocks(mp);
        if (error && error != -ENOSPC)