Commit c47260d4 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: count EFIs when deciding to ask for a continuation of a refcount update

A long time ago, I added to XFS the ability to use deferred reference
count operations as part of a transaction chain.  This enabled us to
avoid blowing out the transaction reservation when the blocks in a
physical extent all had different reference counts because we could ask
the deferred operation manager for a continuation, which would get us a
clean transaction.

The refcount code asks for a continuation when the number of refcount
record updates reaches the point where we think that the transaction has
logged enough full btree blocks due to refcount (and free space) btree
shape changes and refcount record updates that we're in danger of
overflowing the transaction.

We did not previously count the EFIs logged to the refcount update
transaction because the clamps on the length of a bunmap operation were
sufficient to avoid overflowing the transaction reservation even in the
worst case situation where every other block of the unmapped extent is
shared.

Unfortunately, the restrictions on bunmap length avoid failure in the
worst case by imposing a maximum unmap length of ~3000 blocks, even for
non-pathological cases.  This seriously limits performance when freeing
large extents.

Therefore, track EFIs with the same counter as refcount record updates,
and use that information as input into when we should ask for a
continuation.  This enables the next patch to drop the clumsy bunmap
limitation.

Depends: 27dada07 ("xfs: change the order in which child and parent defer ops ar finished")
Depends: 74f4d6a1 ("xfs: only relog deferred intent items if free space in the log gets low")
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 1edf8056
...@@ -960,6 +960,7 @@ xfs_refcount_adjust_extents( ...@@ -960,6 +960,7 @@ xfs_refcount_adjust_extents(
* Either cover the hole (increment) or * Either cover the hole (increment) or
* delete the range (decrement). * delete the range (decrement).
*/ */
cur->bc_ag.refc.nr_ops++;
if (tmp.rc_refcount) { if (tmp.rc_refcount) {
error = xfs_refcount_insert(cur, &tmp, error = xfs_refcount_insert(cur, &tmp,
&found_tmp); &found_tmp);
...@@ -970,7 +971,6 @@ xfs_refcount_adjust_extents( ...@@ -970,7 +971,6 @@ xfs_refcount_adjust_extents(
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto out_error; goto out_error;
} }
cur->bc_ag.refc.nr_ops++;
} else { } else {
fsbno = XFS_AGB_TO_FSB(cur->bc_mp, fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
cur->bc_ag.pag->pag_agno, cur->bc_ag.pag->pag_agno,
...@@ -1001,11 +1001,11 @@ xfs_refcount_adjust_extents( ...@@ -1001,11 +1001,11 @@ xfs_refcount_adjust_extents(
ext.rc_refcount += adj; ext.rc_refcount += adj;
trace_xfs_refcount_modify_extent(cur->bc_mp, trace_xfs_refcount_modify_extent(cur->bc_mp,
cur->bc_ag.pag->pag_agno, &ext); cur->bc_ag.pag->pag_agno, &ext);
cur->bc_ag.refc.nr_ops++;
if (ext.rc_refcount > 1) { if (ext.rc_refcount > 1) {
error = xfs_refcount_update(cur, &ext); error = xfs_refcount_update(cur, &ext);
if (error) if (error)
goto out_error; goto out_error;
cur->bc_ag.refc.nr_ops++;
} else if (ext.rc_refcount == 1) { } else if (ext.rc_refcount == 1) {
error = xfs_refcount_delete(cur, &found_rec); error = xfs_refcount_delete(cur, &found_rec);
if (error) if (error)
...@@ -1014,7 +1014,6 @@ xfs_refcount_adjust_extents( ...@@ -1014,7 +1014,6 @@ xfs_refcount_adjust_extents(
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto out_error; goto out_error;
} }
cur->bc_ag.refc.nr_ops++;
goto advloop; goto advloop;
} else { } else {
fsbno = XFS_AGB_TO_FSB(cur->bc_mp, fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
......
...@@ -67,6 +67,14 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp, ...@@ -67,6 +67,14 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
* log (plus any key updates) so we'll conservatively assume 32 bytes * log (plus any key updates) so we'll conservatively assume 32 bytes
* per record. We must also leave space for btree splits on both ends * per record. We must also leave space for btree splits on both ends
* of the range and space for the CUD and a new CUI. * of the range and space for the CUD and a new CUI.
*
* Each EFI that we attach to the transaction is assumed to consume ~32 bytes.
* This is a low estimate for an EFI tracking a single extent (16 bytes for the
* EFI header, 16 for the extent, and 12 for the xlog op header), but the
* estimate is acceptable if there's more than one extent being freed.
* In the worst case of freeing every other block during a refcount decrease
* operation, we amortize the space used for one EFI log item across 16
* extents.
*/ */
#define XFS_REFCOUNT_ITEM_OVERHEAD 32 #define XFS_REFCOUNT_ITEM_OVERHEAD 32
......
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