xfs: refactor the inode fork memory allocation functions
authorDarrick J. Wong <djwong@kernel.org>
Thu, 15 Aug 2024 18:48:30 +0000 (11:48 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 9 Oct 2024 23:29:16 +0000 (16:29 -0700)
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>
libxfs/xfs_inode_fork.c
libxfs/xfs_inode_fork.h

index 8d7d943311e9a0a1b79d069a467f488b7e263eb4..d72b7f66acb93ff966cc95531dfb604efdf86753 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,14 @@ 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_realloc(ifp, size, __GFP_NOLOCKDEP);
+       ASSERT(broot != NULL);
        /*
         * 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 +358,54 @@ xfs_iformat_attr_fork(
        return error;
 }
 
+/*
+ * (Re)allocate the if_broot component of an inode fork so that it is @new_size
+ * bytes in size.  If this requires a memory allocation, the @add_gfp flags
+ * will be passed to the allocator.  Returns if_broot.
+ */
+struct xfs_btree_block *
+__xfs_broot_realloc(
+       struct xfs_ifork        *ifp,
+       size_t                  new_size,
+       gfp_t                   add_gfp)
+{
+       const gfp_t             gfp = GFP_KERNEL | __GFP_NOFAIL | add_gfp;
+
+       /* 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);
+               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);
+       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 +432,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 +452,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 +465,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 +491,21 @@ xfs_iroot_realloc(
        else
                new_size = 0;
        if (new_size == 0) {
-               ifp->if_broot = NULL;
-               ifp->if_broot_bytes = 0;
+               xfs_broot_free(ifp);
                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..fc5efdeb0d7b6a02ee1406fdf21adcc9fd3124dc 100644 (file)
@@ -170,6 +170,21 @@ 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_realloc(struct xfs_ifork *ifp,
+                               size_t new_size, gfp_t additional_gfp);
+
+static inline struct xfs_btree_block *
+xfs_broot_realloc(struct xfs_ifork *ifp, size_t new_size)
+{
+       return __xfs_broot_realloc(ifp, new_size, 0);
+}
+
+static inline void
+xfs_broot_free(struct xfs_ifork *ifp)
+{
+       __xfs_broot_realloc(ifp, 0, 0);
+}
+
 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 *,