From 5d2f52d4a3ba4c4f92fba39c74c704328271e844 Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Wed, 8 Jan 2025 17:14:33 +1100 Subject: [PATCH] zinject: make iotype extendable I'm about to add a new "type", and I need somewhere to put it! Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Signed-off-by: Rob Norris --- cmd/zinject/zinject.c | 60 +++++++++++++++++++++++++---------------- include/sys/zfs_ioctl.h | 18 +++++++++++++ module/zfs/zio_inject.c | 30 ++++++++++++++++----- 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/cmd/zinject/zinject.c b/cmd/zinject/zinject.c index 6c856763c958..d66b0d986f2d 100644 --- a/cmd/zinject/zinject.c +++ b/cmd/zinject/zinject.c @@ -242,6 +242,35 @@ err_to_str(int err) return ("[unknown]"); } +static const char *const iotypestrtable[ZINJECT_IOTYPES] = { + [ZINJECT_IOTYPE_NULL] = "null", + [ZINJECT_IOTYPE_READ] = "read", + [ZINJECT_IOTYPE_WRITE] = "write", + [ZINJECT_IOTYPE_FREE] = "free", + [ZINJECT_IOTYPE_CLAIM] = "claim", + [ZINJECT_IOTYPE_FLUSH] = "flush", + [ZINJECT_IOTYPE_TRIM] = "trim", + [ZINJECT_IOTYPE_ALL] = "all", +}; + +static zinject_iotype_t +str_to_iotype(const char *arg) +{ + for (uint_t iotype = 0; iotype < ZINJECT_IOTYPES; iotype++) + if (iotypestrtable[iotype] != NULL && + strcasecmp(iotypestrtable[iotype], arg) == 0) + return (iotype); + return (ZINJECT_IOTYPES); +} + +static const char * +iotype_to_str(zinject_iotype_t iotype) +{ + if (iotype >= ZINJECT_IOTYPES || iotypestrtable[iotype] == NULL) + return ("[unknown]"); + return (iotypestrtable[iotype]); +} + /* * Print usage message. */ @@ -435,10 +464,6 @@ 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') @@ -465,7 +490,7 @@ print_device_handler(int id, const char *pool, zinject_record_t *record, (void) printf("%3d %-15s %llx %-5s %-10s %8.4f%% " "%6lu %6lu\n", id, pool, (u_longlong_t)record->zi_guid, - iotypestr[record->zi_iotype], err_to_str(record->zi_error), + iotype_to_str(record->zi_iotype), err_to_str(record->zi_error), freq, record->zi_match_count, record->zi_inject_count); return (0); @@ -866,7 +891,7 @@ main(int argc, char **argv) int quiet = 0; int error = 0; int domount = 0; - int io_type = ZIO_TYPES; + int io_type = ZINJECT_IOTYPE_ALL; int action = VDEV_STATE_UNKNOWN; err_type_t type = TYPE_INVAL; err_type_t label = TYPE_INVAL; @@ -1060,19 +1085,8 @@ main(int argc, char **argv) } break; case 'T': - if (strcasecmp(optarg, "read") == 0) { - io_type = ZIO_TYPE_READ; - } else if (strcasecmp(optarg, "write") == 0) { - io_type = ZIO_TYPE_WRITE; - } else if (strcasecmp(optarg, "free") == 0) { - 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 { + io_type = str_to_iotype(optarg); + if (io_type == ZINJECT_IOTYPES) { (void) fprintf(stderr, "invalid I/O type " "'%s': must be 'read', 'write', 'free', " "'claim', 'flush' or 'all'\n", optarg); @@ -1194,7 +1208,7 @@ main(int argc, char **argv) } if (error == EILSEQ && - (record.zi_freq == 0 || io_type != ZIO_TYPE_READ)) { + (record.zi_freq == 0 || io_type != ZINJECT_IOTYPE_READ)) { (void) fprintf(stderr, "device corrupt errors require " "io type read and a frequency value\n"); libzfs_fini(g_zfs); @@ -1209,9 +1223,9 @@ main(int argc, char **argv) if (record.zi_nlanes) { switch (io_type) { - case ZIO_TYPE_READ: - case ZIO_TYPE_WRITE: - case ZIO_TYPES: + case ZINJECT_IOTYPE_READ: + case ZINJECT_IOTYPE_WRITE: + case ZINJECT_IOTYPE_ALL: break; default: (void) fprintf(stderr, "I/O type for a delay " diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h index e61d7644764e..9afe984e1749 100644 --- a/include/sys/zfs_ioctl.h +++ b/include/sys/zfs_ioctl.h @@ -456,6 +456,24 @@ typedef enum zinject_type { ZINJECT_DELAY_EXPORT, } zinject_type_t; +typedef enum zinject_iotype { + /* + * Compatibility: zi_iotype used to be set to ZIO_TYPE_, so make sure + * the corresponding ZINJECT_IOTYPE_ matches. Note that existing here + * does not mean that injections are possible for all these types. + */ + ZINJECT_IOTYPE_NULL = ZIO_TYPE_NULL, + ZINJECT_IOTYPE_READ = ZIO_TYPE_READ, + ZINJECT_IOTYPE_WRITE = ZIO_TYPE_WRITE, + ZINJECT_IOTYPE_FREE = ZIO_TYPE_FREE, + ZINJECT_IOTYPE_CLAIM = ZIO_TYPE_CLAIM, + ZINJECT_IOTYPE_FLUSH = ZIO_TYPE_FLUSH, + ZINJECT_IOTYPE_TRIM = ZIO_TYPE_TRIM, + ZINJECT_IOTYPE_ALL = ZIO_TYPES, + /* Room for future expansion for ZIO_TYPE_* */ + ZINJECT_IOTYPES = 16, +} zinject_iotype_t; + typedef struct zfs_share { uint64_t z_exportdata; uint64_t z_sharedata; diff --git a/module/zfs/zio_inject.c b/module/zfs/zio_inject.c index f972522b6454..6848d46f73e7 100644 --- a/module/zfs/zio_inject.c +++ b/module/zfs/zio_inject.c @@ -376,6 +376,27 @@ zio_inject_bitflip_cb(void *data, size_t len, void *private) return (1); /* stop after first flip */ } +/* Test if this zio matches the iotype from the injection record. */ +static boolean_t +zio_match_iotype(zio_t *zio, uint32_t iotype) +{ + ASSERT3P(zio, !=, NULL); + + /* Unknown iotype, maybe from a newer version of zinject. Reject it. */ + if (iotype >= ZINJECT_IOTYPES) + return (B_FALSE); + + /* Standard IO types, match against ZIO type. */ + if (iotype < ZINJECT_IOTYPE_ALL) + return (iotype == zio->io_type); + + /* Match any standard IO type. */ + if (iotype == ZINJECT_IOTYPE_ALL) + return (B_TRUE); + + return (B_FALSE); +} + static int zio_handle_device_injection_impl(vdev_t *vd, zio_t *zio, int err1, int err2) { @@ -410,9 +431,8 @@ zio_handle_device_injection_impl(vdev_t *vd, zio_t *zio, int err1, int err2) } /* Handle type specific I/O failures */ - if (zio != NULL && - handler->zi_record.zi_iotype != ZIO_TYPES && - handler->zi_record.zi_iotype != zio->io_type) + if (zio != NULL && !zio_match_iotype(zio, + handler->zi_record.zi_iotype)) continue; if (handler->zi_record.zi_error == err1 || @@ -635,10 +655,8 @@ zio_handle_io_delay(zio_t *zio) continue; /* also match on I/O type (e.g., -T read) */ - if (handler->zi_record.zi_iotype != ZIO_TYPES && - handler->zi_record.zi_iotype != zio->io_type) { + if (!zio_match_iotype(zio, handler->zi_record.zi_iotype)) continue; - } /* * Defensive; should never happen as the array allocation