mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
9.4 KiB
395 lines
9.4 KiB
// SPDX-License-Identifier: GPL-2.0+ |
|
/* |
|
* Copyright (C) 2019 Oracle. All Rights Reserved. |
|
* Author: Darrick J. Wong <[email protected]> |
|
*/ |
|
#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" |
|
|
|
/* |
|
* Warn about metadata corruption that we detected but haven't fixed, and |
|
* make sure we're not sitting on anything that would get in the way of |
|
* recovery. |
|
*/ |
|
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; |
|
|
|
/* 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; |
|
} |
|
} |
|
|
|
/* Measure realtime volume corruption levels. */ |
|
xfs_rt_measure_sickness(mp, &sick, &checked); |
|
if (sick) { |
|
trace_xfs_rt_unfixed_corruption(mp, sick); |
|
warn = true; |
|
} |
|
|
|
/* |
|
* Measure fs corruption and keep the sample around for the warning. |
|
* See the note below for why we exempt FS_COUNTERS. |
|
*/ |
|
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."); |
|
|
|
/* |
|
* We discovered uncorrected metadata problems at some point |
|
* during this filesystem mount and have advised the |
|
* administrator to run repair once the unmount completes. |
|
* |
|
* However, we must be careful -- when FSCOUNTERS are flagged |
|
* unhealthy, the unmount procedure omits writing the clean |
|
* unmount record to the log so that the next mount will run |
|
* recovery and recompute the summary counters. In other |
|
* words, we leave a dirty log to get the counters fixed. |
|
* |
|
* Unfortunately, xfs_repair cannot recover dirty logs, so if |
|
* there were filesystem problems, FSCOUNTERS was flagged, and |
|
* the administrator takes our advice to run xfs_repair, |
|
* they'll have to zap the log before repairing structures. |
|
* We don't really want to encourage this, so we mark the |
|
* FSCOUNTERS healthy so that a subsequent repair run won't see |
|
* a dirty log. |
|
*/ |
|
if (sick & XFS_SICK_FS_COUNTERS) |
|
xfs_fs_mark_healthy(mp, XFS_SICK_FS_COUNTERS); |
|
} |
|
} |
|
|
|
/* Mark unhealthy per-fs metadata. */ |
|
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); |
|
} |
|
|
|
/* Mark a per-fs metadata healed. */ |
|
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); |
|
} |
|
|
|
/* Sample which per-fs metadata are unhealthy. */ |
|
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); |
|
} |
|
|
|
/* Mark unhealthy realtime metadata. */ |
|
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); |
|
} |
|
|
|
/* Mark a realtime metadata healed. */ |
|
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); |
|
} |
|
|
|
/* Sample which realtime metadata are unhealthy. */ |
|
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); |
|
} |
|
|
|
/* Mark unhealthy per-ag metadata. */ |
|
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); |
|
} |
|
|
|
/* Mark per-ag metadata ok. */ |
|
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); |
|
} |
|
|
|
/* Sample which per-ag metadata are unhealthy. */ |
|
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); |
|
} |
|
|
|
/* Mark the unhealthy parts of an inode. */ |
|
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); |
|
|
|
/* |
|
* 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 parts of an inode healed. */ |
|
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); |
|
} |
|
|
|
/* Sample which parts of an inode are unhealthy. */ |
|
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); |
|
} |
|
|
|
/* Mappings between internal sick masks and ioctl sick masks. */ |
|
|
|
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; |
|
} |
|
|
|
/* Fill out fs geometry health info. */ |
|
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 }, |
|
}; |
|
|
|
/* Fill out ag geometry health info. */ |
|
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 }, |
|
}; |
|
|
|
/* Fill out bulkstat health info. */ |
|
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; |
|
} |
|
}
|
|
|