Commit de6077ec authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: report ag header corruption errors to the health tracking system

Whenever we encounter a corrupt AG header, we should report that to the
health monitoring system for later reporting.  Buffer readers that don't
respond to corruption events with a _mark_sick call can be detected with
the following script:

#!/bin/bash

# Detect missing calls to xfs_*_mark_sick

filter=cat
tty -s && filter=less

git grep -A10  -E '( = xfs_trans_read_buf| = xfs_buf_read\()' fs/xfs/*.[ch] fs/xfs/libxfs/*.[ch] | awk '
BEGIN {
	ignore = 0;
	lineno = 0;
	delete lines;
}
{
	if ($0 == "--") {
		if (!ignore) {
			for (i = 0; i < lineno; i++) {
				print(lines[i]);
			}
			printf("--\n");
		}
		delete lines;
		lineno = 0;
		ignore = 0;
	} else if ($0 ~ /mark_sick/) {
		ignore = 1;
	} else {
		lines[lineno++] = $0;
	}
}
' | $filter
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 50645ce8
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "xfs_ag.h" #include "xfs_ag.h"
#include "xfs_ag_resv.h" #include "xfs_ag_resv.h"
#include "xfs_bmap.h" #include "xfs_bmap.h"
#include "xfs_health.h"
struct kmem_cache *xfs_extfree_item_cache; struct kmem_cache *xfs_extfree_item_cache;
...@@ -755,6 +756,8 @@ xfs_alloc_read_agfl( ...@@ -755,6 +756,8 @@ xfs_alloc_read_agfl(
mp, tp, mp->m_ddev_targp, mp, tp, mp->m_ddev_targp,
XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGFL_DADDR(mp)), XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGFL_DADDR(mp)),
XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_agfl_buf_ops); XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_agfl_buf_ops);
if (xfs_metadata_is_sick(error))
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGFL);
if (error) if (error)
return error; return error;
xfs_buf_set_ref(bp, XFS_AGFL_REF); xfs_buf_set_ref(bp, XFS_AGFL_REF);
...@@ -776,6 +779,7 @@ xfs_alloc_update_counters( ...@@ -776,6 +779,7 @@ xfs_alloc_update_counters(
if (unlikely(be32_to_cpu(agf->agf_freeblks) > if (unlikely(be32_to_cpu(agf->agf_freeblks) >
be32_to_cpu(agf->agf_length))) { be32_to_cpu(agf->agf_length))) {
xfs_buf_mark_corrupt(agbp); xfs_buf_mark_corrupt(agbp);
xfs_ag_mark_sick(agbp->b_pag, XFS_SICK_AG_AGF);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
...@@ -3268,6 +3272,8 @@ xfs_read_agf( ...@@ -3268,6 +3272,8 @@ xfs_read_agf(
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGF_DADDR(mp)), XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGF_DADDR(mp)),
XFS_FSS_TO_BB(mp, 1), flags, agfbpp, &xfs_agf_buf_ops); XFS_FSS_TO_BB(mp, 1), flags, agfbpp, &xfs_agf_buf_ops);
if (xfs_metadata_is_sick(error))
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGF);
if (error) if (error)
return error; return error;
......
...@@ -28,7 +28,9 @@ ...@@ -28,7 +28,9 @@
* *
* - checked && sick => metadata needs repair * - checked && sick => metadata needs repair
* - checked && !sick => metadata is ok * - checked && !sick => metadata is ok
* - !checked => has not been examined since mount * - !checked && sick => errors have been observed during normal operation,
* but the metadata has not been checked thoroughly
* - !checked && !sick => has not been examined since mount
*/ */
struct xfs_mount; struct xfs_mount;
...@@ -142,6 +144,8 @@ void xfs_rt_mark_healthy(struct xfs_mount *mp, unsigned int mask); ...@@ -142,6 +144,8 @@ void xfs_rt_mark_healthy(struct xfs_mount *mp, unsigned int mask);
void xfs_rt_measure_sickness(struct xfs_mount *mp, unsigned int *sick, void xfs_rt_measure_sickness(struct xfs_mount *mp, unsigned int *sick,
unsigned int *checked); unsigned int *checked);
void xfs_agno_mark_sick(struct xfs_mount *mp, xfs_agnumber_t agno,
unsigned int mask);
void xfs_ag_mark_sick(struct xfs_perag *pag, unsigned int mask); void xfs_ag_mark_sick(struct xfs_perag *pag, unsigned int mask);
void xfs_ag_mark_corrupt(struct xfs_perag *pag, unsigned int mask); void xfs_ag_mark_corrupt(struct xfs_perag *pag, unsigned int mask);
void xfs_ag_mark_healthy(struct xfs_perag *pag, unsigned int mask); void xfs_ag_mark_healthy(struct xfs_perag *pag, unsigned int mask);
...@@ -222,4 +226,7 @@ void xfs_fsop_geom_health(struct xfs_mount *mp, struct xfs_fsop_geom *geo); ...@@ -222,4 +226,7 @@ void xfs_fsop_geom_health(struct xfs_mount *mp, struct xfs_fsop_geom *geo);
void xfs_ag_geom_health(struct xfs_perag *pag, struct xfs_ag_geometry *ageo); void xfs_ag_geom_health(struct xfs_perag *pag, struct xfs_ag_geometry *ageo);
void xfs_bulkstat_health(struct xfs_inode *ip, struct xfs_bulkstat *bs); void xfs_bulkstat_health(struct xfs_inode *ip, struct xfs_bulkstat *bs);
#define xfs_metadata_is_sick(error) \
(unlikely((error) == -EFSCORRUPTED || (error) == -EFSBADCRC))
#endif /* __XFS_HEALTH_H__ */ #endif /* __XFS_HEALTH_H__ */
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "xfs_log.h" #include "xfs_log.h"
#include "xfs_rmap.h" #include "xfs_rmap.h"
#include "xfs_ag.h" #include "xfs_ag.h"
#include "xfs_health.h"
/* /*
* Lookup a record by ino in the btree given by cur. * Lookup a record by ino in the btree given by cur.
...@@ -2604,6 +2605,8 @@ xfs_read_agi( ...@@ -2604,6 +2605,8 @@ xfs_read_agi(
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGI_DADDR(mp)), XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGI_DADDR(mp)),
XFS_FSS_TO_BB(mp, 1), 0, agibpp, &xfs_agi_buf_ops); XFS_FSS_TO_BB(mp, 1), 0, agibpp, &xfs_agi_buf_ops);
if (xfs_metadata_is_sick(error))
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
if (error) if (error)
return error; return error;
if (tp) if (tp)
......
...@@ -1290,6 +1290,8 @@ xfs_sb_read_secondary( ...@@ -1290,6 +1290,8 @@ xfs_sb_read_secondary(
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
XFS_AG_DADDR(mp, agno, XFS_SB_BLOCK(mp)), XFS_AG_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_sb_buf_ops); XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_sb_buf_ops);
if (xfs_metadata_is_sick(error))
xfs_agno_mark_sick(mp, agno, XFS_SICK_AG_SB);
if (error) if (error)
return error; return error;
xfs_buf_set_ref(bp, XFS_SSB_REF); xfs_buf_set_ref(bp, XFS_SSB_REF);
......
...@@ -201,6 +201,23 @@ xfs_rt_measure_sickness( ...@@ -201,6 +201,23 @@ xfs_rt_measure_sickness(
spin_unlock(&mp->m_sb_lock); 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. */ /* Mark unhealthy per-ag metadata. */
void void
xfs_ag_mark_sick( xfs_ag_mark_sick(
......
...@@ -780,6 +780,8 @@ xfs_init_new_inode( ...@@ -780,6 +780,8 @@ xfs_init_new_inode(
*/ */
if ((pip && ino == pip->i_ino) || !xfs_verify_dir_ino(mp, ino)) { if ((pip && ino == pip->i_ino) || !xfs_verify_dir_ino(mp, ino)) {
xfs_alert(mp, "Allocated a known in-use inode 0x%llx!", ino); xfs_alert(mp, "Allocated a known in-use inode 0x%llx!", ino);
xfs_agno_mark_sick(mp, XFS_INO_TO_AGNO(mp, ino),
XFS_SICK_AG_INOBT);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
...@@ -1970,6 +1972,7 @@ xfs_iunlink_update_bucket( ...@@ -1970,6 +1972,7 @@ xfs_iunlink_update_bucket(
*/ */
if (old_value == new_agino) { if (old_value == new_agino) {
xfs_buf_mark_corrupt(agibp); xfs_buf_mark_corrupt(agibp);
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
...@@ -2019,11 +2022,14 @@ xfs_iunlink_reload_next( ...@@ -2019,11 +2022,14 @@ xfs_iunlink_reload_next(
*/ */
ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, next_agino); ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, next_agino);
error = xfs_iget(mp, tp, ino, XFS_IGET_UNTRUSTED, 0, &next_ip); error = xfs_iget(mp, tp, ino, XFS_IGET_UNTRUSTED, 0, &next_ip);
if (error) if (error) {
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
return error; return error;
}
/* If this is not an unlinked inode, something is very wrong. */ /* If this is not an unlinked inode, something is very wrong. */
if (VFS_I(next_ip)->i_nlink != 0) { if (VFS_I(next_ip)->i_nlink != 0) {
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto rele; goto rele;
} }
...@@ -2061,6 +2067,7 @@ xfs_iunlink_insert_inode( ...@@ -2061,6 +2067,7 @@ xfs_iunlink_insert_inode(
if (next_agino == agino || if (next_agino == agino ||
!xfs_verify_agino_or_null(pag, next_agino)) { !xfs_verify_agino_or_null(pag, next_agino)) {
xfs_buf_mark_corrupt(agibp); xfs_buf_mark_corrupt(agibp);
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
...@@ -2148,6 +2155,7 @@ xfs_iunlink_remove_inode( ...@@ -2148,6 +2155,7 @@ xfs_iunlink_remove_inode(
if (!xfs_verify_agino(pag, head_agino)) { if (!xfs_verify_agino(pag, head_agino)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
agi, sizeof(*agi)); agi, sizeof(*agi));
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
...@@ -2176,8 +2184,10 @@ xfs_iunlink_remove_inode( ...@@ -2176,8 +2184,10 @@ xfs_iunlink_remove_inode(
struct xfs_inode *prev_ip; struct xfs_inode *prev_ip;
prev_ip = xfs_iunlink_lookup(pag, ip->i_prev_unlinked); prev_ip = xfs_iunlink_lookup(pag, ip->i_prev_unlinked);
if (!prev_ip) if (!prev_ip) {
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE);
return -EFSCORRUPTED; return -EFSCORRUPTED;
}
error = xfs_iunlink_log_inode(tp, prev_ip, pag, error = xfs_iunlink_log_inode(tp, prev_ip, pag,
ip->i_next_unlinked); ip->i_next_unlinked);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment