@@ -646,6 +646,17 @@ xfs_btree_ptr_addr(
((char *)block + xfs_btree_ptr_offset(cur, n, level));
}
+struct xfs_ifork *
+xfs_btree_ifork_ptr(
+ struct xfs_btree_cur *cur)
+{
+ ASSERT(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE);
+
+ if (cur->bc_flags & XFS_BTREE_STAGING)
+ return cur->bc_private.b.ifake->if_fork;
+ return XFS_IFORK_PTR(cur->bc_private.b.ip, cur->bc_private.b.whichfork);
+}
+
/*
* Get the root block which is stored in the inode.
*
@@ -656,9 +667,8 @@ STATIC struct xfs_btree_block *
xfs_btree_get_iroot(
struct xfs_btree_cur *cur)
{
- struct xfs_ifork *ifp;
+ struct xfs_ifork *ifp = xfs_btree_ifork_ptr(cur);
- ifp = XFS_IFORK_PTR(cur->bc_private.b.ip, cur->bc_private.b.whichfork);
return (struct xfs_btree_block *)ifp->if_broot;
}
@@ -5045,3 +5055,52 @@ xfs_btree_commit_afakeroot(
cur->bc_ops = ops;
cur->bc_flags &= ~XFS_BTREE_STAGING;
}
+/*
+ * Initialize an inode-rooted btree cursor with the given fake root @ifake, and
+ * prepare @ops for overriding by duplicating them into @new_ops. The caller
+ * can replace pointers in @new_ops as necessary once this call completes.
+ * Note that staging cursors can only be used for bulk loading.
+ */
+void
+xfs_btree_stage_ifakeroot(
+ struct xfs_btree_cur *cur,
+ struct xbtree_ifakeroot *ifake,
+ const struct xfs_btree_ops *ops,
+ struct xfs_btree_ops **new_ops)
+{
+ ASSERT(!(cur->bc_flags & XFS_BTREE_STAGING));
+ ASSERT(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE);
+
+ *new_ops = kmem_alloc(sizeof(struct xfs_btree_ops), KM_NOFS);
+ memcpy(*new_ops, ops, sizeof(struct xfs_btree_ops));
+ (*new_ops)->alloc_block = xfs_btree_fakeroot_alloc_block;
+ (*new_ops)->free_block = xfs_btree_fakeroot_free_block;
+ (*new_ops)->init_ptr_from_cur = xfs_btree_fakeroot_init_ptr_from_cur;
+ (*new_ops)->dup_cursor = xfs_btree_fakeroot_dup_cursor;
+
+ cur->bc_private.b.ifake = ifake;
+ cur->bc_nlevels = ifake->if_levels;
+ cur->bc_ops = *new_ops;
+ cur->bc_flags |= XFS_BTREE_STAGING;
+}
+
+/*
+ * Transform an inode-rooted staging btree cursor back into a regular btree
+ * cursor. Caller is responsible for logging changes before this.
+ */
+void
+xfs_btree_commit_ifakeroot(
+ struct xfs_btree_cur *cur,
+ int whichfork,
+ const struct xfs_btree_ops *ops)
+{
+ ASSERT(cur->bc_flags & XFS_BTREE_STAGING);
+
+ trace_xfs_btree_commit_ifakeroot(cur);
+
+ kmem_free((void *)cur->bc_ops);
+ cur->bc_private.b.ifake = NULL;
+ cur->bc_private.b.whichfork = whichfork;
+ cur->bc_ops = ops;
+ cur->bc_flags &= ~XFS_BTREE_STAGING;
+}
@@ -10,6 +10,7 @@ struct xfs_buf;
struct xfs_inode;
struct xfs_mount;
struct xfs_trans;
+struct xfs_ifork;
extern kmem_zone_t *xfs_btree_cur_zone;
@@ -195,6 +196,18 @@ struct xfs_btree_priv_ag { /* needed for BNO, CNT, INO */
union xfs_btree_cur_private priv;
};
+/* Private information for an inode-rooted btree. */
+struct xfs_btree_priv_inode { /* needed for BMAP */
+ struct xfs_inode *ip; /* pointer to our inode */
+ struct xbtree_ifakeroot *ifake; /* fake inode fork */
+ int allocated; /* count of alloced */
+ short forksize; /* fork's inode space */
+ char whichfork; /* data or attr fork */
+ char flags; /* flags */
+#define XFS_BTCUR_BPRV_WASDEL (1<<0) /* was delayed */
+#define XFS_BTCUR_BPRV_INVALID_OWNER (1<<1) /* for ext swap */
+};
+
/*
* Btree cursor structure.
* This collects all information needed by the btree code in one place.
@@ -217,15 +230,7 @@ typedef struct xfs_btree_cur
int bc_statoff; /* offset of btre stats array */
union {
struct xfs_btree_priv_ag a;
- struct { /* needed for BMAP */
- struct xfs_inode *ip; /* pointer to our inode */
- int allocated; /* count of alloced */
- short forksize; /* fork's inode space */
- char whichfork; /* data or attr fork */
- char flags; /* flags */
-#define XFS_BTCUR_BPRV_WASDEL (1<<0) /* was delayed */
-#define XFS_BTCUR_BPRV_INVALID_OWNER (1<<1) /* for ext swap */
- } b;
+ struct xfs_btree_priv_inode b;
} bc_private; /* per-btree type data */
} xfs_btree_cur_t;
@@ -525,6 +530,7 @@ union xfs_btree_key *xfs_btree_high_key_from_key(struct xfs_btree_cur *cur,
int xfs_btree_has_record(struct xfs_btree_cur *cur, union xfs_btree_irec *low,
union xfs_btree_irec *high, bool *exists);
bool xfs_btree_has_more_records(struct xfs_btree_cur *cur);
+struct xfs_ifork *xfs_btree_ifork_ptr(struct xfs_btree_cur *cur);
/* Fake root for an AG-rooted btree. */
struct xbtree_afakeroot {
@@ -547,4 +553,33 @@ void xfs_btree_commit_afakeroot(struct xfs_btree_cur *cur,
struct xfs_buf *agbp,
const struct xfs_btree_ops *ops);
+/* Fake root for an inode-rooted btree. */
+struct xbtree_ifakeroot {
+ /* Fake inode fork. */
+ struct xfs_ifork *if_fork;
+
+ /* Number of blocks used by the btree. */
+ int64_t if_blocks;
+
+ /* Height of the new btree. */
+ unsigned int if_levels;
+
+ /* Number of bytes available for this fork in the inode. */
+ unsigned int if_fork_size;
+
+ /* Fork format. */
+ unsigned int if_format;
+
+ /* Number of records. */
+ unsigned int if_extents;
+};
+
+/* Cursor interactions with with fake roots for inode-rooted btrees. */
+void xfs_btree_stage_ifakeroot(struct xfs_btree_cur *cur,
+ struct xbtree_ifakeroot *ifake,
+ const struct xfs_btree_ops *ops,
+ struct xfs_btree_ops **new_ops);
+void xfs_btree_commit_ifakeroot(struct xfs_btree_cur *cur, int whichfork,
+ const struct xfs_btree_ops *ops);
+
#endif /* __XFS_BTREE_H__ */
@@ -3637,6 +3637,39 @@ TRACE_EVENT(xfs_btree_commit_afakeroot,
__entry->agbno)
)
+TRACE_EVENT(xfs_btree_commit_ifakeroot,
+ TP_PROTO(struct xfs_btree_cur *cur),
+ TP_ARGS(cur),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_btnum_t, btnum)
+ __field(xfs_agnumber_t, agno)
+ __field(xfs_agino_t, agino)
+ __field(unsigned int, levels)
+ __field(unsigned int, blocks)
+ __field(int, whichfork)
+ ),
+ TP_fast_assign(
+ __entry->dev = cur->bc_mp->m_super->s_dev;
+ __entry->btnum = cur->bc_btnum;
+ __entry->agno = XFS_INO_TO_AGNO(cur->bc_mp,
+ cur->bc_private.b.ip->i_ino);
+ __entry->agino = XFS_INO_TO_AGINO(cur->bc_mp,
+ cur->bc_private.b.ip->i_ino);
+ __entry->levels = cur->bc_private.b.ifake->if_levels;
+ __entry->blocks = cur->bc_private.b.ifake->if_blocks;
+ __entry->whichfork = cur->bc_private.b.whichfork;
+ ),
+ TP_printk("dev %d:%d btree %s ag %u agino %u whichfork %s levels %u blocks %u",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __print_symbolic(__entry->btnum, XFS_BTNUM_STRINGS),
+ __entry->agno,
+ __entry->agino,
+ __entry->whichfork == XFS_ATTR_FORK ? "attr" : "data",
+ __entry->levels,
+ __entry->blocks)
+)
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH