xfs: fix up inode cowblocks tracking tracepoints
[cascardo/linux.git] / fs / xfs / xfs_fsops.c
index 94ac06f..93d12fa 100644 (file)
@@ -43,6 +43,7 @@
 #include "xfs_log.h"
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
+#include "xfs_ag_resv.h"
 
 /*
  * File system operations
@@ -108,7 +109,9 @@ xfs_fs_geometry(
                        (xfs_sb_version_hassparseinodes(&mp->m_sb) ?
                                XFS_FSOP_GEOM_FLAGS_SPINODES : 0) |
                        (xfs_sb_version_hasrmapbt(&mp->m_sb) ?
-                               XFS_FSOP_GEOM_FLAGS_RMAPBT : 0);
+                               XFS_FSOP_GEOM_FLAGS_RMAPBT : 0) |
+                       (xfs_sb_version_hasreflink(&mp->m_sb) ?
+                               XFS_FSOP_GEOM_FLAGS_REFLINK : 0);
                geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ?
                                mp->m_sb.sb_logsectsize : BBSIZE;
                geo->rtsectsize = mp->m_sb.sb_blocksize;
@@ -259,6 +262,12 @@ xfs_growfs_data_private(
                agf->agf_longest = cpu_to_be32(tmpsize);
                if (xfs_sb_version_hascrc(&mp->m_sb))
                        uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
+               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       agf->agf_refcount_root = cpu_to_be32(
+                                       xfs_refc_block(mp));
+                       agf->agf_refcount_level = cpu_to_be32(1);
+                       agf->agf_refcount_blocks = cpu_to_be32(1);
+               }
 
                error = xfs_bwrite(bp);
                xfs_buf_relse(bp);
@@ -450,6 +459,17 @@ xfs_growfs_data_private(
                        rrec->rm_offset = 0;
                        be16_add_cpu(&block->bb_numrecs, 1);
 
+                       /* account for refc btree root */
+                       if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                               rrec = XFS_RMAP_REC_ADDR(block, 5);
+                               rrec->rm_startblock = cpu_to_be32(
+                                               xfs_refc_block(mp));
+                               rrec->rm_blockcount = cpu_to_be32(1);
+                               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+                               rrec->rm_offset = 0;
+                               be16_add_cpu(&block->bb_numrecs, 1);
+                       }
+
                        error = xfs_bwrite(bp);
                        xfs_buf_relse(bp);
                        if (error)
@@ -507,6 +527,28 @@ xfs_growfs_data_private(
                                goto error0;
                }
 
+               /*
+                * refcount btree root block
+                */
+               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       bp = xfs_growfs_get_hdr_buf(mp,
+                               XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
+                               BTOBB(mp->m_sb.sb_blocksize), 0,
+                               &xfs_refcountbt_buf_ops);
+                       if (!bp) {
+                               error = -ENOMEM;
+                               goto error0;
+                       }
+
+                       xfs_btree_init_block(mp, bp, XFS_REFC_CRC_MAGIC,
+                                            0, 0, agno,
+                                            XFS_BTREE_CRC_BLOCKS);
+
+                       error = xfs_bwrite(bp);
+                       xfs_buf_relse(bp);
+                       if (error)
+                               goto error0;
+               }
        }
        xfs_trans_agblocks_delta(tp, nfree);
        /*
@@ -589,6 +631,11 @@ xfs_growfs_data_private(
        xfs_set_low_space_thresholds(mp);
        mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
 
+       /* Reserve AG metadata blocks. */
+       error = xfs_fs_reserve_ag_blocks(mp);
+       if (error && error != -ENOSPC)
+               goto out;
+
        /* update secondary superblocks. */
        for (agno = 1; agno < nagcount; agno++) {
                error = 0;
@@ -639,6 +686,8 @@ xfs_growfs_data_private(
                        continue;
                }
        }
+
+ out:
        return saved_error ? saved_error : error;
 
  error0:
@@ -948,3 +997,59 @@ xfs_do_force_shutdown(
        "Please umount the filesystem and rectify the problem(s)");
        }
 }
+
+/*
+ * Reserve free space for per-AG metadata.
+ */
+int
+xfs_fs_reserve_ag_blocks(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       struct xfs_perag        *pag;
+       int                     error = 0;
+       int                     err2;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               pag = xfs_perag_get(mp, agno);
+               err2 = xfs_ag_resv_init(pag);
+               xfs_perag_put(pag);
+               if (err2 && !error)
+                       error = err2;
+       }
+
+       if (error && error != -ENOSPC) {
+               xfs_warn(mp,
+       "Error %d reserving per-AG metadata reserve pool.", error);
+               xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+       }
+
+       return error;
+}
+
+/*
+ * Free space reserved for per-AG metadata.
+ */
+int
+xfs_fs_unreserve_ag_blocks(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       struct xfs_perag        *pag;
+       int                     error = 0;
+       int                     err2;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               pag = xfs_perag_get(mp, agno);
+               err2 = xfs_ag_resv_free(pag);
+               xfs_perag_put(pag);
+               if (err2 && !error)
+                       error = err2;
+       }
+
+       if (error)
+               xfs_warn(mp,
+       "Error %d freeing per-AG metadata reserve pool.", error);
+
+       return error;
+}