@@ -379,7 +379,10 @@ xfs_btree_del_cursor(
kmem_free(cur->bc_ops);
if (!(cur->bc_flags & XFS_BTREE_LONG_PTRS) && cur->bc_ag.pag)
xfs_perag_put(cur->bc_ag.pag);
- kmem_cache_free(xfs_btree_cur_zone, cur);
+ if (cur->bc_maxlevels > XFS_BTREE_CUR_ZONE_MAXLEVELS)
+ kmem_free(cur);
+ else
+ kmem_cache_free(xfs_btree_cur_zone, cur);
}
/*
@@ -4931,6 +4934,32 @@ xfs_btree_has_more_records(
return block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK);
}
+/* Compute the maximum allowed height for a given btree type. */
+static unsigned int
+xfs_btree_maxlevels(
+ struct xfs_mount *mp,
+ xfs_btnum_t btnum)
+{
+ switch (btnum) {
+ case XFS_BTNUM_BNO:
+ case XFS_BTNUM_CNT:
+ return mp->m_ag_maxlevels;
+ case XFS_BTNUM_BMAP:
+ return max(mp->m_bm_maxlevels[XFS_DATA_FORK],
+ mp->m_bm_maxlevels[XFS_ATTR_FORK]);
+ case XFS_BTNUM_INO:
+ case XFS_BTNUM_FINO:
+ return M_IGEO(mp)->inobt_maxlevels;
+ case XFS_BTNUM_RMAP:
+ return mp->m_rmap_maxlevels;
+ case XFS_BTNUM_REFC:
+ return mp->m_refc_maxlevels;
+ default:
+ ASSERT(0);
+ return XFS_BTREE_MAXLEVELS;
+ }
+}
+
/* Allocate a new btree cursor of the appropriate size. */
struct xfs_btree_cur *
xfs_btree_alloc_cursor(
@@ -4939,13 +4968,18 @@ xfs_btree_alloc_cursor(
xfs_btnum_t btnum)
{
struct xfs_btree_cur *cur;
+ unsigned int maxlevels = xfs_btree_maxlevels(mp, btnum);
- cur = kmem_cache_zalloc(xfs_btree_cur_zone, GFP_NOFS | __GFP_NOFAIL);
+ if (maxlevels > XFS_BTREE_CUR_ZONE_MAXLEVELS)
+ cur = kmem_zalloc(xfs_btree_cur_sizeof(maxlevels), KM_NOFS);
+ else
+ cur = kmem_cache_zalloc(xfs_btree_cur_zone,
+ GFP_NOFS | __GFP_NOFAIL);
cur->bc_tp = tp;
cur->bc_mp = mp;
cur->bc_btnum = btnum;
cur->bc_blocklog = mp->m_sb.sb_blocklog;
- cur->bc_maxlevels = XFS_BTREE_MAXLEVELS;
+ cur->bc_maxlevels = maxlevels;
return cur;
}
@@ -94,6 +94,12 @@ uint32_t xfs_btree_magic(int crc, xfs_btnum_t btnum);
#define XFS_BTREE_MAXLEVELS 9 /* max of all btrees */
+/*
+ * The btree cursor zone hands out cursors that can handle up to this many
+ * levels. This is the known maximum for all btree types.
+ */
+#define XFS_BTREE_CUR_ZONE_MAXLEVELS (9)
+
struct xfs_btree_ops {
/* size of the key and record structures */
size_t key_len;
@@ -1966,8 +1966,8 @@ xfs_init_zones(void)
goto out_destroy_log_ticket_zone;
xfs_btree_cur_zone = kmem_cache_create("xfs_btree_cur",
- xfs_btree_cur_sizeof(XFS_BTREE_MAXLEVELS),
- 0, 0, NULL);
+ xfs_btree_cur_sizeof(XFS_BTREE_CUR_ZONE_MAXLEVELS),
+ 0, 0, NULL);
if (!xfs_btree_cur_zone)
goto out_destroy_bmap_free_item_zone;