diff options
Diffstat (limited to 'fs/xfs/xfs_trans_ail.c')
-rw-r--r-- | fs/xfs/xfs_trans_ail.c | 16 |
1 files changed, 10 insertions, 6 deletions
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index d7eebbf71362..5fc2380092c8 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c | |||
@@ -487,15 +487,19 @@ out_done: | |||
487 | ailp->xa_last_pushed_lsn = 0; | 487 | ailp->xa_last_pushed_lsn = 0; |
488 | 488 | ||
489 | /* | 489 | /* |
490 | * Check for an updated push target before clearing the | 490 | * We clear the XFS_AIL_PUSHING_BIT first before checking |
491 | * XFS_AIL_PUSHING_BIT. If the target changed, we've got more | 491 | * whether the target has changed. If the target has changed, |
492 | * work to do. Wait a bit longer before starting that work. | 492 | * this pushes the requeue race directly onto the result of the |
493 | * atomic test/set bit, so we are guaranteed that either the | ||
494 | * the pusher that changed the target or ourselves will requeue | ||
495 | * the work (but not both). | ||
493 | */ | 496 | */ |
497 | clear_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags); | ||
494 | smp_rmb(); | 498 | smp_rmb(); |
495 | if (XFS_LSN_CMP(ailp->xa_target, target) == 0) { | 499 | if (XFS_LSN_CMP(ailp->xa_target, target) == 0 || |
496 | clear_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags); | 500 | test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags)) |
497 | return; | 501 | return; |
498 | } | 502 | |
499 | tout = 50; | 503 | tout = 50; |
500 | } else if (XFS_LSN_CMP(lsn, target) >= 0) { | 504 | } else if (XFS_LSN_CMP(lsn, target) >= 0) { |
501 | /* | 505 | /* |