summaryrefslogtreecommitdiff
path: root/fs/xfs/xfs_health.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/xfs_health.c')
-rw-r--r--fs/xfs/xfs_health.c361
1 files changed, 276 insertions, 85 deletions
diff --git a/fs/xfs/xfs_health.c b/fs/xfs/xfs_health.c
index 9a57afee9338..7c541fb373d5 100644
--- a/fs/xfs/xfs_health.c
+++ b/fs/xfs/xfs_health.c
@@ -14,6 +14,26 @@
#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"
+#include "xfs_rtgroup.h"
+
+static void
+xfs_health_unmount_group(
+ struct xfs_group *xg,
+ bool *warn)
+{
+ unsigned int sick = 0;
+ unsigned int checked = 0;
+
+ xfs_group_measure_sickness(xg, &sick, &checked);
+ if (sick) {
+ trace_xfs_group_unfixed_corruption(xg, sick);
+ *warn = true;
+ }
+}
/*
* Warn about metadata corruption that we detected but haven't fixed, and
@@ -24,8 +44,8 @@ void
xfs_health_unmount(
struct xfs_mount *mp)
{
- struct xfs_perag *pag;
- xfs_agnumber_t agno;
+ struct xfs_perag *pag = NULL;
+ struct xfs_rtgroup *rtg = NULL;
unsigned int sick = 0;
unsigned int checked = 0;
bool warn = false;
@@ -34,20 +54,12 @@ xfs_health_unmount(
return;
/* Measure AG corruption levels. */
- for_each_perag(mp, agno, pag) {
- xfs_ag_measure_sickness(pag, &sick, &checked);
- if (sick) {
- trace_xfs_ag_unfixed_corruption(mp, agno, sick);
- warn = true;
- }
- }
+ while ((pag = xfs_perag_next(mp, pag)))
+ xfs_health_unmount_group(pag_group(pag), &warn);
- /* Measure realtime volume corruption levels. */
- xfs_rt_measure_sickness(mp, &sick, &checked);
- if (sick) {
- trace_xfs_rt_unfixed_corruption(mp, sick);
- warn = true;
- }
+ /* Measure realtime group corruption levels. */
+ while ((rtg = xfs_rtgroup_next(mp, rtg)))
+ xfs_health_unmount_group(rtg_group(rtg), &warn);
/*
* Measure fs corruption and keep the sample around for the warning.
@@ -93,11 +105,25 @@ 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;
spin_unlock(&mp->m_sb_lock);
}
@@ -108,11 +134,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);
}
@@ -130,90 +158,112 @@ xfs_fs_measure_sickness(
spin_unlock(&mp->m_sb_lock);
}
-/* Mark unhealthy realtime metadata. */
+/* Mark unhealthy per-ag metadata given a raw AG number. */
void
-xfs_rt_mark_sick(
+xfs_agno_mark_sick(
struct xfs_mount *mp,
+ xfs_agnumber_t agno,
unsigned int mask)
{
- ASSERT(!(mask & ~XFS_SICK_RT_PRIMARY));
- trace_xfs_rt_mark_sick(mp, mask);
+ struct xfs_perag *pag = xfs_perag_get(mp, agno);
- spin_lock(&mp->m_sb_lock);
- mp->m_rt_sick |= mask;
- mp->m_rt_checked |= mask;
- spin_unlock(&mp->m_sb_lock);
+ /* per-ag structure not set up yet? */
+ if (!pag)
+ return;
+
+ xfs_ag_mark_sick(pag, mask);
+ xfs_perag_put(pag);
}
-/* Mark a realtime metadata healed. */
-void
-xfs_rt_mark_healthy(
- struct xfs_mount *mp,
+static inline void
+xfs_group_check_mask(
+ struct xfs_group *xg,
unsigned int mask)
{
- ASSERT(!(mask & ~XFS_SICK_RT_PRIMARY));
- trace_xfs_rt_mark_healthy(mp, mask);
-
- spin_lock(&mp->m_sb_lock);
- mp->m_rt_sick &= ~mask;
- mp->m_rt_checked |= mask;
- spin_unlock(&mp->m_sb_lock);
+ if (xg->xg_type == XG_TYPE_AG)
+ ASSERT(!(mask & ~XFS_SICK_AG_ALL));
+ else
+ ASSERT(!(mask & ~XFS_SICK_RG_ALL));
}
-/* Sample which realtime metadata are unhealthy. */
+/* Mark unhealthy per-ag metadata. */
void
-xfs_rt_measure_sickness(
- struct xfs_mount *mp,
- unsigned int *sick,
- unsigned int *checked)
+xfs_group_mark_sick(
+ struct xfs_group *xg,
+ unsigned int mask)
{
- spin_lock(&mp->m_sb_lock);
- *sick = mp->m_rt_sick;
- *checked = mp->m_rt_checked;
- spin_unlock(&mp->m_sb_lock);
+ xfs_group_check_mask(xg, mask);
+ trace_xfs_group_mark_sick(xg, mask);
+
+ spin_lock(&xg->xg_state_lock);
+ xg->xg_sick |= mask;
+ spin_unlock(&xg->xg_state_lock);
}
-/* Mark unhealthy per-ag metadata. */
+/*
+ * Mark per-group metadata as having been checked and found unhealthy by fsck.
+ */
void
-xfs_ag_mark_sick(
- struct xfs_perag *pag,
+xfs_group_mark_corrupt(
+ struct xfs_group *xg,
unsigned int mask)
{
- ASSERT(!(mask & ~XFS_SICK_AG_PRIMARY));
- trace_xfs_ag_mark_sick(pag->pag_mount, pag->pag_agno, mask);
+ xfs_group_check_mask(xg, mask);
+ trace_xfs_group_mark_corrupt(xg, mask);
- spin_lock(&pag->pag_state_lock);
- pag->pag_sick |= mask;
- pag->pag_checked |= mask;
- spin_unlock(&pag->pag_state_lock);
+ spin_lock(&xg->xg_state_lock);
+ xg->xg_sick |= mask;
+ xg->xg_checked |= mask;
+ spin_unlock(&xg->xg_state_lock);
}
-/* Mark per-ag metadata ok. */
+/*
+ * Mark per-group metadata ok.
+ */
void
-xfs_ag_mark_healthy(
- struct xfs_perag *pag,
+xfs_group_mark_healthy(
+ struct xfs_group *xg,
unsigned int mask)
{
- ASSERT(!(mask & ~XFS_SICK_AG_PRIMARY));
- trace_xfs_ag_mark_healthy(pag->pag_mount, pag->pag_agno, mask);
-
- spin_lock(&pag->pag_state_lock);
- pag->pag_sick &= ~mask;
- pag->pag_checked |= mask;
- spin_unlock(&pag->pag_state_lock);
+ xfs_group_check_mask(xg, mask);
+ trace_xfs_group_mark_healthy(xg, mask);
+
+ spin_lock(&xg->xg_state_lock);
+ xg->xg_sick &= ~mask;
+ if (!(xg->xg_sick & XFS_SICK_AG_PRIMARY))
+ xg->xg_sick &= ~XFS_SICK_AG_SECONDARY;
+ xg->xg_checked |= mask;
+ spin_unlock(&xg->xg_state_lock);
}
/* Sample which per-ag metadata are unhealthy. */
void
-xfs_ag_measure_sickness(
- struct xfs_perag *pag,
+xfs_group_measure_sickness(
+ struct xfs_group *xg,
unsigned int *sick,
unsigned int *checked)
{
- spin_lock(&pag->pag_state_lock);
- *sick = pag->pag_sick;
- *checked = pag->pag_checked;
- spin_unlock(&pag->pag_state_lock);
+ spin_lock(&xg->xg_state_lock);
+ *sick = xg->xg_sick;
+ *checked = xg->xg_checked;
+ spin_unlock(&xg->xg_state_lock);
+}
+
+/* Mark unhealthy per-rtgroup metadata given a raw rt group number. */
+void
+xfs_rgno_mark_sick(
+ struct xfs_mount *mp,
+ xfs_rgnumber_t rgno,
+ unsigned int mask)
+{
+ struct xfs_rtgroup *rtg = xfs_rtgroup_get(mp, rgno);
+
+ /* per-rtgroup structure not set up yet? */
+ if (!rtg)
+ return;
+
+ xfs_group_mark_sick(rtg_group(rtg), mask);
+ xfs_rtgroup_put(rtg);
}
/* Mark the unhealthy parts of an inode. */
@@ -222,11 +272,34 @@ 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;
spin_unlock(&ip->i_flags_lock);
@@ -246,11 +319,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);
}
@@ -275,18 +350,23 @@ struct ioctl_sick_map {
unsigned int ioctl_mask;
};
+#define for_each_sick_map(map, m) \
+ for ((m) = (map); (m) < (map) + ARRAY_SIZE(map); (m)++)
+
static const struct ioctl_sick_map fs_map[] = {
{ XFS_SICK_FS_COUNTERS, XFS_FSOP_GEOM_SICK_COUNTERS},
{ 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 },
- { 0, 0 },
+ { XFS_SICK_FS_QUOTACHECK, XFS_FSOP_GEOM_SICK_QUOTACHECK },
+ { XFS_SICK_FS_NLINKS, XFS_FSOP_GEOM_SICK_NLINKS },
+ { XFS_SICK_FS_METADIR, XFS_FSOP_GEOM_SICK_METADIR },
+ { XFS_SICK_FS_METAPATH, XFS_FSOP_GEOM_SICK_METAPATH },
};
static const struct ioctl_sick_map rt_map[] = {
- { XFS_SICK_RT_BITMAP, XFS_FSOP_GEOM_SICK_RT_BITMAP },
- { XFS_SICK_RT_SUMMARY, XFS_FSOP_GEOM_SICK_RT_SUMMARY },
- { 0, 0 },
+ { XFS_SICK_RG_BITMAP, XFS_FSOP_GEOM_SICK_RT_BITMAP },
+ { XFS_SICK_RG_SUMMARY, XFS_FSOP_GEOM_SICK_RT_SUMMARY },
};
static inline void
@@ -308,6 +388,7 @@ xfs_fsop_geom_health(
struct xfs_mount *mp,
struct xfs_fsop_geom *geo)
{
+ struct xfs_rtgroup *rtg = NULL;
const struct ioctl_sick_map *m;
unsigned int sick;
unsigned int checked;
@@ -316,12 +397,14 @@ xfs_fsop_geom_health(
geo->checked = 0;
xfs_fs_measure_sickness(mp, &sick, &checked);
- for (m = fs_map; m->sick_mask; m++)
+ for_each_sick_map(fs_map, m)
xfgeo_health_tick(geo, sick, checked, m);
- xfs_rt_measure_sickness(mp, &sick, &checked);
- for (m = rt_map; m->sick_mask; m++)
- xfgeo_health_tick(geo, sick, checked, m);
+ while ((rtg = xfs_rtgroup_next(mp, rtg))) {
+ xfs_group_measure_sickness(rtg_group(rtg), &sick, &checked);
+ for_each_sick_map(rt_map, m)
+ xfgeo_health_tick(geo, sick, checked, m);
+ }
}
static const struct ioctl_sick_map ag_map[] = {
@@ -335,7 +418,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 },
- { 0, 0 },
+ { XFS_SICK_AG_INODES, XFS_AG_GEOM_SICK_INODES },
};
/* Fill out ag geometry health info. */
@@ -351,8 +434,8 @@ xfs_ag_geom_health(
ageo->ag_sick = 0;
ageo->ag_checked = 0;
- xfs_ag_measure_sickness(pag, &sick, &checked);
- for (m = ag_map; m->sick_mask; m++) {
+ xfs_group_measure_sickness(pag_group(pag), &sick, &checked);
+ for_each_sick_map(ag_map, m) {
if (checked & m->sick_mask)
ageo->ag_checked |= m->ioctl_mask;
if (sick & m->sick_mask)
@@ -360,6 +443,36 @@ xfs_ag_geom_health(
}
}
+static const struct ioctl_sick_map rtgroup_map[] = {
+ { XFS_SICK_RG_SUPER, XFS_RTGROUP_GEOM_SICK_SUPER },
+ { XFS_SICK_RG_BITMAP, XFS_RTGROUP_GEOM_SICK_BITMAP },
+ { XFS_SICK_RG_SUMMARY, XFS_RTGROUP_GEOM_SICK_SUMMARY },
+ { XFS_SICK_RG_RMAPBT, XFS_RTGROUP_GEOM_SICK_RMAPBT },
+ { XFS_SICK_RG_REFCNTBT, XFS_RTGROUP_GEOM_SICK_REFCNTBT },
+};
+
+/* Fill out rtgroup geometry health info. */
+void
+xfs_rtgroup_geom_health(
+ struct xfs_rtgroup *rtg,
+ struct xfs_rtgroup_geometry *rgeo)
+{
+ const struct ioctl_sick_map *m;
+ unsigned int sick;
+ unsigned int checked;
+
+ rgeo->rg_sick = 0;
+ rgeo->rg_checked = 0;
+
+ xfs_group_measure_sickness(rtg_group(rtg), &sick, &checked);
+ for_each_sick_map(rtgroup_map, m) {
+ if (checked & m->sick_mask)
+ rgeo->rg_checked |= m->ioctl_mask;
+ if (sick & m->sick_mask)
+ rgeo->rg_sick |= m->ioctl_mask;
+ }
+}
+
static const struct ioctl_sick_map ino_map[] = {
{ XFS_SICK_INO_CORE, XFS_BS_SICK_INODE },
{ XFS_SICK_INO_BMBTD, XFS_BS_SICK_BMBTD },
@@ -373,7 +486,7 @@ static const struct ioctl_sick_map ino_map[] = {
{ XFS_SICK_INO_BMBTA_ZAPPED, XFS_BS_SICK_BMBTA },
{ XFS_SICK_INO_DIR_ZAPPED, XFS_BS_SICK_DIR },
{ XFS_SICK_INO_SYMLINK_ZAPPED, XFS_BS_SICK_SYMLINK },
- { 0, 0 },
+ { XFS_SICK_INO_DIRTREE, XFS_BS_SICK_DIRTREE },
};
/* Fill out bulkstat health info. */
@@ -390,10 +503,88 @@ xfs_bulkstat_health(
bs->bs_checked = 0;
xfs_inode_measure_sickness(ip, &sick, &checked);
- for (m = ino_map; m->sick_mask; m++) {
+ for_each_sick_map(ino_map, m) {
if (checked & m->sick_mask)
bs->bs_checked |= m->ioctl_mask;
if (sick & m->sick_mask)
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)
+{
+ if (xfs_btree_is_bmap(cur->bc_ops)) {
+ xfs_bmap_mark_sick(cur->bc_ino.ip, cur->bc_ino.whichfork);
+ /* no health state tracking for ephemeral btrees */
+ } else if (cur->bc_ops->type != XFS_BTREE_TYPE_MEM) {
+ ASSERT(cur->bc_group);
+ ASSERT(cur->bc_ops->sick_mask);
+ xfs_group_mark_sick(cur->bc_group, cur->bc_ops->sick_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);
+}