diff mbox

[1/2] xfs: handle indlen shortage on delalloc extent merge

Message ID 1486146865-47286-2-git-send-email-bfoster@redhat.com (mailing list archive)
State Superseded
Headers show

Commit Message

Brian Foster Feb. 3, 2017, 6:34 p.m. UTC
When a delalloc extent is created, it can be merged with pre-existing,
contiguous, delalloc extents. When this occurs,
xfs_bmap_add_extent_hole_delay() merges the extents along with the
associated indirect block reservations. The expectation here is that the
combined worst case indlen reservation is always less than or equal to
the indlen reservation for the individual extents.

This is not always the case, however, as existing extents can less than
the expected indlen reservation if the extent was previously split due
to a hole punch. If a new extent merges with such an extent, the total
indlen requirement may be larger than the sum of the indlen reservations
held by both extents.

xfs_bmap_add_extent_hole_delay() assumes that the worst case indlen
reservation is always available and assigns it to the merged extent
without consideration for the indlen held by the pre-existing extent. As
a result, the subsequent xfs_mod_fdblocks() call can attempt an
unintentional allocation rather than a free (indicated by an ASSERT()
failure). Further, if the allocation happens to fail in this context,
the failure goes unhandled and creates a filesystem wide block
accounting inconsistency.

Fix xfs_bmap_add_extent_hole_delay() to function as designed. Cap the
indlen reservation assigned to the merged extent to the sum of the
indlen reservations held by each of the individual extents.

Signed-off-by: Brian Foster <bfoster@redhat.com>
---
 fs/xfs/libxfs/xfs_bmap.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

Comments

Darrick J. Wong Feb. 7, 2017, 1:14 a.m. UTC | #1
On Fri, Feb 03, 2017 at 01:34:24PM -0500, Brian Foster wrote:
> When a delalloc extent is created, it can be merged with pre-existing,
> contiguous, delalloc extents. When this occurs,
> xfs_bmap_add_extent_hole_delay() merges the extents along with the
> associated indirect block reservations. The expectation here is that the
> combined worst case indlen reservation is always less than or equal to
> the indlen reservation for the individual extents.
> 
> This is not always the case, however, as existing extents can less than
> the expected indlen reservation if the extent was previously split due
> to a hole punch. If a new extent merges with such an extent, the total
> indlen requirement may be larger than the sum of the indlen reservations
> held by both extents.
> 
> xfs_bmap_add_extent_hole_delay() assumes that the worst case indlen
> reservation is always available and assigns it to the merged extent
> without consideration for the indlen held by the pre-existing extent. As
> a result, the subsequent xfs_mod_fdblocks() call can attempt an
> unintentional allocation rather than a free (indicated by an ASSERT()
> failure). Further, if the allocation happens to fail in this context,
> the failure goes unhandled and creates a filesystem wide block
> accounting inconsistency.
> 
> Fix xfs_bmap_add_extent_hole_delay() to function as designed. Cap the
> indlen reservation assigned to the merged extent to the sum of the
> indlen reservations held by each of the individual extents.
> 
> Signed-off-by: Brian Foster <bfoster@redhat.com>

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index bfc00de..d2e48ed 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -2809,7 +2809,8 @@ xfs_bmap_add_extent_hole_delay(
>  		oldlen = startblockval(left.br_startblock) +
>  			startblockval(new->br_startblock) +
>  			startblockval(right.br_startblock);
> -		newlen = xfs_bmap_worst_indlen(ip, temp);
> +		newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
> +					 oldlen);
>  		xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
>  			nullstartblock((int)newlen));
>  		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
> @@ -2830,7 +2831,8 @@ xfs_bmap_add_extent_hole_delay(
>  		xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
>  		oldlen = startblockval(left.br_startblock) +
>  			startblockval(new->br_startblock);
> -		newlen = xfs_bmap_worst_indlen(ip, temp);
> +		newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
> +					 oldlen);
>  		xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
>  			nullstartblock((int)newlen));
>  		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
> @@ -2846,7 +2848,8 @@ xfs_bmap_add_extent_hole_delay(
>  		temp = new->br_blockcount + right.br_blockcount;
>  		oldlen = startblockval(new->br_startblock) +
>  			startblockval(right.br_startblock);
> -		newlen = xfs_bmap_worst_indlen(ip, temp);
> +		newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
> +					 oldlen);
>  		xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
>  			new->br_startoff,
>  			nullstartblock((int)newlen), temp, right.br_state);
> -- 
> 2.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index bfc00de..d2e48ed 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -2809,7 +2809,8 @@  xfs_bmap_add_extent_hole_delay(
 		oldlen = startblockval(left.br_startblock) +
 			startblockval(new->br_startblock) +
 			startblockval(right.br_startblock);
-		newlen = xfs_bmap_worst_indlen(ip, temp);
+		newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+					 oldlen);
 		xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
 			nullstartblock((int)newlen));
 		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
@@ -2830,7 +2831,8 @@  xfs_bmap_add_extent_hole_delay(
 		xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
 		oldlen = startblockval(left.br_startblock) +
 			startblockval(new->br_startblock);
-		newlen = xfs_bmap_worst_indlen(ip, temp);
+		newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+					 oldlen);
 		xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
 			nullstartblock((int)newlen));
 		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
@@ -2846,7 +2848,8 @@  xfs_bmap_add_extent_hole_delay(
 		temp = new->br_blockcount + right.br_blockcount;
 		oldlen = startblockval(new->br_startblock) +
 			startblockval(right.br_startblock);
-		newlen = xfs_bmap_worst_indlen(ip, temp);
+		newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+					 oldlen);
 		xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
 			new->br_startoff,
 			nullstartblock((int)newlen), temp, right.br_state);