diff mbox series

[4/7] xfs_io: monitor filesystem health events

Message ID 170873836612.1902540.13429166309518341696.stgit@frogsfrogsfrogs (mailing list archive)
State New
Headers show
Series [1/7] xfs: use thread_with_file to create a monitoring file | expand

Commit Message

Darrick J. Wong Feb. 24, 2024, 1:35 a.m. UTC
From: Darrick J. Wong <djwong@kernel.org>

Create a subcommand to monitor for health events generated by the kernel.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
 io/Makefile       |    1 
 io/healthmon.c    |  172 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c         |    1 
 io/io.h           |    1 
 man/man8/xfs_io.8 |   22 +++++++
 5 files changed, 197 insertions(+)
 create mode 100644 io/healthmon.c
diff mbox series

Patch

diff --git a/io/Makefile b/io/Makefile
index 787027fe10ed..b1f9cebd63b0 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -24,6 +24,7 @@  CFILES = \
 	fsuuid.c \
 	fsync.c \
 	getrusage.c \
+	healthmon.c \
 	imap.c \
 	inject.c \
 	label.c \
diff --git a/io/healthmon.c b/io/healthmon.c
new file mode 100644
index 000000000000..7db8c52c96c0
--- /dev/null
+++ b/io/healthmon.c
@@ -0,0 +1,172 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/paths.h"
+#include "command.h"
+#include "init.h"
+#include "io.h"
+
+static void
+healthmon_help(void)
+{
+	printf(_(
+"Monitor filesystem health events"
+"\n"
+"-c             Replace the open file with the monitor file.\n"
+"-d delay_ms    Sleep this many milliseconds between reads.\n"
+"-p             Only probe for the existence of the ioctl.\n"
+"-v             Request all events.\n"
+"\n"));
+}
+
+static inline int
+monitor_sleep(
+	int			delay_ms)
+{
+	struct timespec		ts;
+
+	if (!delay_ms)
+		return 0;
+
+	ts.tv_sec = delay_ms / 1000;
+	ts.tv_nsec = (delay_ms % 1000) * 1000000;
+
+	return nanosleep(&ts, NULL);
+}
+
+#define BUFSIZE			(4096)
+
+static int
+monitor(
+	bool			consume,
+	int			delay_ms,
+	bool			verbose,
+	bool			only_probe)
+{
+	struct xfs_health_monitor	hmo = {
+		.format		= XFS_HEALTH_MONITOR_FMT_JSON,
+	};
+	char			*buf;
+	ssize_t			bytes_read;
+	int			mon_fd;
+	int			ret = 1;
+
+	if (verbose)
+		hmo.flags |= XFS_HEALTH_MONITOR_ALL;
+
+	mon_fd = ioctl(file->fd, XFS_IOC_HEALTH_MONITOR, &hmo);
+	if (mon_fd < 0) {
+		perror("XFS_IOC_HEALTH_MONITOR");
+		return 1;
+	}
+
+	if (only_probe) {
+		ret = 0;
+		goto out_mon;
+	}
+
+	buf = malloc(BUFSIZE);
+	if (!buf) {
+		perror("malloc");
+		goto out_mon;
+	}
+
+	if (consume) {
+		close(file->fd);
+		file->fd = mon_fd;
+	}
+
+	monitor_sleep(delay_ms);
+	while ((bytes_read = read(mon_fd, buf, BUFSIZE)) > 0) {
+		char		*write_ptr = buf;
+		ssize_t		bytes_written;
+		size_t		to_write = bytes_read;
+
+		while ((bytes_written = write(STDOUT_FILENO, write_ptr, to_write)) > 0) {
+			write_ptr += bytes_written;
+			to_write -= bytes_written;
+		}
+		if (bytes_written < 0) {
+			perror("healthdump");
+			goto out_buf;
+		}
+
+		monitor_sleep(delay_ms);
+	}
+	if (bytes_read < 0) {
+		perror("healthmon");
+		goto out_buf;
+	}
+
+	ret = 0;
+
+out_buf:
+	free(buf);
+out_mon:
+	close(mon_fd);
+	return ret;
+}
+
+static int
+healthmon_f(
+	int			argc,
+	char			**argv)
+{
+	bool			consume = false;
+	bool			verbose = false;
+	bool			only_probe = false;
+	int			delay_ms = 0;
+	int			c;
+
+	while ((c = getopt(argc, argv, "cd:pv")) != EOF) {
+		switch (c) {
+		case 'c':
+			consume = true;
+			break;
+		case 'd':
+			errno = 0;
+			delay_ms = atoi(optarg);
+			if (delay_ms < 0 || errno) {
+				printf("%s: delay must be positive msecs\n",
+						optarg);
+				exitcode = 1;
+				return 0;
+			}
+			break;
+		case 'p':
+			only_probe = true;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			exitcode = 1;
+			healthmon_help();
+			return 0;
+		}
+	}
+
+	return monitor(consume, delay_ms, verbose, only_probe);
+}
+
+static struct cmdinfo healthmon_cmd = {
+	.name		= "healthmon",
+	.cfunc		= healthmon_f,
+	.argmin		= 0,
+	.argmax		= -1,
+	.flags		= CMD_FLAG_ONESHOT | CMD_NOMAP_OK,
+	.args		= "[-c] [-d delay_ms] [-v]",
+	.help		= healthmon_help,
+};
+
+void
+healthmon_init(void)
+{
+	healthmon_cmd.oneline = _("monitor filesystem health events");
+
+	add_command(&healthmon_cmd);
+}
diff --git a/io/init.c b/io/init.c
index 452f4cfc898c..ef32e74bc744 100644
--- a/io/init.c
+++ b/io/init.c
@@ -91,6 +91,7 @@  init_commands(void)
 	utimes_init();
 	crc32cselftest_init();
 	exchrange_init();
+	healthmon_init();
 }
 
 /*
diff --git a/io/io.h b/io/io.h
index 06a8ae1db496..b8bed3b66171 100644
--- a/io/io.h
+++ b/io/io.h
@@ -192,3 +192,4 @@  extern void		bulkstat_init(void);
 extern void		exchrange_init(void);
 extern void		aginfo_init(void);
 extern void		fsrefcounts_init(void);
+extern void		healthmon_init(void);
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 93a4f0790d8e..9f00d26a0b49 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -1407,6 +1407,28 @@  flag.
 .RE
 .PD
 
+.TP
+.BI "healthmon [ \-c ] [ \-d " delay_ms " ] [ \-p ] [ \-v ]"
+Watch for filesystem health events and write them to the console.
+.RE
+.RS 1.0i
+.PD 0
+.TP
+.BI \-c
+Close the open file and replace it with the monitor file.
+.TP
+.BI "\-d " delay_ms
+Sleep for this long between read attempts.
+.TP
+.B \-p
+Probe for the existence of the functionality by opening the monitoring fd and
+closing it immediately.
+.TP
+.BI \-v
+Request all health events, even if nothing changed.
+.PD
+.RE
+
 .TP
 .BI "inject [ " tag " ]"
 Inject errors into a filesystem to observe filesystem behavior at