diff options
author | Darrick J. Wong <djwong@kernel.org> | 2021-10-22 15:31:05 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2021-12-15 17:29:29 -0800 |
commit | bb8092d02d822601113897de29384972342ed1e8 (patch) | |
tree | b496a8902d1bb28a09623580309e88d66f1193ae /fs/xfs/xfs_extfree_item.c | |
parent | be31442955a3d225b71223eebc056015fab0d698 (diff) |
xfs: allow queued AG intents to drain before scrubbingscrub-drain-intents_2021-12-15
Currently, online scrub isn't sufficiently careful about quiescing
allocation groups before checking them. While scrub does take the AG
header locks, it doesn't serialize against chains of AG update intents
that are being processed concurrently. If there's a collision,
cross-referencing between data structures (e.g. rmapbt and refcountbt)
can yield false corruption events; if repair is running, this results in
incorrect repairs.
Fix this by adding to the perag structure the count of active intents
and make scrub wait until there aren't any to continue. This is a
little stupid since transactions can queue intents without taking buffer
locks, but we'll also wait for those transactions.
XXX: should have instead a per-ag rwsem that gets taken as soon as the
AG[IF] are locked and stays held until the transaction commits or moves
on to the next AG? would we rather have a six lock so that intents can
take an ix lock, and not have to upgrade to x until we actually want to
make changes to that ag? is that how those even work??
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs/xfs_extfree_item.c')
-rw-r--r-- | fs/xfs/xfs_extfree_item.c | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 34de89077961..5345eb8efe30 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -489,6 +489,15 @@ xfs_extent_free_create_done( return &xfs_trans_get_efd(tp, EFI_ITEM(intent), count)->efd_item; } +static inline void +xfs_extent_free_drop_intents( + struct xfs_mount *mp, + const struct xfs_extent_free_item *xefi) +{ + xfs_fs_drop_intents(mp, xefi->xefi_flags & XFS_EFI_REALTIME, + xefi->xefi_startblock); +} + /* Process a free extent. */ STATIC int xfs_extent_free_finish_item( @@ -497,6 +506,7 @@ xfs_extent_free_finish_item( struct list_head *item, struct xfs_btree_cur **state) { + struct xfs_mount *mp = tp->t_mountp; struct xfs_extent_free_item *xefi; int error; @@ -512,6 +522,7 @@ xfs_extent_free_finish_item( } error = xfs_trans_free_extent(tp, EFD_ITEM(done), xefi); + xfs_extent_free_drop_intents(mp, xefi); kmem_cache_free(xfs_extfree_item_cache, xefi); return error; } @@ -527,14 +538,31 @@ xfs_extent_free_abort_intent( /* Cancel a free extent. */ STATIC void xfs_extent_free_cancel_item( + struct xfs_mount *mp, struct list_head *item) { struct xfs_extent_free_item *xefi; xefi = container_of(item, struct xfs_extent_free_item, xefi_list); + xfs_extent_free_drop_intents(mp, xefi); kmem_cache_free(xfs_extfree_item_cache, xefi); } +/* Add a deferred free extent. */ +STATIC void +xfs_extent_free_add_item( + struct xfs_mount *mp, + const struct list_head *item) +{ + const struct xfs_extent_free_item *xefi; + + xefi = container_of(item, struct xfs_extent_free_item, xefi_list); + + /* Grab an intent counter reference for this intent item. */ + xfs_fs_bump_intents(mp, xefi->xefi_flags & XFS_EFI_REALTIME, + xefi->xefi_startblock); +} + const struct xfs_defer_op_type xfs_extent_free_defer_type = { .max_items = XFS_EFI_MAX_FAST_EXTENTS, .create_intent = xfs_extent_free_create_intent, @@ -542,6 +570,7 @@ const struct xfs_defer_op_type xfs_extent_free_defer_type = { .create_done = xfs_extent_free_create_done, .finish_item = xfs_extent_free_finish_item, .cancel_item = xfs_extent_free_cancel_item, + .add_item = xfs_extent_free_add_item, }; /* @@ -596,6 +625,7 @@ xfs_agfl_free_finish_item( extp->ext_len = xefi->xefi_blockcount; efdp->efd_next_extent++; + xfs_extent_free_drop_intents(mp, xefi); kmem_cache_free(xfs_extfree_item_cache, xefi); return error; } @@ -608,6 +638,7 @@ const struct xfs_defer_op_type xfs_agfl_free_defer_type = { .create_done = xfs_extent_free_create_done, .finish_item = xfs_agfl_free_finish_item, .cancel_item = xfs_extent_free_cancel_item, + .add_item = xfs_extent_free_add_item, }; /* Is this recovered EFI ok? */ |