| From 0daaecacb83bc6b656a56393ab77a31c28139bc7 Mon Sep 17 00:00:00 2001 |
| From: Brian Foster <bfoster@redhat.com> |
| Date: Fri, 12 May 2017 10:44:08 -0700 |
| Subject: xfs: fix indlen accounting error on partial delalloc conversion |
| |
| From: Brian Foster <bfoster@redhat.com> |
| |
| commit 0daaecacb83bc6b656a56393ab77a31c28139bc7 upstream. |
| |
| 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: 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> |
| |
| --- |
| fs/xfs/libxfs/xfs_bmap.c | 7 ++++--- |
| 1 file changed, 4 insertions(+), 3 deletions(-) |
| |
| --- a/fs/xfs/libxfs/xfs_bmap.c |
| +++ b/fs/xfs/libxfs/xfs_bmap.c |
| @@ -2208,8 +2208,10 @@ xfs_bmap_add_extent_delay_real( |
| } |
| temp = xfs_bmap_worst_indlen(bma->ip, temp); |
| temp2 = xfs_bmap_worst_indlen(bma->ip, temp2); |
| - diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) - |
| - (bma->cur ? bma->cur->bc_private.b.allocated : 0)); |
| + diff = (int)(temp + temp2 - |
| + (startblockval(PREV.br_startblock) - |
| + (bma->cur ? |
| + bma->cur->bc_private.b.allocated : 0))); |
| if (diff > 0) { |
| error = xfs_mod_fdblocks(bma->ip->i_mount, |
| -((int64_t)diff), false); |
| @@ -2266,7 +2268,6 @@ xfs_bmap_add_extent_delay_real( |
| temp = da_new; |
| if (bma->cur) |
| temp += bma->cur->bc_private.b.allocated; |
| - ASSERT(temp <= da_old); |
| if (temp < da_old) |
| xfs_mod_fdblocks(bma->ip->i_mount, |
| (int64_t)(da_old - temp), false); |