From: Darrick J. Wong <djwong@xxxxxxxxxx> Create a subcommand to monitor for health events generated by the kernel. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- 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 --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@xxxxxxxxxx> + */ +#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