xfs: report dir/attr block corruption errors to the health system
authorDarrick J. Wong <djwong@kernel.org>
Thu, 22 Feb 2024 20:32:18 +0000 (12:32 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Thu, 22 Feb 2024 20:32:18 +0000 (12:32 -0800)
Whenever we encounter corrupt directory or extended attribute blocks, we
should report that to the health monitoring system for later reporting.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
12 files changed:
fs/xfs/libxfs/xfs_attr_leaf.c
fs/xfs/libxfs/xfs_attr_remote.c
fs/xfs/libxfs/xfs_da_btree.c
fs/xfs/libxfs/xfs_dir2.c
fs/xfs/libxfs/xfs_dir2_block.c
fs/xfs/libxfs/xfs_dir2_data.c
fs/xfs/libxfs/xfs_dir2_leaf.c
fs/xfs/libxfs/xfs_dir2_node.c
fs/xfs/libxfs/xfs_health.h
fs/xfs/xfs_attr_inactive.c
fs/xfs/xfs_attr_list.c
fs/xfs/xfs_health.c

index 192d9938a231b9c203a62746b1eee4da55ff8900..ac904cc1a97bae1eae0aa0acb8a3dc00ea92a55a 100644 (file)
@@ -29,6 +29,7 @@
 #include "xfs_log.h"
 #include "xfs_ag.h"
 #include "xfs_errortag.h"
+#include "xfs_health.h"
 
 
 /*
@@ -2343,6 +2344,7 @@ xfs_attr3_leaf_lookup_int(
        entries = xfs_attr3_leaf_entryp(leaf);
        if (ichdr.count >= args->geo->blksize / 8) {
                xfs_buf_mark_corrupt(bp);
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
        }
 
@@ -2362,10 +2364,12 @@ xfs_attr3_leaf_lookup_int(
        }
        if (!(probe >= 0 && (!ichdr.count || probe < ichdr.count))) {
                xfs_buf_mark_corrupt(bp);
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
        }
        if (!(span <= 4 || be32_to_cpu(entry->hashval) == hashval)) {
                xfs_buf_mark_corrupt(bp);
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
        }
 
index 1c007ebf153ab163a5e8de4e698482482ac78b72..43d66fbbdf6f71855233b91512b847a283f35666 100644 (file)
@@ -22,6 +22,7 @@
 #include "xfs_attr_remote.h"
 #include "xfs_trace.h"
 #include "xfs_error.h"
+#include "xfs_health.h"
 
 #define ATTR_RMTVALUE_MAPSIZE  1       /* # of map entries at once */
 
@@ -276,17 +277,18 @@ xfs_attr3_rmt_hdr_set(
  */
 STATIC int
 xfs_attr_rmtval_copyout(
-       struct xfs_mount *mp,
-       struct xfs_buf  *bp,
-       xfs_ino_t       ino,
-       int             *offset,
-       int             *valuelen,
-       uint8_t         **dst)
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp,
+       struct xfs_inode        *dp,
+       int                     *offset,
+       int                     *valuelen,
+       uint8_t                 **dst)
 {
-       char            *src = bp->b_addr;
-       xfs_daddr_t     bno = xfs_buf_daddr(bp);
-       int             len = BBTOB(bp->b_length);
-       int             blksize = mp->m_attr_geo->blksize;
+       char                    *src = bp->b_addr;
+       xfs_ino_t               ino = dp->i_ino;
+       xfs_daddr_t             bno = xfs_buf_daddr(bp);
+       int                     len = BBTOB(bp->b_length);
+       int                     blksize = mp->m_attr_geo->blksize;
 
        ASSERT(len >= blksize);
 
@@ -302,6 +304,7 @@ xfs_attr_rmtval_copyout(
                                xfs_alert(mp,
 "remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)",
                                        bno, *offset, byte_cnt, ino);
+                               xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
                                return -EFSCORRUPTED;
                        }
                        hdr_size = sizeof(struct xfs_attr3_rmt_hdr);
@@ -418,10 +421,12 @@ xfs_attr_rmtval_get(
                        dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
                        error = xfs_buf_read(mp->m_ddev_targp, dblkno, dblkcnt,
                                        0, &bp, &xfs_attr3_rmt_buf_ops);
+                       if (xfs_metadata_is_sick(error))
+                               xfs_dirattr_mark_sick(args->dp, XFS_ATTR_FORK);
                        if (error)
                                return error;
 
-                       error = xfs_attr_rmtval_copyout(mp, bp, args->dp->i_ino,
+                       error = xfs_attr_rmtval_copyout(mp, bp, args->dp,
                                                        &offset, &valuelen,
                                                        &dst);
                        xfs_buf_relse(bp);
index 444ec1560f4348d3e95b87e360c31a7ba8361e67..718d071bb21ae326a9c639c3f0c37bf47aa013f1 100644 (file)
@@ -23,6 +23,7 @@
 #include "xfs_buf_item.h"
 #include "xfs_log.h"
 #include "xfs_errortag.h"
+#include "xfs_health.h"
 
 /*
  * xfs_da_btree.c
@@ -353,6 +354,8 @@ const struct xfs_buf_ops xfs_da3_node_buf_ops = {
 static int
 xfs_da3_node_set_type(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
+       int                     whichfork,
        struct xfs_buf          *bp)
 {
        struct xfs_da_blkinfo   *info = bp->b_addr;
@@ -374,6 +377,7 @@ xfs_da3_node_set_type(
                XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, tp->t_mountp,
                                info, sizeof(*info));
                xfs_trans_brelse(tp, bp);
+               xfs_dirattr_mark_sick(dp, whichfork);
                return -EFSCORRUPTED;
        }
 }
@@ -392,7 +396,7 @@ xfs_da3_node_read(
                        &xfs_da3_node_buf_ops);
        if (error || !*bpp || !tp)
                return error;
-       return xfs_da3_node_set_type(tp, *bpp);
+       return xfs_da3_node_set_type(tp, dp, whichfork, *bpp);
 }
 
 int
@@ -409,6 +413,8 @@ xfs_da3_node_read_mapped(
        error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, mappedbno,
                        XFS_FSB_TO_BB(mp, xfs_dabuf_nfsb(mp, whichfork)), 0,
                        bpp, &xfs_da3_node_buf_ops);
+       if (xfs_metadata_is_sick(error))
+               xfs_dirattr_mark_sick(dp, whichfork);
        if (error || !*bpp)
                return error;
 
@@ -419,7 +425,7 @@ xfs_da3_node_read_mapped(
 
        if (!tp)
                return 0;
-       return xfs_da3_node_set_type(tp, *bpp);
+       return xfs_da3_node_set_type(tp, dp, whichfork, *bpp);
 }
 
 /*
@@ -632,6 +638,7 @@ xfs_da3_split(
        if (node->hdr.info.forw) {
                if (be32_to_cpu(node->hdr.info.forw) != addblk->blkno) {
                        xfs_buf_mark_corrupt(oldblk->bp);
+                       xfs_da_mark_sick(state->args);
                        error = -EFSCORRUPTED;
                        goto out;
                }
@@ -645,6 +652,7 @@ xfs_da3_split(
        if (node->hdr.info.back) {
                if (be32_to_cpu(node->hdr.info.back) != addblk->blkno) {
                        xfs_buf_mark_corrupt(oldblk->bp);
+                       xfs_da_mark_sick(state->args);
                        error = -EFSCORRUPTED;
                        goto out;
                }
@@ -1636,6 +1644,7 @@ xfs_da3_node_lookup_int(
 
                if (magic != XFS_DA_NODE_MAGIC && magic != XFS_DA3_NODE_MAGIC) {
                        xfs_buf_mark_corrupt(blk->bp);
+                       xfs_da_mark_sick(args);
                        return -EFSCORRUPTED;
                }
 
@@ -1651,6 +1660,7 @@ xfs_da3_node_lookup_int(
                /* Tree taller than we can handle; bail out! */
                if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) {
                        xfs_buf_mark_corrupt(blk->bp);
+                       xfs_da_mark_sick(args);
                        return -EFSCORRUPTED;
                }
 
@@ -1659,6 +1669,7 @@ xfs_da3_node_lookup_int(
                        expected_level = nodehdr.level - 1;
                else if (expected_level != nodehdr.level) {
                        xfs_buf_mark_corrupt(blk->bp);
+                       xfs_da_mark_sick(args);
                        return -EFSCORRUPTED;
                } else
                        expected_level--;
@@ -1710,12 +1721,16 @@ xfs_da3_node_lookup_int(
                }
 
                /* We can't point back to the root. */
-               if (XFS_IS_CORRUPT(dp->i_mount, blkno == args->geo->leafblk))
+               if (XFS_IS_CORRUPT(dp->i_mount, blkno == args->geo->leafblk)) {
+                       xfs_da_mark_sick(args);
                        return -EFSCORRUPTED;
+               }
        }
 
-       if (XFS_IS_CORRUPT(dp->i_mount, expected_level != 0))
+       if (XFS_IS_CORRUPT(dp->i_mount, expected_level != 0)) {
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
+       }
 
        /*
         * A leaf block that ends in the hashval that we are interested in
@@ -1733,6 +1748,7 @@ xfs_da3_node_lookup_int(
                        args->blkno = blk->blkno;
                } else {
                        ASSERT(0);
+                       xfs_da_mark_sick(args);
                        return -EFSCORRUPTED;
                }
                if (((retval == -ENOENT) || (retval == -ENOATTR)) &&
@@ -2299,8 +2315,10 @@ xfs_da3_swap_lastblock(
        error = xfs_bmap_last_before(tp, dp, &lastoff, w);
        if (error)
                return error;
-       if (XFS_IS_CORRUPT(mp, lastoff == 0))
+       if (XFS_IS_CORRUPT(mp, lastoff == 0)) {
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
+       }
        /*
         * Read the last block in the btree space.
         */
@@ -2350,6 +2368,7 @@ xfs_da3_swap_lastblock(
                if (XFS_IS_CORRUPT(mp,
                                   be32_to_cpu(sib_info->forw) != last_blkno ||
                                   sib_info->magic != dead_info->magic)) {
+                       xfs_da_mark_sick(args);
                        error = -EFSCORRUPTED;
                        goto done;
                }
@@ -2370,6 +2389,7 @@ xfs_da3_swap_lastblock(
                if (XFS_IS_CORRUPT(mp,
                                   be32_to_cpu(sib_info->back) != last_blkno ||
                                   sib_info->magic != dead_info->magic)) {
+                       xfs_da_mark_sick(args);
                        error = -EFSCORRUPTED;
                        goto done;
                }
@@ -2392,6 +2412,7 @@ xfs_da3_swap_lastblock(
                xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node);
                if (XFS_IS_CORRUPT(mp,
                                   level >= 0 && level != par_hdr.level + 1)) {
+                       xfs_da_mark_sick(args);
                        error = -EFSCORRUPTED;
                        goto done;
                }
@@ -2403,6 +2424,7 @@ xfs_da3_swap_lastblock(
                     entno++)
                        continue;
                if (XFS_IS_CORRUPT(mp, entno == par_hdr.count)) {
+                       xfs_da_mark_sick(args);
                        error = -EFSCORRUPTED;
                        goto done;
                }
@@ -2428,6 +2450,7 @@ xfs_da3_swap_lastblock(
                xfs_trans_brelse(tp, par_buf);
                par_buf = NULL;
                if (XFS_IS_CORRUPT(mp, par_blkno == 0)) {
+                       xfs_da_mark_sick(args);
                        error = -EFSCORRUPTED;
                        goto done;
                }
@@ -2437,6 +2460,7 @@ xfs_da3_swap_lastblock(
                par_node = par_buf->b_addr;
                xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node);
                if (XFS_IS_CORRUPT(mp, par_hdr.level != level)) {
+                       xfs_da_mark_sick(args);
                        error = -EFSCORRUPTED;
                        goto done;
                }
@@ -2567,6 +2591,7 @@ out_free_irecs:
 invalid_mapping:
        /* Caller ok with no mapping. */
        if (XFS_IS_CORRUPT(mp, !(flags & XFS_DABUF_MAP_HOLE_OK))) {
+               xfs_dirattr_mark_sick(dp, whichfork);
                error = -EFSCORRUPTED;
                if (xfs_error_level >= XFS_ERRLEVEL_LOW) {
                        xfs_alert(mp, "%s: bno %u inode %llu",
@@ -2648,6 +2673,8 @@ xfs_da_read_buf(
 
        error = xfs_trans_read_buf_map(mp, tp, mp->m_ddev_targp, mapp, nmap, 0,
                        &bp, ops);
+       if (xfs_metadata_is_sick(error))
+               xfs_dirattr_mark_sick(dp, whichfork);
        if (error)
                goto out_free;
 
index 9018abb38d7f6140d3516e4534f3cc4c9f299868..4821519efad4b7e6cf8ba5eb50079773ec158f91 100644 (file)
@@ -18,6 +18,7 @@
 #include "xfs_errortag.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_health.h"
 
 const struct xfs_name xfs_name_dotdot = {
        .name   = (const unsigned char *)"..",
@@ -626,8 +627,10 @@ xfs_dir2_isblock(
                return 0;
 
        *isblock = true;
-       if (XFS_IS_CORRUPT(mp, args->dp->i_disk_size != args->geo->blksize))
+       if (XFS_IS_CORRUPT(mp, args->dp->i_disk_size != args->geo->blksize)) {
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
+       }
        return 0;
 }
 
index fde46081a8246c525514a905cef0ee91de16bd26..a2da007adb462f8cb3678bfe2f09aec4921a5278 100644 (file)
@@ -20,6 +20,7 @@
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_log.h"
+#include "xfs_health.h"
 
 /*
  * Local function prototypes.
@@ -152,6 +153,7 @@ xfs_dir3_block_read(
                __xfs_buf_mark_corrupt(*bpp, fa);
                xfs_trans_brelse(tp, *bpp);
                *bpp = NULL;
+               xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
                return -EFSCORRUPTED;
        }
 
index dbcf58979a5987263bfa6180e2f153f8a0f75908..7a6d965bea71bf6104b1f3e1b4bdeb7391d46a4c 100644 (file)
@@ -18,6 +18,7 @@
 #include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_log.h"
+#include "xfs_health.h"
 
 static xfs_failaddr_t xfs_dir2_data_freefind_verify(
                struct xfs_dir2_data_hdr *hdr, struct xfs_dir2_data_free *bf,
@@ -433,6 +434,7 @@ xfs_dir3_data_read(
                __xfs_buf_mark_corrupt(*bpp, fa);
                xfs_trans_brelse(tp, *bpp);
                *bpp = NULL;
+               xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
                return -EFSCORRUPTED;
        }
 
@@ -1198,6 +1200,7 @@ xfs_dir2_data_use_free(
 corrupt:
        xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
                        hdr, sizeof(*hdr), __FILE__, __LINE__, fa);
+       xfs_da_mark_sick(args);
        return -EFSCORRUPTED;
 }
 
index cb9e950a911d8afb89792477024a67989050aa9d..08dda5ce9d91c4f0c14f7e333c5978fa12216456 100644 (file)
@@ -19,6 +19,7 @@
 #include "xfs_trace.h"
 #include "xfs_trans.h"
 #include "xfs_buf_item.h"
+#include "xfs_health.h"
 
 /*
  * Local function declarations.
@@ -1393,8 +1394,10 @@ xfs_dir2_leaf_removename(
        bestsp = xfs_dir2_leaf_bests_p(ltp);
        if (be16_to_cpu(bestsp[db]) != oldbest) {
                xfs_buf_mark_corrupt(lbp);
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
        }
+
        /*
         * Mark the former data entry unused.
         */
index 7a03aeb9f4c91e6fbb498851a921cd015254e031..be0b8834028c009a9ae29d91948260252143679f 100644 (file)
@@ -20,6 +20,7 @@
 #include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_log.h"
+#include "xfs_health.h"
 
 /*
  * Function declarations.
@@ -231,6 +232,7 @@ __xfs_dir3_free_read(
                __xfs_buf_mark_corrupt(*bpp, fa);
                xfs_trans_brelse(tp, *bpp);
                *bpp = NULL;
+               xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
                return -EFSCORRUPTED;
        }
 
@@ -443,6 +445,7 @@ xfs_dir2_leaf_to_node(
        if (be32_to_cpu(ltp->bestcount) >
                                (uint)dp->i_disk_size / args->geo->blksize) {
                xfs_buf_mark_corrupt(lbp);
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
        }
 
@@ -517,6 +520,7 @@ xfs_dir2_leafn_add(
         */
        if (index < 0) {
                xfs_buf_mark_corrupt(bp);
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
        }
 
@@ -736,6 +740,7 @@ xfs_dir2_leafn_lookup_for_addname(
                                           cpu_to_be16(NULLDATAOFF))) {
                                if (curfdb != newfdb)
                                        xfs_trans_brelse(tp, curbp);
+                               xfs_da_mark_sick(args);
                                return -EFSCORRUPTED;
                        }
                        curfdb = newfdb;
@@ -804,6 +809,7 @@ xfs_dir2_leafn_lookup_for_entry(
        xfs_dir3_leaf_check(dp, bp);
        if (leafhdr.count <= 0) {
                xfs_buf_mark_corrupt(bp);
+               xfs_da_mark_sick(args);
                return -EFSCORRUPTED;
        }
 
@@ -1739,6 +1745,7 @@ xfs_dir2_node_add_datablk(
                        } else {
                                xfs_alert(mp, " ... fblk is NULL");
                        }
+                       xfs_da_mark_sick(args);
                        return -EFSCORRUPTED;
                }
 
index 8f566a78737fed838845665522583f48a00264d7..ff98c03212b8c4c94ff01e5937a6ca0517ea2e02 100644 (file)
@@ -38,6 +38,7 @@ struct xfs_perag;
 struct xfs_inode;
 struct xfs_fsop_geom;
 struct xfs_btree_cur;
+struct xfs_da_args;
 
 /* Observable health issues for metadata spanning the entire filesystem. */
 #define XFS_SICK_FS_COUNTERS   (1 << 0)  /* summary counters */
@@ -162,6 +163,8 @@ void xfs_inode_measure_sickness(struct xfs_inode *ip, unsigned int *sick,
 void xfs_health_unmount(struct xfs_mount *mp);
 void xfs_bmap_mark_sick(struct xfs_inode *ip, int whichfork);
 void xfs_btree_mark_sick(struct xfs_btree_cur *cur);
+void xfs_dirattr_mark_sick(struct xfs_inode *ip, int whichfork);
+void xfs_da_mark_sick(struct xfs_da_args *args);
 
 /* Now some helpers. */
 
index 89c7a9f4f93054d20b3504a8192fe16deee0dbda..24fb12986a568acbe0f4d4785305e706bec5217c 100644 (file)
@@ -23,6 +23,7 @@
 #include "xfs_quota.h"
 #include "xfs_dir2.h"
 #include "xfs_error.h"
+#include "xfs_health.h"
 
 /*
  * Invalidate any incore buffers associated with this remote attribute value
@@ -147,6 +148,7 @@ xfs_attr3_node_inactive(
        if (level > XFS_DA_NODE_MAXDEPTH) {
                xfs_buf_mark_corrupt(bp);
                xfs_trans_brelse(*trans, bp);   /* no locks for later trans */
+               xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
                return -EFSCORRUPTED;
        }
 
@@ -197,6 +199,7 @@ xfs_attr3_node_inactive(
                default:
                        xfs_buf_mark_corrupt(child_bp);
                        xfs_trans_brelse(*trans, child_bp);
+                       xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
                        error = -EFSCORRUPTED;
                        break;
                }
@@ -286,6 +289,7 @@ xfs_attr3_root_inactive(
                error = xfs_attr3_leaf_inactive(trans, dp, bp);
                break;
        default:
+               xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
                error = -EFSCORRUPTED;
                xfs_buf_mark_corrupt(bp);
                xfs_trans_brelse(*trans, bp);
index 56021403f44774ba4850701310e99f31e9707ea5..0ca4dc62f81424939a1687bcd126ee022d902b8e 100644 (file)
@@ -22,6 +22,7 @@
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_dir2.h"
+#include "xfs_health.h"
 
 STATIC int
 xfs_attr_shortform_compare(const void *a, const void *b)
@@ -125,6 +126,7 @@ xfs_attr_shortform_list(
                                             context->dp->i_mount, sfe,
                                             sizeof(*sfe));
                        kfree(sbuf);
+                       xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
                        return -EFSCORRUPTED;
                }
 
@@ -262,8 +264,10 @@ xfs_attr_node_list_lookup(
                        return 0;
 
                /* We can't point back to the root. */
-               if (XFS_IS_CORRUPT(mp, cursor->blkno == 0))
+               if (XFS_IS_CORRUPT(mp, cursor->blkno == 0)) {
+                       xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
                        return -EFSCORRUPTED;
+               }
        }
 
        if (expected_level != 0)
@@ -275,6 +279,7 @@ xfs_attr_node_list_lookup(
 out_corruptbuf:
        xfs_buf_mark_corrupt(bp);
        xfs_trans_brelse(tp, bp);
+       xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
        return -EFSCORRUPTED;
 }
 
@@ -304,6 +309,8 @@ xfs_attr_node_list(
        if (cursor->blkno > 0) {
                error = xfs_da3_node_read(context->tp, dp, cursor->blkno, &bp,
                                XFS_ATTR_FORK);
+               if (xfs_metadata_is_sick(error))
+                       xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
                if ((error != 0) && (error != -EFSCORRUPTED))
                        return error;
                if (bp) {
index e9338c05ea23766e25cc34b147e4c4876db2cffd..568300ae85550bfb78700f1ed4b1d3764863f015 100644 (file)
@@ -15,6 +15,8 @@
 #include "xfs_health.h"
 #include "xfs_ag.h"
 #include "xfs_btree.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
 
 /*
  * Warn about metadata corruption that we detected but haven't fixed, and
@@ -545,3 +547,40 @@ xfs_btree_mark_sick(
 
        xfs_ag_mark_sick(cur->bc_ag.pag, mask);
 }
+
+/*
+ * Record observations of dir/attr btree corruption with the health tracking
+ * system.
+ */
+void
+xfs_dirattr_mark_sick(
+       struct xfs_inode        *ip,
+       int                     whichfork)
+{
+       unsigned int            mask;
+
+       switch (whichfork) {
+       case XFS_DATA_FORK:
+               mask = XFS_SICK_INO_DIR;
+               break;
+       case XFS_ATTR_FORK:
+               mask = XFS_SICK_INO_XATTR;
+               break;
+       default:
+               ASSERT(0);
+               return;
+       }
+
+       xfs_inode_mark_sick(ip, mask);
+}
+
+/*
+ * Record observations of dir/attr btree corruption with the health tracking
+ * system.
+ */
+void
+xfs_da_mark_sick(
+       struct xfs_da_args      *args)
+{
+       xfs_dirattr_mark_sick(args->dp, args->whichfork);
+}