aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob N <[email protected]>2024-04-16 06:52:20 +1000
committerGitHub <[email protected]>2024-04-15 13:52:20 -0700
commit4725e543be32f74d3a0a46ce3bb5c8e89280b471 (patch)
treef78a9a12284cf26a76ba7b564763c7e9a0ddbba3
parentf22b110f60d83f62b75d20fabb0968ab74324778 (diff)
zinject: "no-op" error injection
When injected, this causes the matching IO to appear to succeed, but the actual work is never submitted to the physical device. This can be used to simulate a write-back cache servicing a write, but the backing device has failed and the cache cannot complete the operation in the background. Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Rob Norris <[email protected]> Closes #16085
-rw-r--r--cmd/zinject/zinject.c7
-rw-r--r--man/man8/zinject.86
-rw-r--r--module/zfs/zio.c10
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zinject/zinject_args.ksh2
4 files changed, 19 insertions, 6 deletions
diff --git a/cmd/zinject/zinject.c b/cmd/zinject/zinject.c
index a1afa4a63..e9141fb4b 100644
--- a/cmd/zinject/zinject.c
+++ b/cmd/zinject/zinject.c
@@ -221,6 +221,7 @@ static const struct errstr errstrtable[] = {
{ ENXIO, "nxio" },
{ ECHILD, "dtl" },
{ EILSEQ, "corrupt" },
+ { ENOSYS, "noop" },
{ 0, NULL },
};
@@ -269,8 +270,8 @@ usage(void)
"\t\tInject a fault into a particular device or the device's\n"
"\t\tlabel. Label injection can either be 'nvlist', 'uber',\n "
"\t\t'pad1', or 'pad2'.\n"
- "\t\t'errno' can be 'nxio' (the default), 'io', 'dtl', or\n"
- "\t\t'corrupt' (bit flip).\n"
+ "\t\t'errno' can be 'nxio' (the default), 'io', 'dtl',\n"
+ "\t\t'corrupt' (bit flip), or 'noop' (successfully do nothing).\n"
"\t\t'frequency' is a value between 0.0001 and 100.0 that limits\n"
"\t\tdevice error injection to a percentage of the IOs.\n"
"\n"
@@ -889,7 +890,7 @@ main(int argc, char **argv)
if (error < 0) {
(void) fprintf(stderr, "invalid error type "
"'%s': must be one of: io decompress "
- "decrypt nxio dtl corrupt\n",
+ "decrypt nxio dtl corrupt noop\n",
optarg);
usage();
libzfs_fini(g_zfs);
diff --git a/man/man8/zinject.8 b/man/man8/zinject.8
index 817dcb7fe..f67b5e378 100644
--- a/man/man8/zinject.8
+++ b/man/man8/zinject.8
@@ -211,9 +211,11 @@ to flip a bit in the data after a read,
.It Sy dtl
for an ECHILD error,
.It Sy io
-for an EIO error where reopening the device will succeed, or
+for an EIO error where reopening the device will succeed,
.It Sy nxio
-for an ENXIO error where reopening the device will fail.
+for an ENXIO error where reopening the device will fail, or
+.It Sy noop
+to drop the IO without executing it, and return success.
.El
.Pp
For EIO and ENXIO, the "failed" reads or writes still occur.
diff --git a/module/zfs/zio.c b/module/zfs/zio.c
index 8d8523038..414e3d4e9 100644
--- a/module/zfs/zio.c
+++ b/module/zfs/zio.c
@@ -4058,6 +4058,16 @@ zio_vdev_io_start(zio_t *zio)
zio->io_type == ZIO_TYPE_WRITE ||
zio->io_type == ZIO_TYPE_TRIM)) {
+ if (zio_handle_device_injection(vd, zio, ENOSYS) != 0) {
+ /*
+ * "no-op" injections return success, but do no actual
+ * work. Just skip the remaining vdev stages.
+ */
+ zio_vdev_io_bypass(zio);
+ zio_interrupt(zio);
+ return (NULL);
+ }
+
if ((zio = vdev_queue_io(zio)) == NULL)
return (NULL);
diff --git a/tests/zfs-tests/tests/functional/cli_root/zinject/zinject_args.ksh b/tests/zfs-tests/tests/functional/cli_root/zinject/zinject_args.ksh
index f8a8ffbb7..dd9ef9ddd 100755
--- a/tests/zfs-tests/tests/functional/cli_root/zinject/zinject_args.ksh
+++ b/tests/zfs-tests/tests/functional/cli_root/zinject/zinject_args.ksh
@@ -47,7 +47,7 @@ function cleanup
function test_device_fault
{
- typeset -a errno=("io" "decompress" "decrypt" "nxio" "dtl" "corrupt")
+ typeset -a errno=("io" "decompress" "decrypt" "nxio" "dtl" "corrupt" "noop")
for e in ${errno[@]}; do
log_must eval \
"zinject -d $DISK1 -e $e -T read -f 0.001 $TESTPOOL"