aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorBrian Foster <bfoster@redhat.com>2017-02-14 01:48:18 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-08 03:30:31 -0400
commit2d7c1c7ffafd6dffa3400cce60174fe904982101 (patch)
tree61facde30a68e6275828fa898d9802a585362025 /fs
parent47d7d1ea6c5ff252728773c20129283ba64c8b7b (diff)
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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-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 9e3b069fc84b..7457937549bb 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -2907,7 +2907,8 @@ xfs_bmap_add_extent_hole_delay(
2907 oldlen = startblockval(left.br_startblock) + 2907 oldlen = startblockval(left.br_startblock) +
2908 startblockval(new->br_startblock) + 2908 startblockval(new->br_startblock) +
2909 startblockval(right.br_startblock); 2909 startblockval(right.br_startblock);
2910 newlen = xfs_bmap_worst_indlen(ip, temp); 2910 newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
2911 oldlen);
2911 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx), 2912 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
2912 nullstartblock((int)newlen)); 2913 nullstartblock((int)newlen));
2913 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); 2914 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
@@ -2928,7 +2929,8 @@ xfs_bmap_add_extent_hole_delay(
2928 xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp); 2929 xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
2929 oldlen = startblockval(left.br_startblock) + 2930 oldlen = startblockval(left.br_startblock) +
2930 startblockval(new->br_startblock); 2931 startblockval(new->br_startblock);
2931 newlen = xfs_bmap_worst_indlen(ip, temp); 2932 newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
2933 oldlen);
2932 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx), 2934 xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
2933 nullstartblock((int)newlen)); 2935 nullstartblock((int)newlen));
2934 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); 2936 trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
@@ -2944,7 +2946,8 @@ xfs_bmap_add_extent_hole_delay(
2944 temp = new->br_blockcount + right.br_blockcount; 2946 temp = new->br_blockcount + right.br_blockcount;
2945 oldlen = startblockval(new->br_startblock) + 2947 oldlen = startblockval(new->br_startblock) +
2946 startblockval(right.br_startblock); 2948 startblockval(right.br_startblock);
2947 newlen = xfs_bmap_worst_indlen(ip, temp); 2949 newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
2950 oldlen);
2948 xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx), 2951 xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
2949 new->br_startoff, 2952 new->br_startoff,
2950 nullstartblock((int)newlen), temp, right.br_state); 2953 nullstartblock((int)newlen), temp, right.br_state);