Commit 0daaecac authored by Brian Foster's avatar Brian Foster Committed by Darrick J. Wong

xfs: fix indlen accounting error on partial delalloc conversion

The delalloc -> real block conversion path uses an incorrect
calculation in the case where the middle part of a delalloc extent
is being converted. This is documented as a rare situation because
XFS generally attempts to maximize contiguity by converting as much
of a delalloc extent as possible.

If this situation does occur, the indlen reservation for the two new
delalloc extents left behind by the conversion of the middle range
is calculated and compared with the original reservation. If more
blocks are required, the delta is allocated from the global block
pool. This delta value can be characterized as the difference
between the new total requirement (temp + temp2) and the currently
available reservation minus those blocks that have already been
allocated (startblockval(PREV.br_startblock) - allocated).

The problem is that the current code does not account for previously
allocated blocks correctly. It subtracts the current allocation
count from the (new - old) delta rather than the old indlen
reservation. This means that more indlen blocks than have been
allocated end up stashed in the remaining extents and free space
accounting is broken as a result.

Fix up the calculation to subtract the allocated block count from
the original extent indlen and thus correctly allocate the
reservation delta based on the difference between the new total
requirement and the unused blocks from the original reservation.
Also remove a bogus assert that contradicts the fact that the new
indlen reservation can be larger than the original indlen
reservation.
Signed-off-by: default avatarBrian Foster <bfoster@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
parent 2ea659a9
...@@ -2065,8 +2065,10 @@ xfs_bmap_add_extent_delay_real( ...@@ -2065,8 +2065,10 @@ xfs_bmap_add_extent_delay_real(
} }
temp = xfs_bmap_worst_indlen(bma->ip, temp); temp = xfs_bmap_worst_indlen(bma->ip, temp);
temp2 = xfs_bmap_worst_indlen(bma->ip, temp2); temp2 = xfs_bmap_worst_indlen(bma->ip, temp2);
diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) - diff = (int)(temp + temp2 -
(bma->cur ? bma->cur->bc_private.b.allocated : 0)); (startblockval(PREV.br_startblock) -
(bma->cur ?
bma->cur->bc_private.b.allocated : 0)));
if (diff > 0) { if (diff > 0) {
error = xfs_mod_fdblocks(bma->ip->i_mount, error = xfs_mod_fdblocks(bma->ip->i_mount,
-((int64_t)diff), false); -((int64_t)diff), false);
...@@ -2123,7 +2125,6 @@ xfs_bmap_add_extent_delay_real( ...@@ -2123,7 +2125,6 @@ xfs_bmap_add_extent_delay_real(
temp = da_new; temp = da_new;
if (bma->cur) if (bma->cur)
temp += bma->cur->bc_private.b.allocated; temp += bma->cur->bc_private.b.allocated;
ASSERT(temp <= da_old);
if (temp < da_old) if (temp < da_old)
xfs_mod_fdblocks(bma->ip->i_mount, xfs_mod_fdblocks(bma->ip->i_mount,
(int64_t)(da_old - temp), false); (int64_t)(da_old - temp), false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment