xfs: convey filesystem shutdown events to the health monitor

Connect the filesystem shutdown code to the health monitor so that xfs
can send events about that to the xfs_healer daemon.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong 2026-01-20 18:06:48 -08:00
parent 5eb4cb18e4
commit 74c4795e50
5 changed files with 121 additions and 1 deletions

View File

@ -1028,6 +1028,9 @@ struct xfs_rtgroup_geometry {
#define XFS_HEALTH_MONITOR_TYPE_CORRUPT (4)
#define XFS_HEALTH_MONITOR_TYPE_HEALTHY (5)
/* filesystem shutdown */
#define XFS_HEALTH_MONITOR_TYPE_SHUTDOWN (6)
/* lost events */
struct xfs_health_monitor_lost {
__u64 count;
@ -1054,6 +1057,20 @@ struct xfs_health_monitor_inode {
__u64 ino;
};
/* shutdown reasons */
#define XFS_HEALTH_SHUTDOWN_META_IO_ERROR (1u << 0)
#define XFS_HEALTH_SHUTDOWN_LOG_IO_ERROR (1u << 1)
#define XFS_HEALTH_SHUTDOWN_FORCE_UMOUNT (1u << 2)
#define XFS_HEALTH_SHUTDOWN_CORRUPT_INCORE (1u << 3)
#define XFS_HEALTH_SHUTDOWN_CORRUPT_ONDISK (1u << 4)
#define XFS_HEALTH_SHUTDOWN_DEVICE_REMOVED (1u << 5)
/* shutdown */
struct xfs_health_monitor_shutdown {
/* XFS_HEALTH_SHUTDOWN_* flags */
__u32 reasons;
};
struct xfs_health_monitor_event {
/* XFS_HEALTH_MONITOR_DOMAIN_* */
__u32 domain;
@ -1074,6 +1091,7 @@ struct xfs_health_monitor_event {
struct xfs_health_monitor_fs fs;
struct xfs_health_monitor_group group;
struct xfs_health_monitor_inode inode;
struct xfs_health_monitor_shutdown shutdown;
} e;
/* zeroes */

View File

@ -25,6 +25,7 @@
#include "xfs_rtrmap_btree.h"
#include "xfs_rtrefcount_btree.h"
#include "xfs_metafile.h"
#include "xfs_healthmon.h"
#include <linux/fserror.h>
@ -544,6 +545,7 @@ xfs_do_force_shutdown(
xfs_stack_trace();
fserror_report_shutdown(mp->m_super, GFP_KERNEL);
xfs_healthmon_report_shutdown(mp, flags);
}
/*

View File

@ -20,6 +20,7 @@
#include "xfs_rtgroup.h"
#include "xfs_health.h"
#include "xfs_healthmon.h"
#include "xfs_fsops.h"
#include <linux/anon_inodes.h>
#include <linux/eventpoll.h>
@ -202,6 +203,11 @@ xfs_healthmon_merge_events(
return false;
}
return false;
case XFS_HEALTHMON_SHUTDOWN:
/* yes, we can race to shutdown */
existing->flags |= new->flags;
return true;
}
return false;
@ -494,6 +500,28 @@ xfs_healthmon_report_inode(
xfs_healthmon_put(hm);
}
/* Add a shutdown event to the reporting queue. */
void
xfs_healthmon_report_shutdown(
struct xfs_mount *mp,
uint32_t flags)
{
struct xfs_healthmon_event event = {
.type = XFS_HEALTHMON_SHUTDOWN,
.domain = XFS_HEALTHMON_MOUNT,
.flags = flags,
};
struct xfs_healthmon *hm = xfs_healthmon_get(mp);
if (!hm)
return;
trace_xfs_healthmon_report_shutdown(hm, flags);
xfs_healthmon_push(hm, &event);
xfs_healthmon_put(hm);
}
static inline void
xfs_healthmon_reset_outbuf(
struct xfs_healthmon *hm)
@ -502,6 +530,44 @@ xfs_healthmon_reset_outbuf(
hm->bufhead = 0;
}
struct flags_map {
unsigned int in_mask;
unsigned int out_mask;
};
static const struct flags_map shutdown_map[] = {
{ SHUTDOWN_META_IO_ERROR, XFS_HEALTH_SHUTDOWN_META_IO_ERROR },
{ SHUTDOWN_LOG_IO_ERROR, XFS_HEALTH_SHUTDOWN_LOG_IO_ERROR },
{ SHUTDOWN_FORCE_UMOUNT, XFS_HEALTH_SHUTDOWN_FORCE_UMOUNT },
{ SHUTDOWN_CORRUPT_INCORE, XFS_HEALTH_SHUTDOWN_CORRUPT_INCORE },
{ SHUTDOWN_CORRUPT_ONDISK, XFS_HEALTH_SHUTDOWN_CORRUPT_ONDISK },
{ SHUTDOWN_DEVICE_REMOVED, XFS_HEALTH_SHUTDOWN_DEVICE_REMOVED },
};
static inline unsigned int
__map_flags(
const struct flags_map *map,
size_t array_len,
unsigned int flags)
{
const struct flags_map *m;
unsigned int ret = 0;
for (m = map; m < map + array_len; m++) {
if (flags & m->in_mask)
ret |= m->out_mask;
}
return ret;
}
#define map_flags(map, flags) __map_flags((map), ARRAY_SIZE(map), (flags))
static inline unsigned int shutdown_mask(unsigned int in)
{
return map_flags(shutdown_map, in);
}
static const unsigned int domain_map[] = {
[XFS_HEALTHMON_MOUNT] = XFS_HEALTH_MONITOR_DOMAIN_MOUNT,
[XFS_HEALTHMON_FS] = XFS_HEALTH_MONITOR_DOMAIN_FS,
@ -517,6 +583,7 @@ static const unsigned int type_map[] = {
[XFS_HEALTHMON_CORRUPT] = XFS_HEALTH_MONITOR_TYPE_CORRUPT,
[XFS_HEALTHMON_HEALTHY] = XFS_HEALTH_MONITOR_TYPE_HEALTHY,
[XFS_HEALTHMON_UNMOUNT] = XFS_HEALTH_MONITOR_TYPE_UNMOUNT,
[XFS_HEALTHMON_SHUTDOWN] = XFS_HEALTH_MONITOR_TYPE_SHUTDOWN,
};
/* Render event as a V0 structure */
@ -545,6 +612,9 @@ xfs_healthmon_format_v0(
case XFS_HEALTHMON_LOST:
hme.e.lost.count = event->lostcount;
break;
case XFS_HEALTHMON_SHUTDOWN:
hme.e.shutdown.reasons = shutdown_mask(event->flags);
break;
default:
break;
}

View File

@ -72,6 +72,9 @@ enum xfs_healthmon_type {
XFS_HEALTHMON_LOST, /* message lost */
XFS_HEALTHMON_UNMOUNT, /* filesystem is unmounting */
/* filesystem shutdown */
XFS_HEALTHMON_SHUTDOWN,
/* metadata health events */
XFS_HEALTHMON_SICK, /* runtime corruption observed */
XFS_HEALTHMON_CORRUPT, /* fsck reported corruption */
@ -119,6 +122,10 @@ struct xfs_healthmon_event {
uint32_t gen;
xfs_ino_t ino;
};
/* shutdown */
struct {
unsigned int flags;
};
};
};
@ -132,6 +139,8 @@ void xfs_healthmon_report_inode(struct xfs_inode *ip,
enum xfs_healthmon_type type, unsigned int old_mask,
unsigned int new_mask);
void xfs_healthmon_report_shutdown(struct xfs_mount *mp, uint32_t flags);
long xfs_ioc_health_monitor(struct file *file,
struct xfs_health_monitor __user *arg);

View File

@ -6012,7 +6012,8 @@ DEFINE_HEALTHMON_EVENT(xfs_healthmon_report_unmount);
{ XFS_HEALTHMON_UNMOUNT, "unmount" }, \
{ XFS_HEALTHMON_SICK, "sick" }, \
{ XFS_HEALTHMON_CORRUPT, "corrupt" }, \
{ XFS_HEALTHMON_HEALTHY, "healthy" }
{ XFS_HEALTHMON_HEALTHY, "healthy" }, \
{ XFS_HEALTHMON_SHUTDOWN, "shutdown" }
#define XFS_HEALTHMON_DOMAIN_STRINGS \
{ XFS_HEALTHMON_MOUNT, "mount" }, \
@ -6022,6 +6023,7 @@ DEFINE_HEALTHMON_EVENT(xfs_healthmon_report_unmount);
{ XFS_HEALTHMON_RTGROUP, "rtgroup" }
TRACE_DEFINE_ENUM(XFS_HEALTHMON_LOST);
TRACE_DEFINE_ENUM(XFS_HEALTHMON_SHUTDOWN);
TRACE_DEFINE_ENUM(XFS_HEALTHMON_UNMOUNT);
TRACE_DEFINE_ENUM(XFS_HEALTHMON_SICK);
TRACE_DEFINE_ENUM(XFS_HEALTHMON_CORRUPT);
@ -6063,6 +6065,9 @@ DECLARE_EVENT_CLASS(xfs_healthmon_event_class,
switch (__entry->domain) {
case XFS_HEALTHMON_MOUNT:
switch (__entry->type) {
case XFS_HEALTHMON_SHUTDOWN:
__entry->mask = event->flags;
break;
case XFS_HEALTHMON_LOST:
__entry->lostcount = event->lostcount;
break;
@ -6207,6 +6212,22 @@ TRACE_EVENT(xfs_healthmon_report_inode,
__entry->gen)
);
TRACE_EVENT(xfs_healthmon_report_shutdown,
TP_PROTO(const struct xfs_healthmon *hm, uint32_t shutdown_flags),
TP_ARGS(hm, shutdown_flags),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(uint32_t, shutdown_flags)
),
TP_fast_assign(
__entry->dev = hm->dev;
__entry->shutdown_flags = shutdown_flags;
),
TP_printk("dev %d:%d shutdown_flags %s",
MAJOR(__entry->dev), MINOR(__entry->dev),
__print_flags(__entry->shutdown_flags, "|", XFS_SHUTDOWN_STRINGS))
);
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH