]> git-server-git.apps.pok.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs_io: add a media verify command
authorDarrick J. Wong <djwong@kernel.org>
Sun, 22 Feb 2026 22:41:13 +0000 (14:41 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Thu, 9 Apr 2026 22:30:17 +0000 (15:30 -0700)
Add a subcommand to invoke the media verification ioctl to make sure
that we can actually check the storage underneath an xfs filesystem.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
io/Makefile
io/init.c
io/io.h
io/verify_media.c [new file with mode: 0644]
man/man8/xfs_io.8

index 8e3783353a52b5393653ac121bae7a0bf777f0a2..79d5e172b8f31f1a45afb668c8d09cc559c0877d 100644 (file)
@@ -51,7 +51,8 @@ CFILES = \
        sync.c \
        sync_file_range.c \
        truncate.c \
-       utimes.c
+       utimes.c \
+       verify_media.c
 
 LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD) $(LIBUUID)
 LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
index cb5573f45ccfbcf1c58e5b22f207960ccdca6809..f2a551ef5592009c5480bc5762b0e794de2c98df 100644 (file)
--- a/io/init.c
+++ b/io/init.c
@@ -93,6 +93,7 @@ init_commands(void)
        exchangerange_init();
        fsprops_init();
        healthmon_init();
+       verifymedia_init();
 }
 
 /*
diff --git a/io/io.h b/io/io.h
index 2f5262bce6acbb5cb6b839cd1fd291dcbeb114d8..0f12b3cfed5e762169aa746089b622eb9e41aef0 100644 (file)
--- a/io/io.h
+++ b/io/io.h
@@ -163,3 +163,4 @@ void                        exchangerange_init(void);
 void                   fsprops_init(void);
 void                   aginfo_init(void);
 void                   healthmon_init(void);
+void                   verifymedia_init(void);
diff --git a/io/verify_media.c b/io/verify_media.c
new file mode 100644 (file)
index 0000000..73eb40e
--- /dev/null
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "io.h"
+
+static void
+verifymedia_help(void)
+{
+       printf(_(
+"\n"
+" Verify the media of the devices backing the filesystem.\n"
+"\n"
+" -d -- Verify the data device (default).\n"
+" -l -- Verify the log device.\n"
+" -r -- Verify the realtime device.\n"
+" -R -- Report media errors to fsnotify.\n"
+" -s -- Sleep this many usecs between IOs.\n"
+"\n"
+" start is the byte offset of the start of the range to verify.  If the start\n"
+" is specified, the end may (optionally) be specified as well."
+"\n"
+" end is the byte offset of the end of the range to verify.\n"
+"\n"
+" If neither start nor end are specified, the media verification will\n"
+" check the entire device."
+"\n"));
+}
+
+static int
+verifymedia_f(
+       int                     argc,
+       char                    **argv)
+{
+       xfs_daddr_t             orig_start_daddr = 0;
+       struct xfs_verify_media me = {
+               .me_start_daddr = orig_start_daddr,
+               .me_end_daddr   = ~0ULL,
+               .me_dev         = XFS_DEV_DATA,
+       };
+       struct timeval          t1, t2;
+       long long               l;
+       size_t                  fsblocksize, fssectsize;
+       const char              *verifydev = _("datadev");
+       int                     c, ret;
+
+       init_cvtnum(&fsblocksize, &fssectsize);
+
+       while ((c = getopt(argc, argv, "b:dlrRs:")) != EOF) {
+               switch (c) {
+               case 'd':
+                       me.me_dev = XFS_DEV_DATA;
+                       verifydev = _("datadev");
+                       break;
+               case 'l':
+                       me.me_dev = XFS_DEV_LOG;
+                       verifydev = _("logdev");
+                       break;
+               case 'r':
+                       me.me_dev = XFS_DEV_RT;
+                       verifydev = _("rtdev");
+                       break;
+               case 'b':
+                       l = cvtnum(fsblocksize, fssectsize, optarg);
+                       if (l < 0 || l > UINT_MAX) {
+                               printf("non-numeric maxio argument -- %s\n",
+                                               optarg);
+                               exitcode = 1;
+                               return 0;
+                       }
+                       me.me_max_io_size = l;
+                       break;
+               case 'R':
+                       me.me_flags |= XFS_VERIFY_MEDIA_REPORT;
+                       break;
+               case 's':
+                       l = atoi(optarg);
+                       if (l < 0) {
+                               printf("non-numeric rest_us argument -- %s\n",
+                                               optarg);
+                               exitcode = 1;
+                               return 0;
+                       }
+                       me.me_rest_us = l;
+                       break;
+               default:
+                       verifymedia_help();
+                       exitcode = 1;
+                       return 0;
+               }
+       }
+
+       /* Range start (optional) */
+       if (optind < argc) {
+               l = cvtnum(fsblocksize, fssectsize, argv[optind]);
+               if (l < 0) {
+                       printf("non-numeric start argument -- %s\n",
+                                       argv[optind]);
+                       exitcode = 1;
+                       return 0;
+               }
+
+               orig_start_daddr = l / 512;
+               me.me_start_daddr = orig_start_daddr;
+               optind++;
+       }
+
+       /* Range end (optional if range start was specified) */
+       if (optind < argc) {
+               l = cvtnum(fsblocksize, fssectsize, argv[optind]);
+               if (l < 0) {
+                       printf("non-numeric end argument -- %s\n",
+                                       argv[optind]);
+                       exitcode = 1;
+                       return 0;
+               }
+
+               me.me_end_daddr = ((l + 511) / 512);
+               optind++;
+       }
+
+       if (optind < argc) {
+               printf("too many arguments -- %s\n", argv[optind]);
+               exitcode = 1;
+               return 0;
+       }
+
+       gettimeofday(&t1, NULL);
+       ret = ioctl(file->fd, XFS_IOC_VERIFY_MEDIA, &me);
+       gettimeofday(&t2, NULL);
+       t2 = tsub(t2, t1);
+       if (ret < 0) {
+               fprintf(stderr,
+ "%s: ioctl(XFS_IOC_VERIFY_MEDIA) [\"%s\"]: %s\n",
+                               progname, file->name, strerror(errno));
+               exitcode = 1;
+               return 0;
+       }
+
+       if (me.me_ioerror) {
+               fprintf(stderr,
+ "%s: verify error at offset %llu length %llu: %s\n",
+                               verifydev,
+                               BBTOB(me.me_start_daddr),
+                               BBTOB(me.me_end_daddr - me.me_start_daddr),
+                               strerror(me.me_ioerror));
+               exitcode = 1;
+       } else {
+               unsigned long long      total;
+
+               if (me.me_end_daddr > orig_start_daddr)
+                       total = BBTOB(me.me_end_daddr - orig_start_daddr);
+               else
+                       total = 0;
+               report_io_times("verified", &t2, BBTOB(orig_start_daddr),
+                               BBTOB(me.me_start_daddr - orig_start_daddr),
+                               total, 1, false);
+       }
+
+       return 0;
+}
+
+static struct cmdinfo verifymedia_cmd = {
+       .name           = "verifymedia",
+       .cfunc          = verifymedia_f,
+       .argmin         = 0,
+       .argmax         = -1,
+       .flags          = CMD_FLAG_ONESHOT | CMD_NOMAP_OK,
+       .args           = "[-lr] [start [end]]",
+       .help           = verifymedia_help,
+};
+
+void
+verifymedia_init(void)
+{
+       add_command(&verifymedia_cmd);
+}
index f7f2956a54a7aa99a8b80b67f3467e9fb671caff..2090cd4c0b2641883af55eecb949fb53854090dc 100644 (file)
@@ -1389,6 +1389,48 @@ specific points under adverse conditions. Without the
 argument, displays the list of error tags available.
 Only available in expert mode and requires privileges.
 
+.TP
+.BI "verifymedia [ \-bdlrsR ] [ " start " [ " end " ]]"
+Check for media errors on the storage devices backing XFS.
+The
+.I start
+and
+.I end
+parameters are the range of physical storage to verify, in bytes.
+The
+.I start
+parameter is inclusive.
+The
+.I end
+parameter is exclusive.
+If neither
+.IR start " nor " end
+are specified, the entire device will be verified.
+.RE
+.RS 1.0i
+.PD 0
+.TP
+.B \-b
+Don't issue any IOs larger than this size.
+.TP
+.B \-d
+Verify the data device.
+This is the default.
+.TP
+.B \-l
+Verify the log device instead of the data device.
+.TP
+.B \-r
+Verify the realtime device instead of the data device.
+.TP
+.B \-R
+Report media errors to fsnotify.
+.TP
+.B \-s
+Sleep this many microseconds between IO requests.
+.PD
+.RE
+
 .TP
 .BI "rginfo [ \-r " rgno " ]"
 Show information about or update the state of realtime allocation groups.