xfs: add a function to deal with corrupt buffers post-verifiers
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 11 Mar 2020 17:37:54 +0000 (10:37 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Thu, 12 Mar 2020 14:58:12 +0000 (07:58 -0700)
Add a helper function to get rid of buffers that we have decided are
corrupt after the verifiers have run.  This function is intended to
handle metadata checks that can't happen in the verifiers, such as
inter-block relationship checking.  Note that we now mark the buffer
stale so that it will not end up on any LRU and will be purged on
release.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
12 files changed:
fs/xfs/libxfs/xfs_alloc.c
fs/xfs/libxfs/xfs_attr_leaf.c
fs/xfs/libxfs/xfs_btree.c
fs/xfs/libxfs/xfs_da_btree.c
fs/xfs/libxfs/xfs_dir2_leaf.c
fs/xfs/libxfs/xfs_dir2_node.c
fs/xfs/xfs_attr_inactive.c
fs/xfs/xfs_attr_list.c
fs/xfs/xfs_buf.c
fs/xfs/xfs_buf.h
fs/xfs/xfs_error.c
fs/xfs/xfs_inode.c

index 9e99c0c..5bc58b7 100644 (file)
@@ -722,7 +722,7 @@ xfs_alloc_update_counters(
        xfs_trans_agblocks_delta(tp, len);
        if (unlikely(be32_to_cpu(agf->agf_freeblks) >
                     be32_to_cpu(agf->agf_length))) {
-               xfs_buf_corruption_error(agbp);
+               xfs_buf_mark_corrupt(agbp);
                return -EFSCORRUPTED;
        }
 
index 4be04ae..6eda182 100644 (file)
@@ -2339,7 +2339,7 @@ xfs_attr3_leaf_lookup_int(
        xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
        entries = xfs_attr3_leaf_entryp(leaf);
        if (ichdr.count >= args->geo->blksize / 8) {
-               xfs_buf_corruption_error(bp);
+               xfs_buf_mark_corrupt(bp);
                return -EFSCORRUPTED;
        }
 
@@ -2358,11 +2358,11 @@ xfs_attr3_leaf_lookup_int(
                        break;
        }
        if (!(probe >= 0 && (!ichdr.count || probe < ichdr.count))) {
-               xfs_buf_corruption_error(bp);
+               xfs_buf_mark_corrupt(bp);
                return -EFSCORRUPTED;
        }
        if (!(span <= 4 || be32_to_cpu(entry->hashval) == hashval)) {
-               xfs_buf_corruption_error(bp);
+               xfs_buf_mark_corrupt(bp);
                return -EFSCORRUPTED;
        }
 
index fd300dc..0599a3e 100644 (file)
@@ -1762,7 +1762,7 @@ xfs_btree_lookup_get_block(
 
 out_bad:
        *blkp = NULL;
-       xfs_buf_corruption_error(bp);
+       xfs_buf_mark_corrupt(bp);
        xfs_trans_brelse(cur->bc_tp, bp);
        return -EFSCORRUPTED;
 }
index e864c3d..a7880c6 100644 (file)
@@ -590,7 +590,7 @@ xfs_da3_split(
        node = oldblk->bp->b_addr;
        if (node->hdr.info.forw) {
                if (be32_to_cpu(node->hdr.info.forw) != addblk->blkno) {
-                       xfs_buf_corruption_error(oldblk->bp);
+                       xfs_buf_mark_corrupt(oldblk->bp);
                        error = -EFSCORRUPTED;
                        goto out;
                }
@@ -603,7 +603,7 @@ xfs_da3_split(
        node = oldblk->bp->b_addr;
        if (node->hdr.info.back) {
                if (be32_to_cpu(node->hdr.info.back) != addblk->blkno) {
-                       xfs_buf_corruption_error(oldblk->bp);
+                       xfs_buf_mark_corrupt(oldblk->bp);
                        error = -EFSCORRUPTED;
                        goto out;
                }
@@ -1624,7 +1624,7 @@ xfs_da3_node_lookup_int(
                }
 
                if (magic != XFS_DA_NODE_MAGIC && magic != XFS_DA3_NODE_MAGIC) {
-                       xfs_buf_corruption_error(blk->bp);
+                       xfs_buf_mark_corrupt(blk->bp);
                        return -EFSCORRUPTED;
                }
 
@@ -1639,7 +1639,7 @@ xfs_da3_node_lookup_int(
 
                /* Tree taller than we can handle; bail out! */
                if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) {
-                       xfs_buf_corruption_error(blk->bp);
+                       xfs_buf_mark_corrupt(blk->bp);
                        return -EFSCORRUPTED;
                }
 
@@ -1647,7 +1647,7 @@ xfs_da3_node_lookup_int(
                if (blkno == args->geo->leafblk)
                        expected_level = nodehdr.level - 1;
                else if (expected_level != nodehdr.level) {
-                       xfs_buf_corruption_error(blk->bp);
+                       xfs_buf_mark_corrupt(blk->bp);
                        return -EFSCORRUPTED;
                } else
                        expected_level--;
index a131b52..95d2a3f 100644 (file)
@@ -1383,7 +1383,7 @@ xfs_dir2_leaf_removename(
        ltp = xfs_dir2_leaf_tail_p(geo, leaf);
        bestsp = xfs_dir2_leaf_bests_p(ltp);
        if (be16_to_cpu(bestsp[db]) != oldbest) {
-               xfs_buf_corruption_error(lbp);
+               xfs_buf_mark_corrupt(lbp);
                return -EFSCORRUPTED;
        }
        /*
index a0cc5e2..dbd1e90 100644 (file)
@@ -439,7 +439,7 @@ xfs_dir2_leaf_to_node(
        ltp = xfs_dir2_leaf_tail_p(args->geo, leaf);
        if (be32_to_cpu(ltp->bestcount) >
                                (uint)dp->i_d.di_size / args->geo->blksize) {
-               xfs_buf_corruption_error(lbp);
+               xfs_buf_mark_corrupt(lbp);
                return -EFSCORRUPTED;
        }
 
@@ -513,7 +513,7 @@ xfs_dir2_leafn_add(
         * into other peoples memory
         */
        if (index < 0) {
-               xfs_buf_corruption_error(bp);
+               xfs_buf_mark_corrupt(bp);
                return -EFSCORRUPTED;
        }
 
@@ -800,7 +800,7 @@ xfs_dir2_leafn_lookup_for_entry(
 
        xfs_dir3_leaf_check(dp, bp);
        if (leafhdr.count <= 0) {
-               xfs_buf_corruption_error(bp);
+               xfs_buf_mark_corrupt(bp);
                return -EFSCORRUPTED;
        }
 
index fe8f60b..c42f90e 100644 (file)
@@ -145,7 +145,7 @@ xfs_attr3_node_inactive(
         * Since this code is recursive (gasp!) we must protect ourselves.
         */
        if (level > XFS_DA_NODE_MAXDEPTH) {
-               xfs_buf_corruption_error(bp);
+               xfs_buf_mark_corrupt(bp);
                xfs_trans_brelse(*trans, bp);   /* no locks for later trans */
                return -EFSCORRUPTED;
        }
@@ -194,7 +194,7 @@ xfs_attr3_node_inactive(
                        error = xfs_attr3_leaf_inactive(trans, dp, child_bp);
                        break;
                default:
-                       xfs_buf_corruption_error(child_bp);
+                       xfs_buf_mark_corrupt(child_bp);
                        xfs_trans_brelse(*trans, child_bp);
                        error = -EFSCORRUPTED;
                        break;
@@ -289,7 +289,7 @@ xfs_attr3_root_inactive(
                break;
        default:
                error = -EFSCORRUPTED;
-               xfs_buf_corruption_error(bp);
+               xfs_buf_mark_corrupt(bp);
                xfs_trans_brelse(*trans, bp);
                break;
        }
index 017f569..5ff1d92 100644 (file)
@@ -274,7 +274,7 @@ xfs_attr_node_list_lookup(
        return 0;
 
 out_corruptbuf:
-       xfs_buf_corruption_error(bp);
+       xfs_buf_mark_corrupt(bp);
        xfs_trans_brelse(tp, bp);
        return -EFSCORRUPTED;
 }
index f8e4fee..6552e59 100644 (file)
@@ -1574,6 +1574,28 @@ xfs_buf_zero(
 }
 
 /*
+ * Log a message about and stale a buffer that a caller has decided is corrupt.
+ *
+ * This function should be called for the kinds of metadata corruption that
+ * cannot be detect from a verifier, such as incorrect inter-block relationship
+ * data.  Do /not/ call this function from a verifier function.
+ *
+ * The buffer must be XBF_DONE prior to the call.  Afterwards, the buffer will
+ * be marked stale, but b_error will not be set.  The caller is responsible for
+ * releasing the buffer or fixing it.
+ */
+void
+__xfs_buf_mark_corrupt(
+       struct xfs_buf          *bp,
+       xfs_failaddr_t          fa)
+{
+       ASSERT(bp->b_flags & XBF_DONE);
+
+       xfs_buf_corruption_error(bp);
+       xfs_buf_stale(bp);
+}
+
+/*
  *     Handling of buffer targets (buftargs).
  */
 
index d79a1fe..9a04c53 100644 (file)
@@ -272,6 +272,8 @@ static inline int xfs_buf_submit(struct xfs_buf *bp)
 }
 
 void xfs_buf_zero(struct xfs_buf *bp, size_t boff, size_t bsize);
+void __xfs_buf_mark_corrupt(struct xfs_buf *bp, xfs_failaddr_t fa);
+#define xfs_buf_mark_corrupt(bp) __xfs_buf_mark_corrupt((bp), __this_address)
 
 /* Buffer Utility Routines */
 extern void *xfs_buf_offset(struct xfs_buf *, size_t);
index 331765a..57068d4 100644 (file)
@@ -345,6 +345,8 @@ xfs_corruption_error(
  * Complain about the kinds of metadata corruption that we can't detect from a
  * verifier, such as incorrect inter-block relationship data.  Does not set
  * bp->b_error.
+ *
+ * Call xfs_buf_mark_corrupt, not this function.
  */
 void
 xfs_buf_corruption_error(
index addc3ee..d65d250 100644 (file)
@@ -2133,7 +2133,7 @@ xfs_iunlink_update_bucket(
         * head of the list.
         */
        if (old_value == new_agino) {
-               xfs_buf_corruption_error(agibp);
+               xfs_buf_mark_corrupt(agibp);
                return -EFSCORRUPTED;
        }
 
@@ -2267,7 +2267,7 @@ xfs_iunlink(
        next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
        if (next_agino == agino ||
            !xfs_verify_agino_or_null(mp, agno, next_agino)) {
-               xfs_buf_corruption_error(agibp);
+               xfs_buf_mark_corrupt(agibp);
                return -EFSCORRUPTED;
        }