aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Foster <bfoster@redhat.com>2017-02-14 01:48:18 -0500
committerDarrick J. Wong <darrick.wong@oracle.com>2017-02-16 20:20:12 -0500
commit0e339ef8556d9e567aa7925f8892c263d79430d9 (patch)
tree7811ddc29c0527b3f1eda2a5b37c25ac70e05cb0
parent9dbddd7b0c649bd6aa9442c717932325ec590303 (diff)
xfs: handle indlen shortage on delalloc extent merge
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>
-rw-r--r--fs/xfs/libxfs/xfs_bmap.c9
1 files changed, 6 insertions, 3 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 1cee514de19e..73c95466c225 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -2805,7 +2805,8 @@ xfs_bmap_add_extent_hole_delay(
2805 oldlen = startblockval(left.br_startblock) + 2805 oldlen = startblockval(left.br_startblock) +
2806 startblockval(new->br_startblock) + 2806 startblockval(new->br_startblock) +
2807 startblockval(right.br_startblock); 2807 startblockval(right.br_startblock);
2808 newlen = xfs_bmap_worst_indlen(ip, temp); 2808 newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
2809 oldlen);
2809 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx), 2810 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
2810 nullstartblock((int)newlen)); 2811 nullstartblock((int)newlen));
2811 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); 2812 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
@@ -2826,7 +2827,8 @@ xfs_bmap_add_extent_hole_delay(
2826 xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp); 2827 xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
2827 oldlen = startblockval(left.br_startblock) + 2828 oldlen = startblockval(left.br_startblock) +
2828 startblockval(new->br_startblock); 2829 startblockval(new->br_startblock);
2829 newlen = xfs_bmap_worst_indlen(ip, temp); 2830 newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
2831 oldlen);
2830 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx), 2832 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
2831 nullstartblock((int)newlen)); 2833 nullstartblock((int)newlen));
2832 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); 2834 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
@@ -2842,7 +2844,8 @@ xfs_bmap_add_extent_hole_delay(
2842 temp = new->br_blockcount + right.br_blockcount; 2844 temp = new->br_blockcount + right.br_blockcount;
2843 oldlen = startblockval(new->br_startblock) + 2845 oldlen = startblockval(new->br_startblock) +
2844 startblockval(right.br_startblock); 2846 startblockval(right.br_startblock);
2845 newlen = xfs_bmap_worst_indlen(ip, temp); 2847 newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
2848 oldlen);
2846 xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx), 2849 xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
2847 new->br_startoff, 2850 new->br_startoff,
2848 nullstartblock((int)newlen), temp, right.br_state); 2851 nullstartblock((int)newlen), temp, right.br_state);