2019-04-12 17:40:25 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright ( C ) 2019 Oracle . All Rights Reserved .
* Author : Darrick J . Wong < darrick . wong @ oracle . com >
*/
# 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"
2021-06-02 03:48:24 +03:00
# include "xfs_ag.h"
2019-04-12 17:40:25 +03:00
2019-04-12 17:41:16 +03:00
/*
* 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 ;
2021-08-19 04:46:53 +03:00
if ( xfs_is_shutdown ( mp ) )
2019-04-12 17:41:16 +03:00
return ;
/* Measure AG corruption levels. */
2021-06-02 03:48:24 +03:00
for_each_perag ( mp , agno , pag ) {
2019-04-12 17:41:16 +03:00
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 ) ;
}
}
2019-04-12 17:40:25 +03:00
/* 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 ) ;
2021-06-07 19:34:50 +03:00
/*
* 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 ) ;
2019-04-12 17:40:25 +03:00
}
/* 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 ) ;
}
2019-04-12 17:41:17 +03:00
/* 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 ) ;
}
2019-04-12 17:41:18 +03:00
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 ;
}
}
2019-04-12 17:41:18 +03:00
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 ,
2019-07-04 06:36:26 +03:00
struct xfs_bulkstat * bs )
2019-04-12 17:41:18 +03:00
{
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 ;
}
}