aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2018-10-18 02:20:35 -0400
committerDave Chinner <david@fromorbit.com>2018-10-18 02:20:35 -0400
commit38b6238eb6b4f4b7fe5442670156c81b21516bee (patch)
treee1a442af0be3fdc594477db77eeedeb5b857f9fe
parent1aff5696f3e03099a4a3e9a0d965ef9b345265a6 (diff)
xfs: fix buffer state management in xrep_findroot_block
We don't handle buffer state properly in online repair's findroot routine. If a buffer already has b_ops set, we don't ever want to touch that, and we don't want to call the read verifiers on a buffer that could be dirty (CRCs are only recomputed during log checkpoints). Therefore, be more careful about what we do with a buffer -- if someone else already attached ops that are not the ones for this btree type, just ignore the buffer. We only attach our btree type's buf ops if it matches the magic/uuid and structure checks. We also modify xfs_buf_read_map to allow callers to set buffer ops on a DONE buffer with NULL ops so that repair doesn't leave behind buffers which won't have buffers attached to them. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
-rw-r--r--fs/xfs/scrub/repair.c71
-rw-r--r--fs/xfs/xfs_trans.h1
-rw-r--r--fs/xfs/xfs_trans_buf.c13
3 files changed, 71 insertions, 14 deletions
diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c
index 63786341ac2a..4fc0a5ea7673 100644
--- a/fs/xfs/scrub/repair.c
+++ b/fs/xfs/scrub/repair.c
@@ -29,6 +29,8 @@
29#include "xfs_ag_resv.h" 29#include "xfs_ag_resv.h"
30#include "xfs_trans_space.h" 30#include "xfs_trans_space.h"
31#include "xfs_quota.h" 31#include "xfs_quota.h"
32#include "xfs_attr.h"
33#include "xfs_reflink.h"
32#include "scrub/xfs_scrub.h" 34#include "scrub/xfs_scrub.h"
33#include "scrub/scrub.h" 35#include "scrub/scrub.h"
34#include "scrub/common.h" 36#include "scrub/common.h"
@@ -699,7 +701,7 @@ xrep_findroot_block(
699 struct xfs_btree_block *btblock; 701 struct xfs_btree_block *btblock;
700 xfs_daddr_t daddr; 702 xfs_daddr_t daddr;
701 int block_level; 703 int block_level;
702 int error; 704 int error = 0;
703 705
704 daddr = XFS_AGB_TO_DADDR(mp, ri->sc->sa.agno, agbno); 706 daddr = XFS_AGB_TO_DADDR(mp, ri->sc->sa.agno, agbno);
705 707
@@ -718,28 +720,69 @@ xrep_findroot_block(
718 return error; 720 return error;
719 } 721 }
720 722
723 /*
724 * Read the buffer into memory so that we can see if it's a match for
725 * our btree type. We have no clue if it is beforehand, and we want to
726 * avoid xfs_trans_read_buf's behavior of dumping the DONE state (which
727 * will cause needless disk reads in subsequent calls to this function)
728 * and logging metadata verifier failures.
729 *
730 * Therefore, pass in NULL buffer ops. If the buffer was already in
731 * memory from some other caller it will already have b_ops assigned.
732 * If it was in memory from a previous unsuccessful findroot_block
733 * call, the buffer won't have b_ops but it should be clean and ready
734 * for us to try to verify if the read call succeeds. The same applies
735 * if the buffer wasn't in memory at all.
736 *
737 * Note: If we never match a btree type with this buffer, it will be
738 * left in memory with NULL b_ops. This shouldn't be a problem unless
739 * the buffer gets written.
740 */
721 error = xfs_trans_read_buf(mp, ri->sc->tp, mp->m_ddev_targp, daddr, 741 error = xfs_trans_read_buf(mp, ri->sc->tp, mp->m_ddev_targp, daddr,
722 mp->m_bsize, 0, &bp, NULL); 742 mp->m_bsize, 0, &bp, NULL);
723 if (error) 743 if (error)
724 return error; 744 return error;
725 745
726 /* 746 /* Ensure the block magic matches the btree type we're looking for. */
727 * Does this look like a block matching our fs and higher than any
728 * other block we've found so far? If so, reattach buffer verifiers
729 * so the AIL won't complain if the buffer is also dirty.
730 */
731 btblock = XFS_BUF_TO_BLOCK(bp); 747 btblock = XFS_BUF_TO_BLOCK(bp);
732 if (be32_to_cpu(btblock->bb_magic) != fab->magic) 748 if (be32_to_cpu(btblock->bb_magic) != fab->magic)
733 goto out; 749 goto out;
734 if (xfs_sb_version_hascrc(&mp->m_sb) &&
735 !uuid_equal(&btblock->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
736 goto out;
737 bp->b_ops = fab->buf_ops;
738 750
739 /* Make sure we pass the verifiers. */ 751 /*
740 bp->b_ops->verify_read(bp); 752 * If the buffer already has ops applied and they're not the ones for
741 if (bp->b_error) 753 * this btree type, we know this block doesn't match the btree and we
742 goto out; 754 * can bail out.
755 *
756 * If the buffer ops match ours, someone else has already validated
757 * the block for us, so we can move on to checking if this is a root
758 * block candidate.
759 *
760 * If the buffer does not have ops, nobody has successfully validated
761 * the contents and the buffer cannot be dirty. If the magic, uuid,
762 * and structure match this btree type then we'll move on to checking
763 * if it's a root block candidate. If there is no match, bail out.
764 */
765 if (bp->b_ops) {
766 if (bp->b_ops != fab->buf_ops)
767 goto out;
768 } else {
769 ASSERT(!xfs_trans_buf_is_dirty(bp));
770 if (!uuid_equal(&btblock->bb_u.s.bb_uuid,
771 &mp->m_sb.sb_meta_uuid))
772 goto out;
773 fab->buf_ops->verify_read(bp);
774 if (bp->b_error) {
775 bp->b_error = 0;
776 goto out;
777 }
778
779 /*
780 * Some read verifiers will (re)set b_ops, so we must be
781 * careful not to blow away any such assignment.
782 */
783 if (!bp->b_ops)
784 bp->b_ops = fab->buf_ops;
785 }
743 786
744 /* 787 /*
745 * This block passes the magic/uuid and verifier tests for this btree 788 * This block passes the magic/uuid and verifier tests for this btree
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index c3d278e96ad1..a0c5dbda18aa 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -220,6 +220,7 @@ void xfs_trans_ijoin(struct xfs_trans *, struct xfs_inode *, uint);
220void xfs_trans_log_buf(struct xfs_trans *, struct xfs_buf *, uint, 220void xfs_trans_log_buf(struct xfs_trans *, struct xfs_buf *, uint,
221 uint); 221 uint);
222void xfs_trans_dirty_buf(struct xfs_trans *, struct xfs_buf *); 222void xfs_trans_dirty_buf(struct xfs_trans *, struct xfs_buf *);
223bool xfs_trans_buf_is_dirty(struct xfs_buf *bp);
223void xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint); 224void xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint);
224 225
225void xfs_extent_free_init_defer_op(void); 226void xfs_extent_free_init_defer_op(void);
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index fc40160c1773..629f1479c9d2 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -350,6 +350,19 @@ xfs_trans_read_buf_map(
350 350
351} 351}
352 352
353/* Has this buffer been dirtied by anyone? */
354bool
355xfs_trans_buf_is_dirty(
356 struct xfs_buf *bp)
357{
358 struct xfs_buf_log_item *bip = bp->b_log_item;
359
360 if (!bip)
361 return false;
362 ASSERT(bip->bli_item.li_type == XFS_LI_BUF);
363 return test_bit(XFS_LI_DIRTY, &bip->bli_item.li_flags);
364}
365
353/* 366/*
354 * Release a buffer previously joined to the transaction. If the buffer is 367 * Release a buffer previously joined to the transaction. If the buffer is
355 * modified within this transaction, decrement the recursion count but do not 368 * modified within this transaction, decrement the recursion count but do not