summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-01-05 17:45:42 -0800
committerDarrick J. Wong <djwong@kernel.org>2021-03-25 17:08:39 -0700
commit9a54a8df2d4db4d358d0101b57ce570543c42b15 (patch)
tree6bc265bc1f729b498b3e27f6046926f37880bb36
parentcb702d8ea61d4a5ed068ce5b8a2289f900c890db (diff)
xfs: support storing records in the inode core root
Add the necessary flags and code so that we can support storing leaf records in the inode root block of a btree. This hasn't been necessary before, but the realtime rmapbt will need to be able to do this. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/libxfs/xfs_btree.c152
-rw-r--r--fs/xfs/libxfs/xfs_btree.h1
-rw-r--r--fs/xfs/libxfs/xfs_btree_staging.c4
3 files changed, 143 insertions, 14 deletions
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 2f79d92e8df8..a6db020b19a6 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -191,6 +191,11 @@ xfs_btree_check_block(
int level, /* level of the btree block */
struct xfs_buf *bp) /* buffer containing block, if any */
{
+ /* Don't check the inode-core root. */
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ level == cur->bc_nlevels - 1)
+ return 0;
+
if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
return xfs_btree_check_lblock(cur, block, level, bp);
else
@@ -1420,12 +1425,16 @@ xfs_btree_log_recs(
int first,
int last)
{
+ if (!bp) {
+ xfs_trans_log_inode(cur->bc_tp, cur->bc_ino.ip,
+ xfs_ilog_fbroot(cur->bc_ino.whichfork));
+ return;
+ }
xfs_trans_buf_set_type(cur->bc_tp, bp, XFS_BLFT_BTREE_BUF);
xfs_trans_log_buf(cur->bc_tp, bp,
xfs_btree_rec_offset(cur, first),
xfs_btree_rec_offset(cur, last + 1) - 1);
-
}
/*
@@ -2898,6 +2907,65 @@ xfs_btree_iroot_realloc(
}
/*
+ * Move the records from a root leaf block to a separate block.
+ *
+ * Trickery here: The amount of memory that we need per record for the incore
+ * root block changes when we convert a leaf block to an internal block.
+ * Therefore, we copy leaf records into the new btree block (cblock) before
+ * freeing the incore root block and changing the tree height.
+ *
+ * Once we've changed the tree height, we allocate a new incore root block
+ * (which will now be an internal root block) and populate it with a pointer to
+ * cblock and the relevant keys.
+ */
+STATIC void
+xfs_btree_promote_leaf_iroot(
+ struct xfs_btree_cur *cur,
+ struct xfs_btree_block *block,
+ struct xfs_buf *cbp,
+ union xfs_btree_ptr *cptr,
+ struct xfs_btree_block *cblock)
+{
+ union xfs_btree_rec *rp;
+ union xfs_btree_rec *crp;
+ union xfs_btree_key *kp;
+ union xfs_btree_ptr *pp;
+ size_t size;
+ int numrecs = xfs_btree_get_numrecs(block);
+
+ /* Copy the records from the leaf root into the new child block. */
+ rp = xfs_btree_rec_addr(cur, 1, block);
+ crp = xfs_btree_rec_addr(cur, 1, cblock);
+ xfs_btree_copy_recs(cur, crp, rp, numrecs);
+
+ /* Zap the old root and change the tree height. */
+ xfs_iroot_free(cur->bc_ino.ip, cur->bc_ino.whichfork);
+ cur->bc_nlevels++;
+ cur->bc_ptrs[1] = 1;
+
+ /*
+ * Allocate a new internal root block buffer and reinitialize it to
+ * point to a single new child.
+ */
+ size = cur->bc_ops->iroot_ops->size(cur->bc_mp, cur->bc_nlevels - 1, 1);
+ xfs_iroot_alloc(cur->bc_ino.ip, cur->bc_ino.whichfork, size);
+ block = xfs_btree_get_iroot(cur);
+ xfs_btree_init_block_int(cur->bc_mp, block, XFS_BUF_DADDR_NULL,
+ cur->bc_btnum, cur->bc_nlevels - 1, 1,
+ cur->bc_ino.ip->i_ino, cur->bc_flags);
+
+ pp = xfs_btree_ptr_addr(cur, 1, block);
+ kp = xfs_btree_key_addr(cur, 1, block);
+ xfs_btree_copy_ptrs(cur, pp, cptr, 1);
+ xfs_btree_get_keys(cur, cblock, kp);
+
+ /* Attach the new block to the cursor and log it. */
+ xfs_btree_setbuf(cur, 0, cbp);
+ xfs_btree_log_block(cur, cbp, XFS_BB_ALL_BITS);
+ xfs_btree_log_recs(cur, cbp, 1, numrecs);
+}
+
+/*
* Move the keys and pointers from a root block to a separate block.
*
* Since the keyptr size does not change, all we have to do is increase the
@@ -2981,7 +3049,7 @@ xfs_btree_new_iroot(
struct xfs_buf *cbp; /* buffer for cblock */
struct xfs_btree_block *block; /* btree block */
struct xfs_btree_block *cblock; /* child btree block */
- union xfs_btree_ptr *pp;
+ union xfs_btree_ptr aptr;
union xfs_btree_ptr nptr; /* new block addr */
int level; /* btree level */
int error; /* error return code */
@@ -2993,10 +3061,15 @@ xfs_btree_new_iroot(
level = cur->bc_nlevels - 1;
block = xfs_btree_get_iroot(cur);
- pp = xfs_btree_ptr_addr(cur, 1, block);
+ ASSERT(level > 0 || (cur->bc_flags & XFS_BTREE_IROOT_RECORDS));
+ if (level > 0)
+ aptr = *xfs_btree_ptr_addr(cur, 1, block);
+ else
+ aptr.l = cpu_to_be64(XFS_INO_TO_FSB(cur->bc_mp,
+ cur->bc_ino.ip->i_ino));
/* Allocate the new block. If we can't do it, we're toast. Give up. */
- error = cur->bc_ops->alloc_block(cur, pp, &nptr, stat);
+ error = cur->bc_ops->alloc_block(cur, &aptr, &nptr, stat);
if (error)
goto error0;
if (*stat == 0)
@@ -3021,10 +3094,14 @@ xfs_btree_new_iroot(
cblock->bb_u.s.bb_blkno = cpu_to_be64(cbp->b_bn);
}
- error = xfs_btree_promote_node_iroot(cur, block, level, cbp, &nptr,
- cblock);
- if (error)
- goto error0;
+ if (level > 0) {
+ error = xfs_btree_promote_node_iroot(cur, block, level, cbp,
+ &nptr, cblock);
+ if (error)
+ goto error0;
+ } else {
+ xfs_btree_promote_leaf_iroot(cur, block, cbp, &nptr, cblock);
+ }
*logflags |=
XFS_ILOG_CORE | xfs_ilog_fbroot(cur->bc_ino.whichfork);
@@ -3519,6 +3596,46 @@ error0:
}
/*
+ * Move the records from a child leaf block to the root block.
+ *
+ * Trickery here: The amount of memory we need per record for the incore root
+ * block changes when we convert a leaf block to an internal block. Therefore,
+ * we free the incore root block, change the tree height, allocate a new incore
+ * root, and copy the records from the doomed block into the new root.
+ */
+STATIC void
+xfs_btree_demote_leaf_child(
+ struct xfs_btree_cur *cur,
+ struct xfs_btree_block *cblock,
+ int numrecs)
+{
+ union xfs_btree_rec *rp;
+ union xfs_btree_rec *crp;
+ struct xfs_btree_block *block;
+ size_t size;
+
+ /* Zap the old root and change the tree height. */
+ xfs_iroot_free(cur->bc_ino.ip, cur->bc_ino.whichfork);
+ cur->bc_bufs[0] = NULL;
+ cur->bc_nlevels--;
+
+ /*
+ * Allocate a new internal root block buffer and reinitialize it with
+ * the leaf records in the child.
+ */
+ size = cur->bc_ops->iroot_ops->size(cur->bc_mp, 0, numrecs);
+ xfs_iroot_alloc(cur->bc_ino.ip, cur->bc_ino.whichfork, size);
+ block = xfs_btree_get_iroot(cur);
+ xfs_btree_init_block_int(cur->bc_mp, block, XFS_BUF_DADDR_NULL,
+ cur->bc_btnum, 0, numrecs, cur->bc_ino.ip->i_ino,
+ cur->bc_flags);
+
+ rp = xfs_btree_rec_addr(cur, 1, block);
+ crp = xfs_btree_rec_addr(cur, 1, cblock);
+ xfs_btree_copy_recs(cur, rp, crp, numrecs);
+}
+
+/*
* Move the keyptrs from a child node block to the root block.
*
* Since the keyptr size does not change, all we have to do is increase the
@@ -3599,14 +3716,19 @@ xfs_btree_kill_iroot(
#endif
ASSERT(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE);
- ASSERT(cur->bc_nlevels > 1);
+ ASSERT((cur->bc_flags & XFS_BTREE_IROOT_RECORDS) ||
+ cur->bc_nlevels > 1);
/*
* Don't deal with the root block needs to be a leaf case.
* We're just going to turn the thing back into extents anyway.
*/
level = cur->bc_nlevels - 1;
- if (level == 1)
+ if (level == 1 && !(cur->bc_flags & XFS_BTREE_IROOT_RECORDS))
+ goto out0;
+
+ /* If we're already a leaf, jump out. */
+ if (level == 0)
goto out0;
/*
@@ -3636,9 +3758,13 @@ xfs_btree_kill_iroot(
ASSERT(xfs_btree_ptr_is_null(cur, &ptr));
#endif
- error = xfs_btree_demote_node_child(cur, cblock, level, numrecs);
- if (error)
- return error;
+ if (level > 1) {
+ error = xfs_btree_demote_node_child(cur, cblock, level,
+ numrecs);
+ if (error)
+ return error;
+ } else
+ xfs_btree_demote_leaf_child(cur, cblock, numrecs);
error = xfs_btree_free_block(cur, cbp);
if (error)
diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h
index 32db3afac33e..ffe8aa03addb 100644
--- a/fs/xfs/libxfs/xfs_btree.h
+++ b/fs/xfs/libxfs/xfs_btree.h
@@ -252,6 +252,7 @@ typedef struct xfs_btree_cur
* is dynamically allocated and must be freed when the cursor is deleted.
*/
#define XFS_BTREE_STAGING (1<<5)
+#define XFS_BTREE_IROOT_RECORDS (1<<6) /* iroot can store records */
#define XFS_BTREE_NOERROR 0
diff --git a/fs/xfs/libxfs/xfs_btree_staging.c b/fs/xfs/libxfs/xfs_btree_staging.c
index b2058f42e2c1..fe0fd1363770 100644
--- a/fs/xfs/libxfs/xfs_btree_staging.c
+++ b/fs/xfs/libxfs/xfs_btree_staging.c
@@ -687,7 +687,9 @@ xfs_btree_bload_compute_geometry(
*
* Note that bmap btrees forbid records in the root.
*/
- if (level != 0 && nr_this_level <= avg_per_block) {
+ if ((level != 0 ||
+ (cur->bc_flags & XFS_BTREE_IROOT_RECORDS)) &&
+ nr_this_level <= avg_per_block) {
nr_blocks++;
break;
}