]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: convey filesystem shutdown events to the health monitor
authorDarrick J. Wong <djwong@kernel.org>
Wed, 21 Jan 2026 02:06:48 +0000 (18:06 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 21 Jan 2026 02:06:48 +0000 (18:06 -0800)
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>
fs/xfs/libxfs/xfs_fs.h
fs/xfs/xfs_fsops.c
fs/xfs/xfs_healthmon.c
fs/xfs/xfs_healthmon.h
fs/xfs/xfs_trace.h

index 04e1dcf61257d0f89d51906106074d1b460a3118..c8f7011a7ef8efca3dda8917bfbed0ac4915053a 100644 (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 */
index b7c21f68edc78d9954da688fb472601dc53455a3..368173bf8a40911ea29078d517c76aaab17b53c8 100644 (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);
 }
 
 /*
index 0039a79822e86ae37dc5c441a88ca93d919f2215..97f764e79541529e7c4a50be2f417b56d5d83f58 100644 (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;
                }
index 121e5942639524d2be34637e406d879fd809dcd6..1f68b5d65a8edcb1429341a98f1a46f786849e73 100644 (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);
 
index debe9846418a046a22cface699ab51f3ec9da18d..ec99a6d3dd318c186d5b77b4b23fb266dc433de3 100644 (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