xfs: fix internal error from AGFL exhaustion
[platform/kernel/linux-starfive.git] / fs / xfs / libxfs / xfs_alloc.c
index 3069194..100ab59 100644 (file)
@@ -2275,16 +2275,37 @@ xfs_alloc_min_freelist(
 
        ASSERT(mp->m_alloc_maxlevels > 0);
 
+       /*
+        * For a btree shorter than the maximum height, the worst case is that
+        * every level gets split and a new level is added, then while inserting
+        * another entry to refill the AGFL, every level under the old root gets
+        * split again. This is:
+        *
+        *   (full height split reservation) + (AGFL refill split height)
+        * = (current height + 1) + (current height - 1)
+        * = (new height) + (new height - 2)
+        * = 2 * new height - 2
+        *
+        * For a btree of maximum height, the worst case is that every level
+        * under the root gets split, then while inserting another entry to
+        * refill the AGFL, every level under the root gets split again. This is
+        * also:
+        *
+        *   2 * (current height - 1)
+        * = 2 * (new height - 1)
+        * = 2 * new height - 2
+        */
+
        /* space needed by-bno freespace btree */
        min_free = min_t(unsigned int, levels[XFS_BTNUM_BNOi] + 1,
-                                      mp->m_alloc_maxlevels);
+                                      mp->m_alloc_maxlevels) * 2 - 2;
        /* space needed by-size freespace btree */
        min_free += min_t(unsigned int, levels[XFS_BTNUM_CNTi] + 1,
-                                      mp->m_alloc_maxlevels);
+                                      mp->m_alloc_maxlevels) * 2 - 2;
        /* space needed reverse mapping used space btree */
        if (xfs_has_rmapbt(mp))
                min_free += min_t(unsigned int, levels[XFS_BTNUM_RMAPi] + 1,
-                                               mp->m_rmap_maxlevels);
+                                               mp->m_rmap_maxlevels) * 2 - 2;
 
        return min_free;
 }