aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorBrian Foster <bfoster@redhat.com>2018-09-28 23:44:40 -0400
committerDave Chinner <david@fromorbit.com>2018-09-28 23:44:40 -0400
commitd9183105caa926522a4bc8a40e162de7019f1a21 (patch)
tree01f98539b08537a19b90415386e5b828db2a378d /fs/xfs
parentd5a2e2893da0d62c3888c91ae2da798adc17a9b9 (diff)
xfs: don't unlock invalidated buf on aborted tx commit
xfstests generic/388,475 occasionally reproduce assertion failures in xfs_buf_item_unpin() when the final bli reference is dropped on an invalidated buffer and the buffer is not locked as it is expected to be. Invalidated buffers should remain locked on transaction commit until the final unpin, at which point the buffer is removed from the AIL and the bli is freed since stale buffers are not written back. The assert failures are associated with filesystem shutdown, typically due to log I/O errors injected by the test. The problematic situation can occur if the shutdown happens to cause a race between an active transaction that has invalidated a particular buffer and an I/O error on a log buffer that contains the bli associated with the same (now stale) buffer. Both transaction and log contexts acquire a bli reference. If the transaction has already invalidated the buffer by the time the I/O error occurs and ends up aborting due to shutdown, the transaction and log hold the last two references to a stale bli. If the transaction cancel occurs first, it treats the buffer as non-stale due to the aborted state: the bli reference is dropped and the buffer is released/unlocked. The log buffer I/O error handling eventually calls into xfs_buf_item_unpin(), drops the final reference to the bli and treats it as stale. The buffer wasn't left locked by xfs_buf_item_unlock(), however, so the assert fails and the buffer is double unlocked. The latter problem is mitigated by the fact that the fs is shutdown and no further damage is possible. ->iop_unlock() of an invalidated buffer should behave consistently with respect to the bli refcount, regardless of aborted state. If the refcount remains elevated on commit, we know the bli is awaiting an unpin (since it can't be in another transaction) and will be handled appropriately on log buffer completion. If the final bli reference of an invalidated buffer is dropped in ->iop_unlock(), we can assume the transaction has aborted because invalidation implies a dirty transaction. In the non-abort case, the log would have acquired a bli reference in ->iop_pin() and prevented bli release at ->iop_unlock() time. In the abort case the item must be freed and buffer unlocked because it wasn't pinned by the log. Rework xfs_buf_item_unlock() to simplify the currently circuitous and duplicate logic and leave invalidated buffers locked based on bli refcount, regardless of aborted state. This ensures that a pinned, stale buffer is always found locked when eventually unpinned. 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')
-rw-r--r--fs/xfs/xfs_buf_item.c91
-rw-r--r--fs/xfs/xfs_trace.h1
2 files changed, 42 insertions, 50 deletions
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 1c9d1398980b..42fce70b474d 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -556,73 +556,66 @@ xfs_buf_item_unlock(
556{ 556{
557 struct xfs_buf_log_item *bip = BUF_ITEM(lip); 557 struct xfs_buf_log_item *bip = BUF_ITEM(lip);
558 struct xfs_buf *bp = bip->bli_buf; 558 struct xfs_buf *bp = bip->bli_buf;
559 bool freed;
559 bool aborted; 560 bool aborted;
560 bool hold = !!(bip->bli_flags & XFS_BLI_HOLD); 561 bool hold = bip->bli_flags & XFS_BLI_HOLD;
561 bool dirty = !!(bip->bli_flags & XFS_BLI_DIRTY); 562 bool dirty = bip->bli_flags & XFS_BLI_DIRTY;
563 bool stale = bip->bli_flags & XFS_BLI_STALE;
562#if defined(DEBUG) || defined(XFS_WARN) 564#if defined(DEBUG) || defined(XFS_WARN)
563 bool ordered = !!(bip->bli_flags & XFS_BLI_ORDERED); 565 bool ordered = bip->bli_flags & XFS_BLI_ORDERED;
564#endif 566#endif
565 567
566 aborted = test_bit(XFS_LI_ABORTED, &lip->li_flags);
567
568 /* Clear the buffer's association with this transaction. */
569 bp->b_transp = NULL;
570
571 /*
572 * The per-transaction state has been copied above so clear it from the
573 * bli.
574 */
575 bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_HOLD | XFS_BLI_ORDERED);
576
577 /*
578 * If the buf item is marked stale, then don't do anything. We'll
579 * unlock the buffer and free the buf item when the buffer is unpinned
580 * for the last time.
581 */
582 if (bip->bli_flags & XFS_BLI_STALE) {
583 trace_xfs_buf_item_unlock_stale(bip);
584 ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
585 if (!aborted) {
586 atomic_dec(&bip->bli_refcount);
587 return;
588 }
589 }
590
591 trace_xfs_buf_item_unlock(bip); 568 trace_xfs_buf_item_unlock(bip);
592 569
593 /* 570 /*
594 * If the buf item isn't tracking any data, free it, otherwise drop the
595 * reference we hold to it. If we are aborting the transaction, this may
596 * be the only reference to the buf item, so we free it anyway
597 * regardless of whether it is dirty or not. A dirty abort implies a
598 * shutdown, anyway.
599 *
600 * The bli dirty state should match whether the blf has logged segments 571 * The bli dirty state should match whether the blf has logged segments
601 * except for ordered buffers, where only the bli should be dirty. 572 * except for ordered buffers, where only the bli should be dirty.
602 */ 573 */
603 ASSERT((!ordered && dirty == xfs_buf_item_dirty_format(bip)) || 574 ASSERT((!ordered && dirty == xfs_buf_item_dirty_format(bip)) ||
604 (ordered && dirty && !xfs_buf_item_dirty_format(bip))); 575 (ordered && dirty && !xfs_buf_item_dirty_format(bip)));
576 ASSERT(!stale || (bip->__bli_format.blf_flags & XFS_BLF_CANCEL));
577
578 aborted = test_bit(XFS_LI_ABORTED, &lip->li_flags);
605 579
606 /* 580 /*
607 * Clean buffers, by definition, cannot be in the AIL. However, aborted 581 * Clear the buffer's association with this transaction and
608 * buffers may be in the AIL regardless of dirty state. An aborted 582 * per-transaction state from the bli, which has been copied above.
609 * transaction that invalidates a buffer already in the AIL may have 583 */
610 * marked it stale and cleared the dirty state, for example. 584 bp->b_transp = NULL;
611 * 585 bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_HOLD | XFS_BLI_ORDERED);
612 * Therefore if we are aborting a buffer and we've just taken the last 586
613 * reference away, we have to check if it is in the AIL before freeing 587 /*
614 * it. We need to free it in this case, because an aborted transaction 588 * Drop the transaction's bli reference and deal with the item if we had
615 * has already shut the filesystem down and this is the last chance we 589 * the last one. We must free the item if clean or aborted since it
616 * will have to do so. 590 * wasn't pinned by the log and this is the last chance to do so. If the
591 * bli is freed and dirty (but non-aborted), the buffer was not dirty in
592 * this transaction but modified by a previous one and still awaiting
593 * writeback. In that case, the bli is freed on buffer writeback
594 * completion.
617 */ 595 */
618 if (atomic_dec_and_test(&bip->bli_refcount)) { 596 freed = atomic_dec_and_test(&bip->bli_refcount);
619 if (aborted) { 597 if (freed) {
620 ASSERT(XFS_FORCED_SHUTDOWN(lip->li_mountp)); 598 ASSERT(!aborted || XFS_FORCED_SHUTDOWN(lip->li_mountp));
599 /*
600 * An aborted item may be in the AIL regardless of dirty state.
601 * For example, consider an aborted transaction that invalidated
602 * a dirty bli and cleared the dirty state.
603 */
604 if (aborted)
621 xfs_trans_ail_remove(lip, SHUTDOWN_LOG_IO_ERROR); 605 xfs_trans_ail_remove(lip, SHUTDOWN_LOG_IO_ERROR);
606 if (aborted || !dirty)
622 xfs_buf_item_relse(bp); 607 xfs_buf_item_relse(bp);
623 } else if (!dirty) 608 } else if (stale) {
624 xfs_buf_item_relse(bp); 609 /*
610 * Stale buffers remain locked until final unpin unless the bli
611 * was freed in the branch above. A freed stale bli implies an
612 * abort because buffer invalidation dirties the bli and
613 * transaction.
614 */
615 ASSERT(!freed);
616 return;
625 } 617 }
618 ASSERT(!stale || (aborted && freed));
626 619
627 if (!hold) 620 if (!hold)
628 xfs_buf_relse(bp); 621 xfs_buf_relse(bp);
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index ad315e83bc02..3043e5ed6495 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -473,7 +473,6 @@ DEFINE_BUF_ITEM_EVENT(xfs_buf_item_pin);
473DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin); 473DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin);
474DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin_stale); 474DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin_stale);
475DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unlock); 475DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unlock);
476DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unlock_stale);
477DEFINE_BUF_ITEM_EVENT(xfs_buf_item_committed); 476DEFINE_BUF_ITEM_EVENT(xfs_buf_item_committed);
478DEFINE_BUF_ITEM_EVENT(xfs_buf_item_push); 477DEFINE_BUF_ITEM_EVENT(xfs_buf_item_push);
479DEFINE_BUF_ITEM_EVENT(xfs_trans_get_buf); 478DEFINE_BUF_ITEM_EVENT(xfs_trans_get_buf);