xfs: refactor the inode fork memory allocation functions

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>
This commit is contained in:
Darrick J. Wong 2024-11-20 16:20:13 -08:00
parent 4f13f0a3fc
commit 6c1c55ac3c
2 changed files with 82 additions and 39 deletions

View File

@ -178,7 +178,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;
@ -211,16 +211,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;
@ -362,6 +359,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
@ -388,7 +448,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;
@ -409,9 +468,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;
}
@ -424,13 +481,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));
@ -451,39 +507,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));
}

View 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 *,