]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: try other AGs to allocate a BMBT block
authorDarrick J. Wong <darrick.wong@oracle.com>
Tue, 25 Oct 2016 01:43:48 +0000 (12:43 +1100)
committerDave Chinner <david@fromorbit.com>
Tue, 25 Oct 2016 01:43:48 +0000 (12:43 +1100)
Source kernel commit: 90e2056d76adc7894a019f5289d259de58065e13

Prior to the introduction of reflink, allocating a block and mapping
it into a file was performed in a single transaction with a single
block reservation, and the allocator was supposed to find enough
blocks to allocate the extent and any BMBT blocks that might be
necessary (unless we're low on space).

However, due to the way copy on write works, allocation and mapping
have been split into two transactions, which means that we must be
able to handle the case where we allocate an extent for CoW but that
AG runs out of free space before the blocks can be mapped into a file,
and the mapping requires a new BMBT block.  When this happens, look in
one of the other AGs for a BMBT block instead of taking the FS down.

The same applies to the functions that convert a data fork to extents
and later btree format.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
libxfs/xfs_bmap.c
libxfs/xfs_bmap_btree.c

index 74a205252044e304811c53ef2b17f12260165994..4f0146241058b33530e520578a424ba93103bda7 100644 (file)
@@ -745,6 +745,7 @@ xfs_bmap_extents_to_btree(
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
        } else if (dfops->dop_low) {
+try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = *firstblock;
        } else {
@@ -759,6 +760,21 @@ xfs_bmap_extents_to_btree(
                xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
                return error;
        }
+
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               dfops->dop_low = true;
+               goto try_another_ag;
+       }
        /*
         * Allocation can't fail, the space was reserved.
         */
@@ -894,6 +910,7 @@ xfs_bmap_local_to_extents(
         * file currently fits in an inode.
         */
        if (*firstblock == NULLFSBLOCK) {
+try_another_ag:
                args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino);
                args.type = XFS_ALLOCTYPE_START_BNO;
        } else {
@@ -906,6 +923,19 @@ xfs_bmap_local_to_extents(
        if (error)
                goto done;
 
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&ip->i_mount->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               goto try_another_ag;
+       }
        /* Can't fail, the space was reserved. */
        ASSERT(args.fsbno != NULLFSBLOCK);
        ASSERT(args.len == 1);
index 3d1a02e3143741666b2c6882e2a82b287052c143..601385d4eba0eb86f66c747c46313c940fc0c1ac 100644 (file)
@@ -450,6 +450,7 @@ xfs_bmbt_alloc_block(
 
        if (args.fsbno == NULLFSBLOCK) {
                args.fsbno = be64_to_cpu(start->l);
+try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                /*
                 * Make sure there is sufficient room left in the AG to
@@ -479,6 +480,22 @@ xfs_bmbt_alloc_block(
        if (error)
                goto error0;
 
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               cur->bc_private.b.dfops->dop_low = true;
+               args.fsbno = cur->bc_private.b.firstblock;
+               goto try_another_ag;
+       }
+
        if (args.fsbno == NULLFSBLOCK && args.minleft) {
                /*
                 * Could not find an AG with enough free space to satisfy