mirror of
https://github.com/torvalds/linux
synced 2024-10-14 15:29:13 +00:00
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: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
50645ce882
commit
de6077ec41
|
@ -26,6 +26,7 @@
|
|||
#include "xfs_ag.h"
|
||||
#include "xfs_ag_resv.h"
|
||||
#include "xfs_bmap.h"
|
||||
#include "xfs_health.h"
|
||||
|
||||
struct kmem_cache *xfs_extfree_item_cache;
|
||||
|
||||
|
@ -755,6 +756,8 @@ xfs_alloc_read_agfl(
|
|||
mp, tp, mp->m_ddev_targp,
|
||||
XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGFL_DADDR(mp)),
|
||||
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)
|
||||
return error;
|
||||
xfs_buf_set_ref(bp, XFS_AGFL_REF);
|
||||
|
@ -776,6 +779,7 @@ xfs_alloc_update_counters(
|
|||
if (unlikely(be32_to_cpu(agf->agf_freeblks) >
|
||||
be32_to_cpu(agf->agf_length))) {
|
||||
xfs_buf_mark_corrupt(agbp);
|
||||
xfs_ag_mark_sick(agbp->b_pag, XFS_SICK_AG_AGF);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
|
@ -3268,6 +3272,8 @@ xfs_read_agf(
|
|||
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
|
||||
XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGF_DADDR(mp)),
|
||||
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)
|
||||
return error;
|
||||
|
||||
|
|
|
@ -26,9 +26,11 @@
|
|||
* and the "sick" field tells us if that piece was found to need repairs.
|
||||
* Therefore we can conclude that for a given sick flag value:
|
||||
*
|
||||
* - checked && sick => metadata needs repair
|
||||
* - checked && !sick => metadata is ok
|
||||
* - !checked => has not been examined since mount
|
||||
* - checked && sick => metadata needs repair
|
||||
* - checked && !sick => metadata is ok
|
||||
* - !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;
|
||||
|
@ -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,
|
||||
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_corrupt(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);
|
|||
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);
|
||||
|
||||
#define xfs_metadata_is_sick(error) \
|
||||
(unlikely((error) == -EFSCORRUPTED || (error) == -EFSBADCRC))
|
||||
|
||||
#endif /* __XFS_HEALTH_H__ */
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "xfs_log.h"
|
||||
#include "xfs_rmap.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_health.h"
|
||||
|
||||
/*
|
||||
* Lookup a record by ino in the btree given by cur.
|
||||
|
@ -2604,6 +2605,8 @@ xfs_read_agi(
|
|||
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
|
||||
XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGI_DADDR(mp)),
|
||||
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)
|
||||
return error;
|
||||
if (tp)
|
||||
|
|
|
@ -1290,6 +1290,8 @@ xfs_sb_read_secondary(
|
|||
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
|
||||
XFS_AG_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
|
||||
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)
|
||||
return error;
|
||||
xfs_buf_set_ref(bp, XFS_SSB_REF);
|
||||
|
|
|
@ -201,6 +201,23 @@ xfs_rt_measure_sickness(
|
|||
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. */
|
||||
void
|
||||
xfs_ag_mark_sick(
|
||||
|
|
|
@ -780,6 +780,8 @@ xfs_init_new_inode(
|
|||
*/
|
||||
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_agno_mark_sick(mp, XFS_INO_TO_AGNO(mp, ino),
|
||||
XFS_SICK_AG_INOBT);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
|
@ -1970,6 +1972,7 @@ xfs_iunlink_update_bucket(
|
|||
*/
|
||||
if (old_value == new_agino) {
|
||||
xfs_buf_mark_corrupt(agibp);
|
||||
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
|
@ -2019,11 +2022,14 @@ xfs_iunlink_reload_next(
|
|||
*/
|
||||
ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, next_agino);
|
||||
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;
|
||||
}
|
||||
|
||||
/* If this is not an unlinked inode, something is very wrong. */
|
||||
if (VFS_I(next_ip)->i_nlink != 0) {
|
||||
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
|
||||
error = -EFSCORRUPTED;
|
||||
goto rele;
|
||||
}
|
||||
|
@ -2061,6 +2067,7 @@ xfs_iunlink_insert_inode(
|
|||
if (next_agino == agino ||
|
||||
!xfs_verify_agino_or_null(pag, next_agino)) {
|
||||
xfs_buf_mark_corrupt(agibp);
|
||||
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
|
@ -2148,6 +2155,7 @@ xfs_iunlink_remove_inode(
|
|||
if (!xfs_verify_agino(pag, head_agino)) {
|
||||
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
|
||||
agi, sizeof(*agi));
|
||||
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
|
@ -2176,8 +2184,10 @@ xfs_iunlink_remove_inode(
|
|||
struct xfs_inode *prev_ip;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
error = xfs_iunlink_log_inode(tp, prev_ip, pag,
|
||||
ip->i_next_unlinked);
|
||||
|
|
Loading…
Reference in a new issue