aboutsummaryrefslogtreecommitdiff
path: root/cmd/zinject/zinject.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/zinject/zinject.c')
-rw-r--r--cmd/zinject/zinject.c215
1 files changed, 181 insertions, 34 deletions
diff --git a/cmd/zinject/zinject.c b/cmd/zinject/zinject.c
index 74c54baebf6f..ed60cce3dd16 100644
--- a/cmd/zinject/zinject.c
+++ b/cmd/zinject/zinject.c
@@ -6,7 +6,7 @@
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
+ * or https://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
@@ -22,6 +22,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
* Copyright (c) 2017, Intel Corporation.
+ * Copyright (c) 2023-2024, Klara Inc.
*/
/*
@@ -208,6 +209,38 @@ type_to_name(uint64_t type)
}
}
+struct errstr {
+ int err;
+ const char *str;
+};
+static const struct errstr errstrtable[] = {
+ { EIO, "io" },
+ { ECKSUM, "checksum" },
+ { EINVAL, "decompress" },
+ { EACCES, "decrypt" },
+ { ENXIO, "nxio" },
+ { ECHILD, "dtl" },
+ { EILSEQ, "corrupt" },
+ { ENOSYS, "noop" },
+ { 0, NULL },
+};
+
+static int
+str_to_err(const char *str)
+{
+ for (int i = 0; errstrtable[i].str != NULL; i++)
+ if (strcasecmp(errstrtable[i].str, str) == 0)
+ return (errstrtable[i].err);
+ return (-1);
+}
+static const char *
+err_to_str(int err)
+{
+ for (int i = 0; errstrtable[i].str != NULL; i++)
+ if (errstrtable[i].err == err)
+ return (errstrtable[i].str);
+ return ("[unknown]");
+}
/*
* Print usage message.
@@ -233,12 +266,12 @@ usage(void)
"\t\tspa_vdev_exit() will trigger a panic.\n"
"\n"
"\tzinject -d device [-e errno] [-L <nvlist|uber|pad1|pad2>] [-F]\n"
- "\t\t[-T <read|write|free|claim|all>] [-f frequency] pool\n\n"
+ "\t\t[-T <read|write|free|claim|flush|all>] [-f frequency] pool\n\n"
"\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"
@@ -277,6 +310,11 @@ usage(void)
"\t\tcreate 3 lanes on the device; one lane with a latency\n"
"\t\tof 10 ms and two lanes with a 25 ms latency.\n"
"\n"
+ "\tzinject -P import|export -s <seconds> pool\n"
+ "\t\tAdd an artificial delay to a future pool import or export,\n"
+ "\t\tsuch that the operation takes a minimum of supplied seconds\n"
+ "\t\tto complete.\n"
+ "\n"
"\tzinject -I [-s <seconds> | -g <txgs>] pool\n"
"\t\tCause the pool to stop writing blocks yet not\n"
"\t\treport errors for a duration. Simulates buggy hardware\n"
@@ -359,8 +397,10 @@ print_data_handler(int id, const char *pool, zinject_record_t *record,
{
int *count = data;
- if (record->zi_guid != 0 || record->zi_func[0] != '\0')
+ if (record->zi_guid != 0 || record->zi_func[0] != '\0' ||
+ record->zi_duration != 0) {
return (0);
+ }
if (*count == 0) {
(void) printf("%3s %-15s %-6s %-6s %-8s %3s %-4s "
@@ -392,6 +432,10 @@ static int
print_device_handler(int id, const char *pool, zinject_record_t *record,
void *data)
{
+ static const char *iotypestr[] = {
+ "null", "read", "write", "free", "claim", "flush", "trim", "all",
+ };
+
int *count = data;
if (record->zi_guid == 0 || record->zi_func[0] != '\0')
@@ -401,14 +445,21 @@ print_device_handler(int id, const char *pool, zinject_record_t *record,
return (0);
if (*count == 0) {
- (void) printf("%3s %-15s %s\n", "ID", "POOL", "GUID");
- (void) printf("--- --------------- ----------------\n");
+ (void) printf("%3s %-15s %-16s %-5s %-10s %-9s\n",
+ "ID", "POOL", "GUID", "TYPE", "ERROR", "FREQ");
+ (void) printf(
+ "--- --------------- ---------------- "
+ "----- ---------- ---------\n");
}
*count += 1;
- (void) printf("%3d %-15s %llx\n", id, pool,
- (u_longlong_t)record->zi_guid);
+ double freq = record->zi_freq == 0 ? 100.0f :
+ (((double)record->zi_freq) / ZI_PERCENTAGE_MAX) * 100.0f;
+
+ (void) printf("%3d %-15s %llx %-5s %-10s %8.4f%%\n", id, pool,
+ (u_longlong_t)record->zi_guid, iotypestr[record->zi_iotype],
+ err_to_str(record->zi_error), freq);
return (0);
}
@@ -463,6 +514,33 @@ print_panic_handler(int id, const char *pool, zinject_record_t *record,
return (0);
}
+static int
+print_pool_delay_handler(int id, const char *pool, zinject_record_t *record,
+ void *data)
+{
+ int *count = data;
+
+ if (record->zi_cmd != ZINJECT_DELAY_IMPORT &&
+ record->zi_cmd != ZINJECT_DELAY_EXPORT) {
+ return (0);
+ }
+
+ if (*count == 0) {
+ (void) printf("%3s %-19s %-11s %s\n",
+ "ID", "POOL", "DELAY (sec)", "COMMAND");
+ (void) printf("--- ------------------- -----------"
+ " -------\n");
+ }
+
+ *count += 1;
+
+ (void) printf("%3d %-19s %-11llu %s\n",
+ id, pool, (u_longlong_t)record->zi_duration,
+ record->zi_cmd == ZINJECT_DELAY_IMPORT ? "import": "export");
+
+ return (0);
+}
+
/*
* Print all registered error handlers. Returns the number of handlers
* registered.
@@ -493,6 +571,13 @@ print_all_handlers(void)
count = 0;
}
+ (void) iter_handlers(print_pool_delay_handler, &count);
+ if (count > 0) {
+ total += count;
+ (void) printf("\n");
+ count = 0;
+ }
+
(void) iter_handlers(print_panic_handler, &count);
return (count + total);
@@ -565,9 +650,27 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
zc.zc_guid = flags;
if (zfs_ioctl(g_zfs, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
- (void) fprintf(stderr, "failed to add handler: %s\n",
- errno == EDOM ? "block level exceeds max level of object" :
- strerror(errno));
+ const char *errmsg = strerror(errno);
+
+ switch (errno) {
+ case EDOM:
+ errmsg = "block level exceeds max level of object";
+ break;
+ case EEXIST:
+ if (record->zi_cmd == ZINJECT_DELAY_IMPORT)
+ errmsg = "pool already imported";
+ if (record->zi_cmd == ZINJECT_DELAY_EXPORT)
+ errmsg = "a handler already exists";
+ break;
+ case ENOENT:
+ /* import delay injector running on older zfs module */
+ if (record->zi_cmd == ZINJECT_DELAY_IMPORT)
+ errmsg = "import delay injector not supported";
+ break;
+ default:
+ break;
+ }
+ (void) fprintf(stderr, "failed to add handler: %s\n", errmsg);
return (1);
}
@@ -592,6 +695,9 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
} else if (record->zi_duration < 0) {
(void) printf(" txgs: %lld \n",
(u_longlong_t)-record->zi_duration);
+ } else if (record->zi_timer > 0) {
+ (void) printf(" timer: %lld ms\n",
+ (u_longlong_t)NSEC2MSEC(record->zi_timer));
} else {
(void) printf("objset: %llu\n",
(u_longlong_t)record->zi_objset);
@@ -790,7 +896,7 @@ main(int argc, char **argv)
}
while ((c = getopt(argc, argv,
- ":aA:b:C:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:")) != -1) {
+ ":aA:b:C:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:")) != -1) {
switch (c) {
case 'a':
flags |= ZINJECT_FLUSH_ARC;
@@ -842,24 +948,12 @@ main(int argc, char **argv)
}
break;
case 'e':
- if (strcasecmp(optarg, "io") == 0) {
- error = EIO;
- } else if (strcasecmp(optarg, "checksum") == 0) {
- error = ECKSUM;
- } else if (strcasecmp(optarg, "decompress") == 0) {
- error = EINVAL;
- } else if (strcasecmp(optarg, "decrypt") == 0) {
- error = EACCES;
- } else if (strcasecmp(optarg, "nxio") == 0) {
- error = ENXIO;
- } else if (strcasecmp(optarg, "dtl") == 0) {
- error = ECHILD;
- } else if (strcasecmp(optarg, "corrupt") == 0) {
- error = EILSEQ;
- } else {
+ error = str_to_err(optarg);
+ if (error < 0) {
(void) fprintf(stderr, "invalid error type "
- "'%s': must be 'io', 'checksum' or "
- "'nxio'\n", optarg);
+ "'%s': must be one of: io decompress "
+ "decrypt nxio dtl corrupt noop\n",
+ optarg);
usage();
libzfs_fini(g_zfs);
return (1);
@@ -920,6 +1014,19 @@ main(int argc, char **argv)
sizeof (record.zi_func));
record.zi_cmd = ZINJECT_PANIC;
break;
+ case 'P':
+ if (strcasecmp(optarg, "import") == 0) {
+ record.zi_cmd = ZINJECT_DELAY_IMPORT;
+ } else if (strcasecmp(optarg, "export") == 0) {
+ record.zi_cmd = ZINJECT_DELAY_EXPORT;
+ } else {
+ (void) fprintf(stderr, "invalid command '%s': "
+ "must be 'import' or 'export'\n", optarg);
+ usage();
+ libzfs_fini(g_zfs);
+ return (1);
+ }
+ break;
case 'q':
quiet = 1;
break;
@@ -947,12 +1054,14 @@ main(int argc, char **argv)
io_type = ZIO_TYPE_FREE;
} else if (strcasecmp(optarg, "claim") == 0) {
io_type = ZIO_TYPE_CLAIM;
+ } else if (strcasecmp(optarg, "flush") == 0) {
+ io_type = ZIO_TYPE_FLUSH;
} else if (strcasecmp(optarg, "all") == 0) {
io_type = ZIO_TYPES;
} else {
(void) fprintf(stderr, "invalid I/O type "
"'%s': must be 'read', 'write', 'free', "
- "'claim' or 'all'\n", optarg);
+ "'claim', 'flush' or 'all'\n", optarg);
usage();
libzfs_fini(g_zfs);
return (1);
@@ -999,7 +1108,7 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- if (record.zi_duration != 0)
+ if (record.zi_duration != 0 && record.zi_cmd == 0)
record.zi_cmd = ZINJECT_IGNORED_WRITES;
if (cancel != NULL) {
@@ -1083,6 +1192,22 @@ main(int argc, char **argv)
libzfs_fini(g_zfs);
return (1);
}
+
+ if (record.zi_nlanes) {
+ switch (io_type) {
+ case ZIO_TYPE_READ:
+ case ZIO_TYPE_WRITE:
+ case ZIO_TYPES:
+ break;
+ default:
+ (void) fprintf(stderr, "I/O type for a delay "
+ "must be 'read' or 'write'\n");
+ usage();
+ libzfs_fini(g_zfs);
+ return (1);
+ }
+ }
+
if (!error)
error = ENXIO;
@@ -1129,8 +1254,8 @@ main(int argc, char **argv)
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
level != 0 || device != NULL || record.zi_freq > 0 ||
dvas != 0) {
- (void) fprintf(stderr, "panic (-p) incompatible with "
- "other options\n");
+ (void) fprintf(stderr, "%s incompatible with other "
+ "options\n", "import|export delay (-P)");
usage();
libzfs_fini(g_zfs);
return (2);
@@ -1148,6 +1273,28 @@ main(int argc, char **argv)
if (argv[1] != NULL)
record.zi_type = atoi(argv[1]);
dataset[0] = '\0';
+ } else if (record.zi_cmd == ZINJECT_DELAY_IMPORT ||
+ record.zi_cmd == ZINJECT_DELAY_EXPORT) {
+ if (raw != NULL || range != NULL || type != TYPE_INVAL ||
+ level != 0 || device != NULL || record.zi_freq > 0 ||
+ dvas != 0) {
+ (void) fprintf(stderr, "%s incompatible with other "
+ "options\n", "import|export delay (-P)");
+ usage();
+ libzfs_fini(g_zfs);
+ return (2);
+ }
+
+ if (argc != 1 || record.zi_duration <= 0) {
+ (void) fprintf(stderr, "import|export delay (-P) "
+ "injection requires a duration (-s) and a single "
+ "pool name\n");
+ usage();
+ libzfs_fini(g_zfs);
+ return (2);
+ }
+
+ (void) strlcpy(pool, argv[0], sizeof (pool));
} else if (record.zi_cmd == ZINJECT_IGNORED_WRITES) {
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
level != 0 || record.zi_freq > 0 || dvas != 0) {