]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: hoist the code that moves the incore inode fork broot memory
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:11:28 +0000 (21:11 -0700)
committerChristoph Hellwig <hch@lst.de>
Mon, 12 Aug 2024 07:35:27 +0000 (09:35 +0200)
Whenever we change the size of the memory buffer holding an inode fork
btree root block, we have to copy the contents over.  Refactor all this
into a single function that handles both, in preparation for making
xfs_iroot_realloc more generic.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/libxfs/xfs_inode_fork.c

index e4175f46f1dab72741252f4add60ad2a08589427..58135d238754ed84382eef53b73b3e5a2c8b32be 100644 (file)
@@ -387,6 +387,50 @@ xfs_iroot_free(
        ifp->if_broot = NULL;
 }
 
+/* Move the bmap btree root from one incore buffer to another. */
+static void
+xfs_ifork_move_broot(
+       struct xfs_inode        *ip,
+       int                     whichfork,
+       struct xfs_btree_block  *dst_broot,
+       size_t                  dst_bytes,
+       struct xfs_btree_block  *src_broot,
+       size_t                  src_bytes,
+       unsigned int            numrecs)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       void                    *dptr;
+       void                    *sptr;
+
+       ASSERT(xfs_bmap_bmdr_space(src_broot) <= xfs_inode_fork_size(ip, whichfork));
+
+       /*
+        * We always have to move the pointers because they are not butted
+        * against the btree block header.
+        */
+       if (numrecs) {
+               sptr = xfs_bmap_broot_ptr_addr(mp, src_broot, 1, src_bytes);
+               dptr = xfs_bmap_broot_ptr_addr(mp, dst_broot, 1, dst_bytes);
+               memmove(dptr, sptr, numrecs * sizeof(xfs_fsblock_t));
+       }
+
+       if (src_broot == dst_broot)
+               return;
+
+       /*
+        * If the root is being totally relocated, we have to migrate the block
+        * header and the keys that come after it.
+        */
+       memcpy(dst_broot, src_broot, xfs_bmbt_block_len(mp));
+
+       /* Now copy the keys, which come right after the header. */
+       if (numrecs) {
+               sptr = xfs_bmbt_key_addr(mp, src_broot, 1);
+               dptr = xfs_bmbt_key_addr(mp, dst_broot, 1);
+               memcpy(dptr, sptr, numrecs * sizeof(struct xfs_bmbt_key));
+       }
+}
+
 /*
  * Reallocate the space for if_broot based on the number of records
  * being added or deleted as indicated in rec_diff.  Move the records
@@ -413,12 +457,11 @@ xfs_iroot_realloc(
 {
        struct xfs_mount        *mp = ip->i_mount;
        int                     cur_max;
-       struct xfs_ifork        *ifp;
+       struct xfs_ifork        *ifp = xfs_ifork_ptr(ip, whichfork);
        struct xfs_btree_block  *new_broot;
        int                     new_max;
        size_t                  new_size;
-       char                    *np;
-       char                    *op;
+       size_t                  old_size = ifp->if_broot_bytes;
 
        /*
         * Handle the degenerate case quietly.
@@ -427,13 +470,12 @@ xfs_iroot_realloc(
                return;
        }
 
-       ifp = xfs_ifork_ptr(ip, whichfork);
        if (rec_diff > 0) {
                /*
                 * If there wasn't any memory allocated before, just
                 * allocate it now and get out.
                 */
-               if (ifp->if_broot_bytes == 0) {
+               if (old_size == 0) {
                        new_size = xfs_bmap_broot_space_calc(mp, rec_diff);
                        xfs_iroot_alloc(ip, whichfork, new_size);
                        return;
@@ -442,22 +484,16 @@ xfs_iroot_realloc(
                /*
                 * If there is already an existing if_broot, then we need
                 * to realloc() it and shift the pointers to their new
-                * location.  The records don't change location because
-                * they are kept butted up against the btree block header.
+                * location.
                 */
-               cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
+               cur_max = xfs_bmbt_maxrecs(mp, old_size, 0);
                new_max = cur_max + rec_diff;
                new_size = xfs_bmap_broot_space_calc(mp, new_max);
                ifp->if_broot = krealloc(ifp->if_broot, new_size,
                                         GFP_KERNEL | __GFP_NOFAIL);
-               op = (char *)xfs_bmap_broot_ptr_addr(mp, ifp->if_broot, 1,
-                                                    ifp->if_broot_bytes);
-               np = (char *)xfs_bmap_broot_ptr_addr(mp, ifp->if_broot, 1,
-                                                    (int)new_size);
-               ifp->if_broot_bytes = (int)new_size;
-               ASSERT(xfs_bmap_bmdr_space(ifp->if_broot) <=
-                       xfs_inode_fork_size(ip, whichfork));
-               memmove(np, op, cur_max * (uint)sizeof(xfs_fsblock_t));
+               ifp->if_broot_bytes = new_size;
+               xfs_ifork_move_broot(ip, whichfork, ifp->if_broot, new_size,
+                               ifp->if_broot, old_size, cur_max);
                return;
        }
 
@@ -466,8 +502,8 @@ xfs_iroot_realloc(
         * if_broot buffer.  It must already exist.  If we go to zero
         * records, just get rid of the root and clear the status bit.
         */
-       ASSERT((ifp->if_broot != NULL) && (ifp->if_broot_bytes > 0));
-       cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
+       ASSERT((ifp->if_broot != NULL) && (old_size > 0));
+       cur_max = xfs_bmbt_maxrecs(mp, old_size, 0);
        new_max = cur_max + rec_diff;
        ASSERT(new_max >= 0);
        if (new_max > 0)
@@ -479,36 +515,15 @@ xfs_iroot_realloc(
                return;
        }
 
-       /* First copy over the btree block header. */
+       /* Reallocate the btree root and move the contents. */
        new_broot = kmalloc(new_size,
                        GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL);
-       memcpy(new_broot, ifp->if_broot, xfs_bmbt_block_len(ip->i_mount));
-
-       /*
-        * Only copy the keys and pointers if there are any.
-        */
-       if (new_max > 0) {
-               /*
-                * First copy the keys.
-                */
-               op = (char *)xfs_bmbt_key_addr(mp, ifp->if_broot, 1);
-               np = (char *)xfs_bmbt_key_addr(mp, new_broot, 1);
-               memcpy(np, op, new_max * (uint)sizeof(xfs_bmbt_key_t));
+       xfs_ifork_move_broot(ip, whichfork, new_broot, new_size, ifp->if_broot,
+                       old_size, new_max);
 
-               /*
-                * Then copy the pointers.
-                */
-               op = (char *)xfs_bmap_broot_ptr_addr(mp, ifp->if_broot, 1,
-                                                    ifp->if_broot_bytes);
-               np = (char *)xfs_bmap_broot_ptr_addr(mp, new_broot, 1,
-                                                    (int)new_size);
-               memcpy(np, op, new_max * (uint)sizeof(xfs_fsblock_t));
-       }
        kfree(ifp->if_broot);
        ifp->if_broot = new_broot;
        ifp->if_broot_bytes = (int)new_size;
-       ASSERT(xfs_bmap_bmdr_space(ifp->if_broot) <=
-               xfs_inode_fork_size(ip, whichfork));
        return;
 }