| From 1e39764d09f652d1339737d054b13bff74d0ad98 Mon Sep 17 00:00:00 2001 |
| From: Brian Foster <bfoster@redhat.com> |
| Date: Mon, 13 Feb 2017 22:48:18 -0800 |
| Subject: [PATCH] xfs: handle indlen shortage on delalloc extent merge |
| |
| commit 0e339ef8556d9e567aa7925f8892c263d79430d9 upstream. |
| |
| 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> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c |
| index bafb8bfc5ee8..ebee5b9c8a0e 100644 |
| --- a/fs/xfs/libxfs/xfs_bmap.c |
| +++ b/fs/xfs/libxfs/xfs_bmap.c |
| @@ -2833,7 +2833,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_); |
| @@ -2854,7 +2855,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_); |
| @@ -2870,7 +2872,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.12.0 |
| |