Merge tag 'xfs-6.9-merge-8' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[sfrench/cifs-2.6.git] / fs / xfs / xfs_health.c
index 9a57afee933834125f2820712fce11783a6eda45..b39f959146bc1dca7bb2c70bf6404cd04af08409 100644 (file)
 #include "xfs_trace.h"
 #include "xfs_health.h"
 #include "xfs_ag.h"
+#include "xfs_btree.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_quota_defs.h"
 
 /*
  * Warn about metadata corruption that we detected but haven't fixed, and
@@ -93,9 +97,23 @@ xfs_fs_mark_sick(
        struct xfs_mount        *mp,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~XFS_SICK_FS_PRIMARY));
+       ASSERT(!(mask & ~XFS_SICK_FS_ALL));
        trace_xfs_fs_mark_sick(mp, mask);
 
+       spin_lock(&mp->m_sb_lock);
+       mp->m_fs_sick |= mask;
+       spin_unlock(&mp->m_sb_lock);
+}
+
+/* Mark per-fs metadata as having been checked and found unhealthy by fsck. */
+void
+xfs_fs_mark_corrupt(
+       struct xfs_mount        *mp,
+       unsigned int            mask)
+{
+       ASSERT(!(mask & ~XFS_SICK_FS_ALL));
+       trace_xfs_fs_mark_corrupt(mp, mask);
+
        spin_lock(&mp->m_sb_lock);
        mp->m_fs_sick |= mask;
        mp->m_fs_checked |= mask;
@@ -108,11 +126,13 @@ xfs_fs_mark_healthy(
        struct xfs_mount        *mp,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~XFS_SICK_FS_PRIMARY));
+       ASSERT(!(mask & ~XFS_SICK_FS_ALL));
        trace_xfs_fs_mark_healthy(mp, mask);
 
        spin_lock(&mp->m_sb_lock);
        mp->m_fs_sick &= ~mask;
+       if (!(mp->m_fs_sick & XFS_SICK_FS_PRIMARY))
+               mp->m_fs_sick &= ~XFS_SICK_FS_SECONDARY;
        mp->m_fs_checked |= mask;
        spin_unlock(&mp->m_sb_lock);
 }
@@ -136,9 +156,23 @@ xfs_rt_mark_sick(
        struct xfs_mount        *mp,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~XFS_SICK_RT_PRIMARY));
+       ASSERT(!(mask & ~XFS_SICK_RT_ALL));
        trace_xfs_rt_mark_sick(mp, mask);
 
+       spin_lock(&mp->m_sb_lock);
+       mp->m_rt_sick |= mask;
+       spin_unlock(&mp->m_sb_lock);
+}
+
+/* Mark realtime metadata as having been checked and found unhealthy by fsck. */
+void
+xfs_rt_mark_corrupt(
+       struct xfs_mount        *mp,
+       unsigned int            mask)
+{
+       ASSERT(!(mask & ~XFS_SICK_RT_ALL));
+       trace_xfs_rt_mark_corrupt(mp, mask);
+
        spin_lock(&mp->m_sb_lock);
        mp->m_rt_sick |= mask;
        mp->m_rt_checked |= mask;
@@ -151,11 +185,13 @@ xfs_rt_mark_healthy(
        struct xfs_mount        *mp,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~XFS_SICK_RT_PRIMARY));
+       ASSERT(!(mask & ~XFS_SICK_RT_ALL));
        trace_xfs_rt_mark_healthy(mp, mask);
 
        spin_lock(&mp->m_sb_lock);
        mp->m_rt_sick &= ~mask;
+       if (!(mp->m_rt_sick & XFS_SICK_RT_PRIMARY))
+               mp->m_rt_sick &= ~XFS_SICK_RT_SECONDARY;
        mp->m_rt_checked |= mask;
        spin_unlock(&mp->m_sb_lock);
 }
@@ -173,15 +209,46 @@ xfs_rt_measure_sickness(
        spin_unlock(&mp->m_sb_lock);
 }
 
+/* Mark unhealthy per-ag metadata given a raw AG number. */
+void
+xfs_agno_mark_sick(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       unsigned int            mask)
+{
+       struct xfs_perag        *pag = xfs_perag_get(mp, agno);
+
+       /* per-ag structure not set up yet? */
+       if (!pag)
+               return;
+
+       xfs_ag_mark_sick(pag, mask);
+       xfs_perag_put(pag);
+}
+
 /* Mark unhealthy per-ag metadata. */
 void
 xfs_ag_mark_sick(
        struct xfs_perag        *pag,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~XFS_SICK_AG_PRIMARY));
+       ASSERT(!(mask & ~XFS_SICK_AG_ALL));
        trace_xfs_ag_mark_sick(pag->pag_mount, pag->pag_agno, mask);
 
+       spin_lock(&pag->pag_state_lock);
+       pag->pag_sick |= mask;
+       spin_unlock(&pag->pag_state_lock);
+}
+
+/* Mark per-ag metadata as having been checked and found unhealthy by fsck. */
+void
+xfs_ag_mark_corrupt(
+       struct xfs_perag        *pag,
+       unsigned int            mask)
+{
+       ASSERT(!(mask & ~XFS_SICK_AG_ALL));
+       trace_xfs_ag_mark_corrupt(pag->pag_mount, pag->pag_agno, mask);
+
        spin_lock(&pag->pag_state_lock);
        pag->pag_sick |= mask;
        pag->pag_checked |= mask;
@@ -194,11 +261,13 @@ xfs_ag_mark_healthy(
        struct xfs_perag        *pag,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~XFS_SICK_AG_PRIMARY));
+       ASSERT(!(mask & ~XFS_SICK_AG_ALL));
        trace_xfs_ag_mark_healthy(pag->pag_mount, pag->pag_agno, mask);
 
        spin_lock(&pag->pag_state_lock);
        pag->pag_sick &= ~mask;
+       if (!(pag->pag_sick & XFS_SICK_AG_PRIMARY))
+               pag->pag_sick &= ~XFS_SICK_AG_SECONDARY;
        pag->pag_checked |= mask;
        spin_unlock(&pag->pag_state_lock);
 }
@@ -222,9 +291,32 @@ xfs_inode_mark_sick(
        struct xfs_inode        *ip,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~(XFS_SICK_INO_PRIMARY | XFS_SICK_INO_ZAPPED)));
+       ASSERT(!(mask & ~XFS_SICK_INO_ALL));
        trace_xfs_inode_mark_sick(ip, mask);
 
+       spin_lock(&ip->i_flags_lock);
+       ip->i_sick |= mask;
+       spin_unlock(&ip->i_flags_lock);
+
+       /*
+        * Keep this inode around so we don't lose the sickness report.  Scrub
+        * grabs inodes with DONTCACHE assuming that most inode are ok, which
+        * is not the case here.
+        */
+       spin_lock(&VFS_I(ip)->i_lock);
+       VFS_I(ip)->i_state &= ~I_DONTCACHE;
+       spin_unlock(&VFS_I(ip)->i_lock);
+}
+
+/* Mark inode metadata as having been checked and found unhealthy by fsck. */
+void
+xfs_inode_mark_corrupt(
+       struct xfs_inode        *ip,
+       unsigned int            mask)
+{
+       ASSERT(!(mask & ~XFS_SICK_INO_ALL));
+       trace_xfs_inode_mark_corrupt(ip, mask);
+
        spin_lock(&ip->i_flags_lock);
        ip->i_sick |= mask;
        ip->i_checked |= mask;
@@ -246,11 +338,13 @@ xfs_inode_mark_healthy(
        struct xfs_inode        *ip,
        unsigned int            mask)
 {
-       ASSERT(!(mask & ~(XFS_SICK_INO_PRIMARY | XFS_SICK_INO_ZAPPED)));
+       ASSERT(!(mask & ~XFS_SICK_INO_ALL));
        trace_xfs_inode_mark_healthy(ip, mask);
 
        spin_lock(&ip->i_flags_lock);
        ip->i_sick &= ~mask;
+       if (!(ip->i_sick & XFS_SICK_INO_PRIMARY))
+               ip->i_sick &= ~XFS_SICK_INO_SECONDARY;
        ip->i_checked |= mask;
        spin_unlock(&ip->i_flags_lock);
 }
@@ -280,6 +374,8 @@ static const struct ioctl_sick_map fs_map[] = {
        { XFS_SICK_FS_UQUOTA,   XFS_FSOP_GEOM_SICK_UQUOTA },
        { XFS_SICK_FS_GQUOTA,   XFS_FSOP_GEOM_SICK_GQUOTA },
        { XFS_SICK_FS_PQUOTA,   XFS_FSOP_GEOM_SICK_PQUOTA },
+       { XFS_SICK_FS_QUOTACHECK, XFS_FSOP_GEOM_SICK_QUOTACHECK },
+       { XFS_SICK_FS_NLINKS,   XFS_FSOP_GEOM_SICK_NLINKS },
        { 0, 0 },
 };
 
@@ -335,6 +431,7 @@ static const struct ioctl_sick_map ag_map[] = {
        { XFS_SICK_AG_FINOBT,   XFS_AG_GEOM_SICK_FINOBT },
        { XFS_SICK_AG_RMAPBT,   XFS_AG_GEOM_SICK_RMAPBT },
        { XFS_SICK_AG_REFCNTBT, XFS_AG_GEOM_SICK_REFCNTBT },
+       { XFS_SICK_AG_INODES,   XFS_AG_GEOM_SICK_INODES },
        { 0, 0 },
 };
 
@@ -397,3 +494,92 @@ xfs_bulkstat_health(
                        bs->bs_sick |= m->ioctl_mask;
        }
 }
+
+/* Mark a block mapping sick. */
+void
+xfs_bmap_mark_sick(
+       struct xfs_inode        *ip,
+       int                     whichfork)
+{
+       unsigned int            mask;
+
+       switch (whichfork) {
+       case XFS_DATA_FORK:
+               mask = XFS_SICK_INO_BMBTD;
+               break;
+       case XFS_ATTR_FORK:
+               mask = XFS_SICK_INO_BMBTA;
+               break;
+       case XFS_COW_FORK:
+               mask = XFS_SICK_INO_BMBTC;
+               break;
+       default:
+               ASSERT(0);
+               return;
+       }
+
+       xfs_inode_mark_sick(ip, mask);
+}
+
+/* Record observations of btree corruption with the health tracking system. */
+void
+xfs_btree_mark_sick(
+       struct xfs_btree_cur            *cur)
+{
+       switch (cur->bc_ops->type) {
+       case XFS_BTREE_TYPE_MEM:
+               /* no health state tracking for ephemeral btrees */
+               return;
+       case XFS_BTREE_TYPE_AG:
+               ASSERT(cur->bc_ops->sick_mask);
+               xfs_ag_mark_sick(cur->bc_ag.pag, cur->bc_ops->sick_mask);
+               return;
+       case XFS_BTREE_TYPE_INODE:
+               if (xfs_btree_is_bmap(cur->bc_ops)) {
+                       xfs_bmap_mark_sick(cur->bc_ino.ip,
+                                          cur->bc_ino.whichfork);
+                       return;
+               }
+               fallthrough;
+       default:
+               ASSERT(0);
+               return;
+       }
+}
+
+/*
+ * 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);
+}