xfs: rmap btree requires more reserved free space
[deliverable/linux.git] / fs / xfs / libxfs / xfs_alloc.c
index dba7ce4b4be88bb4c3cb4a7da92c99b68f08a252..73ab1ea1270d6f730f47368b8a3823d03c7bd257 100644 (file)
@@ -62,6 +62,70 @@ xfs_prealloc_blocks(
        return XFS_IBT_BLOCK(mp) + 1;
 }
 
+/*
+ * In order to avoid ENOSPC-related deadlock caused by out-of-order locking of
+ * AGF buffer (PV 947395), we place constraints on the relationship among
+ * actual allocations for data blocks, freelist blocks, and potential file data
+ * bmap btree blocks. However, these restrictions may result in no actual space
+ * allocated for a delayed extent, for example, a data block in a certain AG is
+ * allocated but there is no additional block for the additional bmap btree
+ * block due to a split of the bmap btree of the file. The result of this may
+ * lead to an infinite loop when the file gets flushed to disk and all delayed
+ * extents need to be actually allocated. To get around this, we explicitly set
+ * aside a few blocks which will not be reserved in delayed allocation.
+ *
+ * When rmap is disabled, we need to reserve 4 fsbs _per AG_ for the freelist
+ * and 4 more to handle a potential split of the file's bmap btree.
+ *
+ * When rmap is enabled, we must also be able to handle two rmap btree inserts
+ * to record both the file data extent and a new bmbt block.  The bmbt block
+ * might not be in the same AG as the file data extent.  In the worst case
+ * the bmap btree splits multiple levels and all the new blocks come from
+ * different AGs, so set aside enough to handle rmap btree splits in all AGs.
+ */
+unsigned int
+xfs_alloc_set_aside(
+       struct xfs_mount        *mp)
+{
+       unsigned int            blocks;
+
+       blocks = 4 + (mp->m_sb.sb_agcount * XFS_ALLOC_AGFL_RESERVE);
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               blocks += mp->m_sb.sb_agcount * mp->m_rmap_maxlevels;
+       return blocks;
+}
+
+/*
+ * When deciding how much space to allocate out of an AG, we limit the
+ * allocation maximum size to the size the AG. However, we cannot use all the
+ * blocks in the AG - some are permanently used by metadata. These
+ * blocks are generally:
+ *     - the AG superblock, AGF, AGI and AGFL
+ *     - the AGF (bno and cnt) and AGI btree root blocks, and optionally
+ *       the AGI free inode and rmap btree root blocks.
+ *     - blocks on the AGFL according to xfs_alloc_set_aside() limits
+ *     - the rmapbt root block
+ *
+ * The AG headers are sector sized, so the amount of space they take up is
+ * dependent on filesystem geometry. The others are all single blocks.
+ */
+unsigned int
+xfs_alloc_ag_max_usable(
+       struct xfs_mount        *mp)
+{
+       unsigned int            blocks;
+
+       blocks = XFS_BB_TO_FSB(mp, XFS_FSS_TO_BB(mp, 4)); /* ag headers */
+       blocks += XFS_ALLOC_AGFL_RESERVE;
+       blocks += 3;                    /* AGF, AGI btree root blocks */
+       if (xfs_sb_version_hasfinobt(&mp->m_sb))
+               blocks++;               /* finobt root block */
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               blocks++;               /* rmap root block */
+
+       return mp->m_sb.sb_agblocks - blocks;
+}
+
 /*
  * Lookup the record equal to [bno, len] in the btree given by cur.
  */
@@ -1904,6 +1968,11 @@ xfs_alloc_min_freelist(
        /* space needed by-size freespace btree */
        min_free += min_t(unsigned int, pag->pagf_levels[XFS_BTNUM_CNTi] + 1,
                                       mp->m_ag_maxlevels);
+       /* space needed reverse mapping used space btree */
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               min_free += min_t(unsigned int,
+                                 pag->pagf_levels[XFS_BTNUM_RMAPi] + 1,
+                                 mp->m_rmap_maxlevels);
 
        return min_free;
 }
This page took 0.025679 seconds and 5 git commands to generate.