]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs_io: monitor filesystem health events
authorDarrick J. Wong <djwong@kernel.org>
Wed, 7 Aug 2024 22:54:56 +0000 (15:54 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 14 Aug 2024 03:08:27 +0000 (20:08 -0700)
Create a subcommand to monitor for health events generated by the kernel.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
io/Makefile
io/healthmon.c [new file with mode: 0644]
io/init.c
io/io.h
man/man8/xfs_io.8

index c57594b090f70c6aab60497dfc856ee6e3175054..451d2a15b259199071e4dc2bf4e0d291c7ed7440 100644 (file)
@@ -26,6 +26,7 @@ CFILES = \
        fsuuid.c \
        fsync.c \
        getrusage.c \
+       healthmon.c \
        imap.c \
        init.c \
        inject.c \
diff --git a/io/healthmon.c b/io/healthmon.c
new file mode 100644 (file)
index 0000000..78e84b2
--- /dev/null
@@ -0,0 +1,183 @@
+// 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);
+}
+
+static int
+monitor(
+       size_t                  bufsize,
+       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)
+{
+       size_t                  bufsize = 4096;
+       bool                    consume = false;
+       bool                    verbose = false;
+       bool                    only_probe = false;
+       int                     delay_ms = 0;
+       int                     c;
+
+       while ((c = getopt(argc, argv, "b:cd:pv")) != EOF) {
+               switch (c) {
+               case 'b':
+                       errno = 0;
+                       c = atoi(optarg);
+                       if (c < 0 || errno) {
+                               printf("%s: bufsize must be positive\n",
+                                               optarg);
+                               exitcode = 1;
+                               return 0;
+                       }
+                       bufsize = c;
+                       break;
+               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(bufsize, 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);
+}
index 17b772813bc113ab32da1ec089c934327302ccbd..22ebd2f7522a18944174b4207123e263770ffbf2 100644 (file)
--- a/io/init.c
+++ b/io/init.c
@@ -92,6 +92,7 @@ init_commands(void)
        crc32cselftest_init();
        exchangerange_init();
        fsprops_init();
+       healthmon_init();
 }
 
 /*
diff --git a/io/io.h b/io/io.h
index 7ae7cf90ace32372c59725eb7821665f127395ff..267f3ffac3692422b83609ebc0c577aa4d8e5645 100644 (file)
--- a/io/io.h
+++ b/io/io.h
@@ -157,3 +157,4 @@ void                        exchangerange_init(void);
 void                   fsprops_init(void);
 void                   aginfo_init(void);
 void                   fsrefcounts_init(void);
+void                   healthmon_init(void);
index 47222f7c2268a4b9dee9e3740aedfa839f1f9973..dcf97b2a9452c2ac9814cbcfd9b98b633a8bd447 100644 (file)
@@ -1413,6 +1413,31 @@ flag.
 .RE
 .PD
 
+.TP
+.BI "healthmon [ \-c " bufsize " ] [ \-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 "\-b " bufsize
+Use a buffer of this size to read events from the kernel.
+.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