]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: support storing records in the inode core root
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:11:32 +0000 (21:11 -0700)
committerChristoph Hellwig <hch@lst.de>
Sat, 10 Aug 2024 08:44:22 +0000 (10:44 +0200)
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>
fs/xfs/libxfs/xfs_btree.c
fs/xfs/libxfs/xfs_btree.h
fs/xfs/libxfs/xfs_btree_staging.c

index a87b604aeb775753a327ec0a8d45986a6b4d8db8..2920fcf6546a7a5f7ac2ed02c50ea8a33a645f49 100644 (file)
@@ -1549,12 +1549,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);
-
 }
 
 /*
@@ -3101,6 +3105,64 @@ xfs_btree_iroot_realloc(
                        cur->bc_ops->iroot_ops, rec_diff);
 }
 
+/*
+ * 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_levels[1].ptr = 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(cur->bc_mp, block, cur->bc_ops,
+                       cur->bc_nlevels - 1, 1, cur->bc_ino.ip->i_ino);
+
+       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.
  *
@@ -3185,7 +3247,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 */
@@ -3197,10 +3259,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_ops->geom_flags & XFS_BTGEO_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 = xfs_btree_alloc_block(cur, pp, &nptr, stat);
+       error = xfs_btree_alloc_block(cur, &aptr, &nptr, stat);
        if (error)
                goto error0;
        if (*stat == 0)
@@ -3226,10 +3293,14 @@ xfs_btree_new_iroot(
                        cblock->bb_u.s.bb_blkno = bno;
        }
 
-       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);
@@ -3732,6 +3803,45 @@ error0:
        return error;
 }
 
+/*
+ * 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_levels[0].bp = 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(cur->bc_mp, block, cur->bc_ops, 0, numrecs,
+                       cur->bc_ino.ip->i_ino);
+
+       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.
  *
@@ -3813,14 +3923,19 @@ xfs_btree_kill_iroot(
 #endif
 
        ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_INODE);
-       ASSERT(cur->bc_nlevels > 1);
+       ASSERT((cur->bc_ops->geom_flags & XFS_BTGEO_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_ops->geom_flags & XFS_BTGEO_IROOT_RECORDS))
+               goto out0;
+
+       /* If we're already a leaf, jump out. */
+       if (level == 0)
                goto out0;
 
        /*
@@ -3850,9 +3965,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)
index f0cbc806b056146a0d0e11a9383dc6ea75fc5065..1f3aac749225d1ee4209d27b4c25a0d709aa14b0 100644 (file)
@@ -220,7 +220,7 @@ struct xfs_btree_ops {
 
 /* btree geometry flags */
 #define XFS_BTGEO_OVERLAPPING          (1U << 0) /* overlapping intervals */
-
+#define XFS_BTGEO_IROOT_RECORDS                (1U << 1) /* iroot can store records */
 
 union xfs_btree_irec {
        struct xfs_alloc_rec_incore     a;
index 6949297031529e37dcaa98b389d8f21526130f82..4c0f0dc0a19cbe9885879f54fa96cd31aae882ec 100644 (file)
@@ -607,7 +607,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_ops->geom_flags & XFS_BTGEO_IROOT_RECORDS)) &&
+                           nr_this_level <= avg_per_block) {
                                nr_blocks++;
                                break;
                        }