]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: refactor the inode fork memory allocation functions
authorDarrick J. Wong <djwong@kernel.org>
Mon, 24 Feb 2025 18:21:44 +0000 (10:21 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 25 Feb 2025 17:15:57 +0000 (09:15 -0800)
Source kernel commit: 6c1c55ac3c0512262817a088e805d99aad4c0867

Hoist the code that allocates, frees, and reallocates if_broot into a
single xfs_iroot_krealloc function.  Eventually we're going to push
xfs_iroot_realloc into the btree ops structure to handle multiple
inode-rooted btrees, but first let's separate out the bits that should
stay in xfs_inode_fork.c.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
libxfs/xfs_inode_fork.c
libxfs/xfs_inode_fork.h

index 8d7d943311e9a0a1b79d069a467f488b7e263eb4..8c779c46d8217bf77d46f70d00cd85a73a65ed5b 100644 (file)
@@ -176,7 +176,7 @@ xfs_iformat_btree(
        struct xfs_mount        *mp = ip->i_mount;
        xfs_bmdr_block_t        *dfp;
        struct xfs_ifork        *ifp;
-       /* REFERENCED */
+       struct xfs_btree_block  *broot;
        int                     nrecs;
        int                     size;
        int                     level;
@@ -209,16 +209,13 @@ xfs_iformat_btree(
                return -EFSCORRUPTED;
        }
 
-       ifp->if_broot_bytes = size;
-       ifp->if_broot = kmalloc(size,
-                               GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL);
-       ASSERT(ifp->if_broot != NULL);
+       broot = xfs_broot_alloc(ifp, size);
        /*
         * Copy and convert from the on-disk structure
         * to the in-memory structure.
         */
        xfs_bmdr_to_bmbt(ip, dfp, XFS_DFORK_SIZE(dip, ip->i_mount, whichfork),
-                        ifp->if_broot, size);
+                        broot, size);
 
        ifp->if_bytes = 0;
        ifp->if_data = NULL;
@@ -360,6 +357,69 @@ xfs_iformat_attr_fork(
        return error;
 }
 
+/*
+ * Allocate the if_broot component of an inode fork so that it is @new_size
+ * bytes in size, using __GFP_NOLOCKDEP like all the other code that
+ * initializes a broot during inode load.  Returns if_broot.
+ */
+struct xfs_btree_block *
+xfs_broot_alloc(
+       struct xfs_ifork        *ifp,
+       size_t                  new_size)
+{
+       ASSERT(ifp->if_broot == NULL);
+
+       ifp->if_broot = kmalloc(new_size,
+                               GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL);
+       ifp->if_broot_bytes = new_size;
+       return ifp->if_broot;
+}
+
+/*
+ * Reallocate the if_broot component of an inode fork so that it is @new_size
+ * bytes in size.  Returns if_broot.
+ */
+struct xfs_btree_block *
+xfs_broot_realloc(
+       struct xfs_ifork        *ifp,
+       size_t                  new_size)
+{
+       /* No size change?  No action needed. */
+       if (new_size == ifp->if_broot_bytes)
+               return ifp->if_broot;
+
+       /* New size is zero, free it. */
+       if (new_size == 0) {
+               ifp->if_broot_bytes = 0;
+               kfree(ifp->if_broot);
+               ifp->if_broot = NULL;
+               return NULL;
+       }
+
+       /*
+        * Shrinking the iroot means we allocate a new smaller object and copy
+        * it.  We don't trust krealloc not to nop on realloc-down.
+        */
+       if (ifp->if_broot_bytes > 0 && ifp->if_broot_bytes > new_size) {
+               struct xfs_btree_block  *old_broot = ifp->if_broot;
+
+               ifp->if_broot = kmalloc(new_size, GFP_KERNEL | __GFP_NOFAIL);
+               ifp->if_broot_bytes = new_size;
+               memcpy(ifp->if_broot, old_broot, new_size);
+               kfree(old_broot);
+               return ifp->if_broot;
+       }
+
+       /*
+        * Growing the iroot means we can krealloc.  This may get us the same
+        * object.
+        */
+       ifp->if_broot = krealloc(ifp->if_broot, new_size,
+                       GFP_KERNEL | __GFP_NOFAIL);
+       ifp->if_broot_bytes = new_size;
+       return ifp->if_broot;
+}
+
 /*
  * Reallocate the space for if_broot based on the number of records
  * being added or deleted as indicated in rec_diff.  Move the records
@@ -386,7 +446,6 @@ xfs_iroot_realloc(
 {
        struct xfs_mount        *mp = ip->i_mount;
        struct xfs_ifork        *ifp = xfs_ifork_ptr(ip, whichfork);
-       struct xfs_btree_block  *new_broot;
        char                    *np;
        char                    *op;
        size_t                  new_size;
@@ -407,9 +466,7 @@ xfs_iroot_realloc(
                 */
                if (old_size == 0) {
                        new_size = xfs_bmap_broot_space_calc(mp, rec_diff);
-                       ifp->if_broot = kmalloc(new_size,
-                                               GFP_KERNEL | __GFP_NOFAIL);
-                       ifp->if_broot_bytes = (int)new_size;
+                       xfs_broot_realloc(ifp, new_size);
                        return;
                }
 
@@ -422,13 +479,12 @@ xfs_iroot_realloc(
                cur_max = xfs_bmbt_maxrecs(mp, old_size, false);
                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);
+
+               xfs_broot_realloc(ifp, new_size);
                op = (char *)xfs_bmap_broot_ptr_addr(mp, ifp->if_broot, 1,
                                                     old_size);
                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));
@@ -449,39 +505,21 @@ xfs_iroot_realloc(
        else
                new_size = 0;
        if (new_size == 0) {
-               ifp->if_broot = NULL;
-               ifp->if_broot_bytes = 0;
+               xfs_broot_realloc(ifp, 0);
                return;
        }
 
        /*
-        * Shrink the btree root by allocating a smaller object and copying the
-        * fields from the old object to the new object.  krealloc does nothing
-        * if we realloc downwards.
-        */
-       new_broot = kmalloc(new_size, GFP_KERNEL | __GFP_NOFAIL);
-       /*
-        * First copy over the btree block header.
-        */
-       memcpy(new_broot, ifp->if_broot, xfs_bmbt_block_len(ip->i_mount));
-
-       /*
-        * 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));
-
-       /*
-        * Then copy the pointers.
+        * Shrink the btree root by moving the bmbt pointers, since they are
+        * not butted up against the btree block header, then reallocating
+        * broot.
         */
        op = (char *)xfs_bmap_broot_ptr_addr(mp, ifp->if_broot, 1, old_size);
-       np = (char *)xfs_bmap_broot_ptr_addr(mp, new_broot, 1, (int)new_size);
-       memcpy(np, op, new_max * (uint)sizeof(xfs_fsblock_t));
+       np = (char *)xfs_bmap_broot_ptr_addr(mp, ifp->if_broot, 1,
+                                            (int)new_size);
+       memmove(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;
+       xfs_broot_realloc(ifp, new_size);
        ASSERT(xfs_bmap_bmdr_space(ifp->if_broot) <=
               xfs_inode_fork_size(ip, whichfork));
 }
index 2373d12fd474f0cc4f8227a038a554649f580ee7..e3c5c9121044fdc7b623382859555b0af8835e62 100644 (file)
@@ -170,6 +170,11 @@ void               xfs_iflush_fork(struct xfs_inode *, struct xfs_dinode *,
 void           xfs_idestroy_fork(struct xfs_ifork *ifp);
 void *         xfs_idata_realloc(struct xfs_inode *ip, int64_t byte_diff,
                                int whichfork);
+struct xfs_btree_block *xfs_broot_alloc(struct xfs_ifork *ifp,
+                               size_t new_size);
+struct xfs_btree_block *xfs_broot_realloc(struct xfs_ifork *ifp,
+                               size_t new_size);
+
 void           xfs_iroot_realloc(struct xfs_inode *, int, int);
 int            xfs_iread_extents(struct xfs_trans *, struct xfs_inode *, int);
 int            xfs_iextents_copy(struct xfs_inode *, struct xfs_bmbt_rec *,