@@ -369,6 +369,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
@@ -395,12 +439,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.
@@ -409,13 +452,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;
@@ -424,22 +466,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_NOFS | __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;
}
@@ -448,8 +484,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)
@@ -461,35 +497,14 @@ xfs_iroot_realloc(
return;
}
- /* First copy over the btree block header. */
+ /* Reallocate the btree root and move the contents. */
new_broot = kmem_alloc(new_size, KM_NOFS);
- memcpy(new_broot, ifp->if_broot, xfs_bmbt_block_len(ip->i_mount));
+ xfs_ifork_move_broot(ip, whichfork, new_broot, new_size, ifp->if_broot,
+ old_size, new_max);
- /*
- * 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));
-
- /*
- * 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));
- }
kmem_free(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;
}