@@ -30,6 +30,9 @@
},
{
"$ref": "#/$events/inode_metadata"
+ },
+ {
+ "$ref": "#/$events/shutdown"
}
],
@@ -205,6 +208,31 @@
}
},
+ "$comment": "Shutdown event data are defined here.",
+ "$shutdown": {
+ "reason": {
+ "description": [
+ "Reason for a filesystem to shut down.",
+ "Options include:",
+ "",
+ " * corrupt_incore: in-memory corruption",
+ " * corrupt_ondisk: on-disk corruption",
+ " * device_removed: device removed",
+ " * force_umount: userspace asked for it",
+ " * log_ioerr: log write IO error",
+ " * meta_ioerr: metadata writeback IO error"
+ ],
+ "enum": [
+ "corrupt_incore",
+ "corrupt_ondisk",
+ "device_removed",
+ "force_umount",
+ "log_ioerr",
+ "meta_ioerr"
+ ]
+ }
+ },
+
"$comment": "Event types are defined here.",
"$events": {
"lost": {
@@ -386,6 +414,40 @@
"generation",
"structures"
]
+ },
+ "shutdown": {
+ "title": "Abnormal Shutdown Event",
+ "description": [
+ "The filesystem went offline due to",
+ "unrecoverable errors."
+ ],
+ "type": "object",
+
+ "properties": {
+ "type": {
+ "const": "shutdown"
+ },
+ "time_ns": {
+ "$ref": "#/$defs/time_ns"
+ },
+ "domain": {
+ "const": "mount"
+ },
+ "reasons": {
+ "type": "array",
+ "items": {
+ "$ref": "#/$shutdown/reason"
+ },
+ "minItems": 1
+ }
+ },
+
+ "required": [
+ "type",
+ "time_ns",
+ "domain",
+ "reasons"
+ ]
}
}
}
@@ -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>
@@ -67,6 +68,7 @@ struct xfs_healthmon {
struct xfs_healthmon_event *last_event;
/* live update hooks */
+ struct xfs_shutdown_hook shook;
struct xfs_health_hook hhook;
/* filesystem mount, or NULL if we've unmounted */
@@ -384,6 +386,43 @@ xfs_healthmon_metadata_hook(
goto out_unlock;
}
+/* Add a shutdown event to the reporting queue. */
+STATIC int
+xfs_healthmon_shutdown_hook(
+ struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ struct xfs_healthmon *hm;
+ struct xfs_healthmon_event *event;
+ int error;
+
+ hm = container_of(nb, struct xfs_healthmon, shook.shutdown_hook.nb);
+
+ mutex_lock(&hm->lock);
+
+ trace_xfs_healthmon_shutdown_hook(hm->mp, action, hm->events,
+ hm->lost_prev_event);
+
+ error = xfs_healthmon_start_live_update(hm);
+ if (error)
+ goto out_unlock;
+
+ event = xfs_healthmon_alloc(hm, XFS_HEALTHMON_SHUTDOWN,
+ XFS_HEALTHMON_MOUNT);
+ if (!event)
+ goto out_unlock;
+
+ event->flags = action;
+ error = xfs_healthmon_push(hm, event);
+ if (error)
+ kfree(event);
+
+out_unlock:
+ mutex_unlock(&hm->lock);
+ return NOTIFY_DONE;
+}
+
/* Render the health update type as a string. */
STATIC const char *
xfs_healthmon_typestring(
@@ -391,6 +430,7 @@ xfs_healthmon_typestring(
{
static const char *type_strings[] = {
[XFS_HEALTHMON_LOST] = "lost",
+ [XFS_HEALTHMON_SHUTDOWN] = "shutdown",
[XFS_HEALTHMON_UNMOUNT] = "unmount",
[XFS_HEALTHMON_SICK] = "sick",
[XFS_HEALTHMON_CORRUPT] = "corrupt",
@@ -606,6 +646,25 @@ xfs_healthmon_format_inode(
event->gen);
}
+/* Render shutdown mask as a string set */
+static int
+xfs_healthmon_format_shutdown(
+ struct seq_buf *outbuf,
+ const struct xfs_healthmon_event *event)
+{
+ static const struct flag_string mask_strings[] = {
+ { SHUTDOWN_META_IO_ERROR, "meta_ioerr" },
+ { SHUTDOWN_LOG_IO_ERROR, "log_ioerr" },
+ { SHUTDOWN_FORCE_UMOUNT, "force_umount" },
+ { SHUTDOWN_CORRUPT_INCORE, "corrupt_incore" },
+ { SHUTDOWN_CORRUPT_ONDISK, "corrupt_ondisk" },
+ { SHUTDOWN_DEVICE_REMOVED, "device_removed" },
+ };
+
+ return xfs_healthmon_format_mask(outbuf, "reasons", mask_strings,
+ event->flags);
+}
+
static inline void
xfs_healthmon_reset_outbuf(
struct xfs_healthmon *hm)
@@ -645,6 +704,9 @@ xfs_healthmon_format(
goto overrun;
switch (event->type) {
+ case XFS_HEALTHMON_SHUTDOWN:
+ ret = xfs_healthmon_format_shutdown(outbuf, event);
+ break;
case XFS_HEALTHMON_LOST:
/* empty */
break;
@@ -928,6 +990,7 @@ xfs_healthmon_detach_hooks(
* through the health monitoring subsystem from xfs_fs_put_super, so
* it is now time to detach the hooks.
*/
+ xfs_shutdown_hook_del(hm->mp, &hm->shook);
xfs_health_hook_del(hm->mp, &hm->hhook);
return;
@@ -948,6 +1011,7 @@ xfs_healthmon_release(
wake_up_all(&hm->wait);
iterate_supers_type(hm->fstyp, xfs_healthmon_detach_hooks, hm);
+ xfs_shutdown_hook_disable();
xfs_health_hook_disable();
mutex_destroy(&hm->lock);
@@ -1027,6 +1091,7 @@ xfs_ioc_health_monitor(
/* Enable hooks to receive events, generally. */
xfs_health_hook_enable();
+ xfs_shutdown_hook_enable();
/* Attach specific event hooks to this monitor. */
xfs_health_hook_setup(&hm->hhook, xfs_healthmon_metadata_hook);
@@ -1034,11 +1099,16 @@ xfs_ioc_health_monitor(
if (ret)
goto out_hooks;
+ xfs_shutdown_hook_setup(&hm->shook, xfs_healthmon_shutdown_hook);
+ ret = xfs_shutdown_hook_add(mp, &hm->shook);
+ if (ret)
+ goto out_healthhook;
+
/* Set up VFS file and file descriptor. */
name = kasprintf(GFP_KERNEL, "XFS (%s): healthmon", mp->m_super->s_id);
if (!name) {
ret = -ENOMEM;
- goto out_healthhook;
+ goto out_shutdownhook;
}
fd = anon_inode_getfd(name, &xfs_healthmon_fops, hm,
@@ -1046,17 +1116,20 @@ xfs_ioc_health_monitor(
kvfree(name);
if (fd < 0) {
ret = fd;
- goto out_healthhook;
+ goto out_shutdownhook;
}
trace_xfs_healthmon_create(mp, hmo.flags, hmo.format);
return fd;
+out_shutdownhook:
+ xfs_shutdown_hook_del(mp, &hm->shook);
out_healthhook:
xfs_health_hook_del(mp, &hm->hhook);
out_hooks:
xfs_health_hook_disable();
+ xfs_shutdown_hook_disable();
mutex_destroy(&hm->lock);
xfs_healthmon_free_events(hm);
kfree(hm);
@@ -9,6 +9,9 @@
enum xfs_healthmon_type {
XFS_HEALTHMON_LOST, /* message lost */
+ /* filesystem shutdown */
+ XFS_HEALTHMON_SHUTDOWN,
+
/* metadata health events */
XFS_HEALTHMON_SICK, /* runtime corruption observed */
XFS_HEALTHMON_CORRUPT, /* fsck reported corruption */
@@ -6173,8 +6173,32 @@ DEFINE_HEALTHMON_EVENT(xfs_healthmon_read_finish);
DEFINE_HEALTHMON_EVENT(xfs_healthmon_release);
DEFINE_HEALTHMON_EVENT(xfs_healthmon_unmount);
+TRACE_EVENT(xfs_healthmon_shutdown_hook,
+ TP_PROTO(const struct xfs_mount *mp, uint32_t shutdown_flags,
+ unsigned int events, bool lost_prev),
+ TP_ARGS(mp, shutdown_flags, events, lost_prev),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(uint32_t, shutdown_flags)
+ __field(unsigned int, events)
+ __field(bool, lost_prev)
+ ),
+ TP_fast_assign(
+ __entry->dev = mp ? mp->m_super->s_dev : 0;
+ __entry->shutdown_flags = shutdown_flags;
+ __entry->events = events;
+ __entry->lost_prev = lost_prev;
+ ),
+ TP_printk("dev %d:%d shutdown_flags %s events %u lost_prev? %d",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __print_flags(__entry->shutdown_flags, "|", XFS_SHUTDOWN_STRINGS),
+ __entry->events,
+ __entry->lost_prev)
+);
+
#define XFS_HEALTHMON_TYPE_STRINGS \
{ XFS_HEALTHMON_LOST, "lost" }, \
+ { XFS_HEALTHMON_SHUTDOWN, "shutdown" }, \
{ XFS_HEALTHMON_UNMOUNT, "unmount" }, \
{ XFS_HEALTHMON_SICK, "sick" }, \
{ XFS_HEALTHMON_CORRUPT, "corrupt" }, \
@@ -6188,6 +6212,7 @@ DEFINE_HEALTHMON_EVENT(xfs_healthmon_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);