]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: support storing records in the inode core root
authorDarrick J. Wong <djwong@kernel.org>
Mon, 23 Sep 2024 20:41:49 +0000 (13:41 -0700)
committerChristoph Hellwig <hch@lst.de>
Wed, 9 Oct 2024 13:55:44 +0000 (15:55 +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 a001c541626c08d84f863773d5c36ce64041969d..35669639519bcd4c6c6e27fec02376f9eb9f0a48 100644 (file)
@@ -1537,12 +1537,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);
-
 }
 
 /*
@@ -3078,6 +3082,59 @@ xfs_btree_split(
 #define xfs_btree_split        __xfs_btree_split
 #endif /* __KERNEL__ */
 
+/* Move the records from a root leaf block to a separate block. */
+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;
+       struct xfs_btree_block  *broot;
+       int                     numrecs = xfs_btree_get_numrecs(block);
+
+       /* Copy the records from the leaf broot 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);
+
+       /*
+        * Increment the tree height.
+        *
+        * Trickery here: The amount of memory that we need per record for the
+        * ifork's btree root block may change when we convert the broot from a
+        * leaf to a node block.  Free the existing leaf broot so that nobody
+        * thinks we need to migrate node pointers when we realloc the broot
+        * buffer after bumping nlevels.
+        */
+       cur->bc_ops->broot_realloc(cur, 0);
+       cur->bc_nlevels++;
+       cur->bc_levels[1].ptr = 1;
+
+       /*
+        * Allocate a new node broot and initialize it to point to the new
+        * child block.
+        */
+       broot = cur->bc_ops->broot_realloc(cur, 1);
+       xfs_btree_init_block(cur->bc_mp, broot, cur->bc_ops,
+                       cur->bc_nlevels - 1, 1, cur->bc_ino.ip->i_ino);
+
+       pp = xfs_btree_ptr_addr(cur, 1, broot);
+       kp = xfs_btree_key_addr(cur, 1, broot);
+       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.
  *
@@ -3163,7 +3220,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 */
@@ -3175,10 +3232,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)
@@ -3204,10 +3266,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);
        *stat = 1;
@@ -3709,6 +3775,43 @@ error0:
        return error;
 }
 
+/* Move the records from a child leaf block to the root block. */
+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  *broot;
+
+       /*
+        * Decrease the tree height.
+        *
+        * Trickery here: The amount of memory that we need per record for the
+        * ifork's btree root block may change when we convert the broot from a
+        * node to a leaf.  Free the old node broot so that we can get a fresh
+        * leaf broot.
+        */
+       cur->bc_ops->broot_realloc(cur, 0);
+       cur->bc_nlevels--;
+
+       /*
+        * Allocate a new leaf broot and copy the records from the old child.
+        * Detach the old child from the cursor.
+        */
+       broot = cur->bc_ops->broot_realloc(cur, numrecs);
+       xfs_btree_init_block(cur->bc_mp, broot, cur->bc_ops, 0, numrecs,
+                       cur->bc_ino.ip->i_ino);
+
+       rp = xfs_btree_rec_addr(cur, 1, broot);
+       crp = xfs_btree_rec_addr(cur, 1, cblock);
+       xfs_btree_copy_recs(cur, rp, crp, numrecs);
+
+       cur->bc_levels[0].bp = NULL;
+}
+
 /*
  * Move the keyptrs from a child node block to the root block.
  *
@@ -3787,14 +3890,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;
 
        /*
@@ -3824,9 +3932,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 b3bae649726e1c07532456088e6459508f4d3f6f..e7d1cb020b862cd2ff2453089c09a4894b0c31ec 100644 (file)
@@ -233,7 +233,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;
                        }