#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_inode.h"
#include "xfs_trace.h"
#include "xfs_health.h"
#include "xfs_ag.h"
void
xfs_health_unmount(
struct xfs_mount *mp)
{
struct xfs_perag *pag;
xfs_agnumber_t agno;
unsigned int sick = 0;
unsigned int checked = 0;
bool warn = false;
if (xfs_is_shutdown(mp))
return;
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;
}
}
xfs_rt_measure_sickness(mp, &sick, &checked);
if (sick) {
trace_xfs_rt_unfixed_corruption(mp, sick);
warn = true;
}
xfs_fs_measure_sickness(mp, &sick, &checked);
if (sick & ~XFS_SICK_FS_COUNTERS) {
trace_xfs_fs_unfixed_corruption(mp, sick);
warn = true;
}
if (warn) {
xfs_warn(mp,
"Uncorrected metadata errors detected; please run xfs_repair.");
if (sick & XFS_SICK_FS_COUNTERS)
xfs_fs_mark_healthy(mp, XFS_SICK_FS_COUNTERS);
}
}
void
xfs_fs_mark_sick(
struct xfs_mount *mp,
unsigned int mask)
{
ASSERT(!(mask & ~XFS_SICK_FS_PRIMARY));
trace_xfs_fs_mark_sick(mp, mask);
spin_lock(&mp->m_sb_lock);
mp->m_fs_sick |= mask;
mp->m_fs_checked |= mask;
spin_unlock(&mp->m_sb_lock);
}
void
xfs_fs_mark_healthy(
struct xfs_mount *mp,
unsigned int mask)
{
ASSERT(!(mask & ~XFS_SICK_FS_PRIMARY));
trace_xfs_fs_mark_healthy(mp, mask);
spin_lock(&mp->m_sb_lock);
mp->m_fs_sick &= ~mask;
mp->m_fs_checked |= mask;
spin_unlock(&mp->m_sb_lock);
}
void
xfs_fs_measure_sickness(
struct xfs_mount *mp,
unsigned int *sick,
unsigned int *checked)
{
spin_lock(&mp->m_sb_lock);
*sick = mp->m_fs_sick;
*checked = mp->m_fs_checked;
spin_unlock(&mp->m_sb_lock);
}
void
xfs_rt_mark_sick(
struct xfs_mount *mp,
unsigned int mask)
{
ASSERT(!(mask & ~XFS_SICK_RT_PRIMARY));
trace_xfs_rt_mark_sick(mp, mask);
spin_lock(&mp->m_sb_lock);
mp->m_rt_sick |= mask;
mp->m_rt_checked |= mask;
spin_unlock(&mp->m_sb_lock);
}
void
xfs_rt_mark_healthy(
struct xfs_mount *mp,
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);
}
void
xfs_rt_measure_sickness(
struct xfs_mount *mp,
unsigned int *sick,
unsigned int *checked)
{
spin_lock(&mp->m_sb_lock);
*sick = mp->m_rt_sick;
*checked = mp->m_rt_checked;
spin_unlock(&mp->m_sb_lock);
}
void
xfs_ag_mark_sick(
struct xfs_perag *pag,
unsigned int mask)
{
ASSERT(!(mask & ~XFS_SICK_AG_PRIMARY));
trace_xfs_ag_mark_sick(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);
}
void
xfs_ag_mark_healthy(
struct xfs_perag *pag,
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);
}
void
xfs_ag_measure_sickness(
struct xfs_perag *pag,
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);
}
void
xfs_inode_mark_sick(
struct xfs_inode *ip,
unsigned int mask)
{
ASSERT(!(mask & ~XFS_SICK_INO_PRIMARY));
trace_xfs_inode_mark_sick(ip, mask);
spin_lock(&ip->i_flags_lock);
ip->i_sick |= mask;
ip->i_checked |= mask;
spin_unlock(&ip->i_flags_lock);
spin_lock(&VFS_I(ip)->i_lock);
VFS_I(ip)->i_state &= ~I_DONTCACHE;
spin_unlock(&VFS_I(ip)->i_lock);
}
void
xfs_inode_mark_healthy(
struct xfs_inode *ip,
unsigned int mask)
{
ASSERT(!(mask & ~XFS_SICK_INO_PRIMARY));
trace_xfs_inode_mark_healthy(ip, mask);
spin_lock(&ip->i_flags_lock);
ip->i_sick &= ~mask;
ip->i_checked |= mask;
spin_unlock(&ip->i_flags_lock);
}
void
xfs_inode_measure_sickness(
struct xfs_inode *ip,
unsigned int *sick,
unsigned int *checked)
{
spin_lock(&ip->i_flags_lock);
*sick = ip->i_sick;
*checked = ip->i_checked;
spin_unlock(&ip->i_flags_lock);
}
struct ioctl_sick_map {
unsigned int sick_mask;
unsigned int ioctl_mask;
};
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 },
};
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 },
};
static inline void
xfgeo_health_tick(
struct xfs_fsop_geom *geo,
unsigned int sick,
unsigned int checked,
const struct ioctl_sick_map *m)
{
if (checked & m->sick_mask)
geo->checked |= m->ioctl_mask;
if (sick & m->sick_mask)
geo->sick |= m->ioctl_mask;
}
void
xfs_fsop_geom_health(
struct xfs_mount *mp,
struct xfs_fsop_geom *geo)
{
const struct ioctl_sick_map *m;
unsigned int sick;
unsigned int checked;
geo->sick = 0;
geo->checked = 0;
xfs_fs_measure_sickness(mp, &sick, &checked);
for (m = fs_map; m->sick_mask; 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);
}
static const struct ioctl_sick_map ag_map[] = {
{ XFS_SICK_AG_SB, XFS_AG_GEOM_SICK_SB },
{ XFS_SICK_AG_AGF, XFS_AG_GEOM_SICK_AGF },
{ XFS_SICK_AG_AGFL, XFS_AG_GEOM_SICK_AGFL },
{ XFS_SICK_AG_AGI, XFS_AG_GEOM_SICK_AGI },
{ XFS_SICK_AG_BNOBT, XFS_AG_GEOM_SICK_BNOBT },
{ XFS_SICK_AG_CNTBT, XFS_AG_GEOM_SICK_CNTBT },
{ XFS_SICK_AG_INOBT, XFS_AG_GEOM_SICK_INOBT },
{ 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 },
};
void
xfs_ag_geom_health(
struct xfs_perag *pag,
struct xfs_ag_geometry *ageo)
{
const struct ioctl_sick_map *m;
unsigned int sick;
unsigned int checked;
ageo->ag_sick = 0;
ageo->ag_checked = 0;
xfs_ag_measure_sickness(pag, &sick, &checked);
for (m = ag_map; m->sick_mask; m++) {
if (checked & m->sick_mask)
ageo->ag_checked |= m->ioctl_mask;
if (sick & m->sick_mask)
ageo->ag_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 },
{ XFS_SICK_INO_BMBTA, XFS_BS_SICK_BMBTA },
{ XFS_SICK_INO_BMBTC, XFS_BS_SICK_BMBTC },
{ XFS_SICK_INO_DIR, XFS_BS_SICK_DIR },
{ XFS_SICK_INO_XATTR, XFS_BS_SICK_XATTR },
{ XFS_SICK_INO_SYMLINK, XFS_BS_SICK_SYMLINK },
{ XFS_SICK_INO_PARENT, XFS_BS_SICK_PARENT },
{ 0, 0 },
};
void
xfs_bulkstat_health(
struct xfs_inode *ip,
struct xfs_bulkstat *bs)
{
const struct ioctl_sick_map *m;
unsigned int sick;
unsigned int checked;
bs->bs_sick = 0;
bs->bs_checked = 0;
xfs_inode_measure_sickness(ip, &sick, &checked);
for (m = ino_map; m->sick_mask; m++) {
if (checked & m->sick_mask)
bs->bs_checked |= m->ioctl_mask;
if (sick & m->sick_mask)
bs->bs_sick |= m->ioctl_mask;
}
}