aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_trans_buf.c
diff options
context:
space:
mode:
authorBrian Foster <bfoster@redhat.com>2018-09-28 23:45:26 -0400
committerDave Chinner <david@fromorbit.com>2018-09-28 23:45:26 -0400
commit95808459b110f16b50f03a70ecfa72bb14bd8a96 (patch)
tree10f08200523405f5b534af39552d3061a348e118 /fs/xfs/xfs_trans_buf.c
parent23420d05e67d23728e116321c4afe084ebfa6427 (diff)
xfs: refactor xfs_buf_log_item reference count handling
The xfs_buf_log_item structure has a reference counter with slightly tricky semantics. In the common case, a buffer is logged and committed in a transaction, committed to the on-disk log (added to the AIL) and then finally written back and removed from the AIL. The bli refcount covers two potentially overlapping timeframes: 1. the bli is held in an active transaction 2. the bli is pinned by the log The caveat to this approach is that the reference counter does not purely dictate the lifetime of the bli. IOW, when a dirty buffer is physically logged and unpinned, the bli refcount may go to zero as the log item is inserted into the AIL. Only once the buffer is written back can the bli finally be freed. The above semantics means that it is not enough for the various refcount decrementing contexts to release the bli on decrement to zero. xfs_trans_brelse(), transaction commit (->iop_unlock()) and unpin (->iop_unpin()) must all drop the associated reference and make additional checks to determine if the current context is responsible for freeing the item. For example, if a transaction holds but does not dirty a particular bli, the commit may drop the refcount to zero. If the bli itself is clean, it is also not AIL resident and must be freed at this time. The same is true for xfs_trans_brelse(). If the transaction dirties a bli and then aborts or an unpin results in an abort due to a log I/O error, the last reference count holder is expected to explicitly remove the item from the AIL and release it (since an abort means filesystem shutdown and metadata writeback will never occur). This leads to fairly complex checks being replicated in a few different places. Since ->iop_unlock() and xfs_trans_brelse() are nearly identical, refactor the logic into a common helper that implements and documents the semantics in one place. This patch does not change behavior. Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs/xfs_trans_buf.c')
-rw-r--r--fs/xfs/xfs_trans_buf.c23
1 files changed, 2 insertions, 21 deletions
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index 7498f87ceed3..286a287ac57a 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -339,8 +339,6 @@ xfs_trans_brelse(
339 struct xfs_buf *bp) 339 struct xfs_buf *bp)
340{ 340{
341 struct xfs_buf_log_item *bip = bp->b_log_item; 341 struct xfs_buf_log_item *bip = bp->b_log_item;
342 bool freed;
343 bool dirty;
344 342
345 ASSERT(bp->b_transp == tp); 343 ASSERT(bp->b_transp == tp);
346 344
@@ -379,25 +377,8 @@ xfs_trans_brelse(
379 xfs_trans_del_item(&bip->bli_item); 377 xfs_trans_del_item(&bip->bli_item);
380 bip->bli_flags &= ~XFS_BLI_HOLD; 378 bip->bli_flags &= ~XFS_BLI_HOLD;
381 379
382 /* 380 /* drop the reference to the bli */
383 * Drop the reference to the bli. At this point, the bli must be either 381 xfs_buf_item_put(bip);
384 * freed or dirty (or both). If freed, there are a couple cases where we
385 * are responsible to free the item. If the bli is clean, we're the last
386 * user of it. If the fs has shut down, the bli may be dirty and AIL
387 * resident, but won't ever be written back. We therefore may also need
388 * to remove it from the AIL before freeing it.
389 */
390 freed = atomic_dec_and_test(&bip->bli_refcount);
391 dirty = bip->bli_flags & XFS_BLI_DIRTY;
392 ASSERT(freed || dirty);
393 if (freed) {
394 bool abort = XFS_FORCED_SHUTDOWN(tp->t_mountp);
395 ASSERT(abort || !test_bit(XFS_LI_IN_AIL, &bip->bli_item.li_flags));
396 if (abort)
397 xfs_trans_ail_remove(&bip->bli_item, SHUTDOWN_LOG_IO_ERROR);
398 if (!dirty || abort)
399 xfs_buf_item_relse(bp);
400 }
401 382
402 bp->b_transp = NULL; 383 bp->b_transp = NULL;
403 xfs_buf_relse(bp); 384 xfs_buf_relse(bp);