Skip to content

Commit c505d0c

Browse files
committed
zinject: Introduce ready delay fault injection
This adds a pause to the ZIO pipeline in the ready stage for matching I/O (data, dnode, or raw bookmark). Signed-off-by: Robert Evans <[email protected]>
1 parent b2196fb commit c505d0c

File tree

6 files changed

+128
-11
lines changed

6 files changed

+128
-11
lines changed

cmd/zinject/zinject.c

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@
107107
* zinject
108108
* zinject <-a | -u pool>
109109
* zinject -c <id|all>
110+
* zinject -E <delay> [-a] [-m] [-f freq] [-l level] [-r range]
111+
* [-T iotype] [-t type object | -b bookmark pool]
110112
* zinject [-q] <-t type> [-f freq] [-u] [-a] [-m] [-e errno] [-l level]
111113
* [-r range] <object>
112114
* zinject [-f freq] [-a] [-m] [-u] -b objset:object:level:start:end pool
@@ -132,14 +134,18 @@
132134
* The '-f' flag controls the frequency of errors injected, expressed as a
133135
* real number percentage between 0.0001 and 100. The default is 100.
134136
*
135-
* The this form is responsible for actually injecting the handler into the
137+
* The <object> form is responsible for actually injecting the handler into the
136138
* framework. It takes the arguments described above, translates them to the
137139
* internal tuple using libzpool, and then issues an ioctl() to register the
138140
* handler.
139141
*
140-
* The final form can target a specific bookmark, regardless of whether a
142+
* The '-b' option can target a specific bookmark, regardless of whether a
141143
* human-readable interface has been designed. It allows developers to specify
142144
* a particular block by number.
145+
*
146+
* The '-E' option injects pipeline ready stage delays for the given object or
147+
* bookmark. The delay is specified in milliseconds, and it supports I/O type
148+
* and range filters.
143149
*/
144150

145151
#include <errno.h>
@@ -346,6 +352,13 @@ usage(void)
346352
"\t\tsuch that the operation takes a minimum of supplied seconds\n"
347353
"\t\tto complete.\n"
348354
"\n"
355+
"\tzinject -E <delay> [-a] [-m] [-f freq] [-l level] [-r range]\n"
356+
"\t\t[-T iotype] [-t type object | -b bookmark pool]\n"
357+
"\n"
358+
"\t\tInject pipeline ready stage delays for the given object path\n"
359+
"\t\t(data or dnode) or raw bookmark. The delay is specified in\n"
360+
"\t\tmilliseconds.\n"
361+
"\n"
349362
"\tzinject -I [-s <seconds> | -g <txgs>] pool\n"
350363
"\t\tCause the pool to stop writing blocks yet not\n"
351364
"\t\treport errors for a duration. Simulates buggy hardware\n"
@@ -724,12 +737,15 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
724737
if (quiet) {
725738
(void) printf("%llu\n", (u_longlong_t)zc.zc_guid);
726739
} else {
740+
boolean_t show_object = B_FALSE;
741+
boolean_t show_iotype = B_FALSE;
727742
(void) printf("Added handler %llu with the following "
728743
"properties:\n", (u_longlong_t)zc.zc_guid);
729744
(void) printf(" pool: %s\n", pool);
730745
if (record->zi_guid) {
731746
(void) printf(" vdev: %llx\n",
732747
(u_longlong_t)record->zi_guid);
748+
show_iotype = B_TRUE;
733749
} else if (record->zi_func[0] != '\0') {
734750
(void) printf(" panic function: %s\n",
735751
record->zi_func);
@@ -742,7 +758,18 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
742758
} else if (record->zi_timer > 0) {
743759
(void) printf(" timer: %lld ms\n",
744760
(u_longlong_t)NSEC2MSEC(record->zi_timer));
761+
if (record->zi_cmd == ZINJECT_DELAY_READY) {
762+
show_object = B_TRUE;
763+
show_iotype = B_TRUE;
764+
}
745765
} else {
766+
show_object = B_TRUE;
767+
}
768+
if (show_iotype) {
769+
(void) printf("iotype: %s\n",
770+
iotype_to_str(record->zi_iotype));
771+
}
772+
if (show_object) {
746773
(void) printf("objset: %llu\n",
747774
(u_longlong_t)record->zi_objset);
748775
(void) printf("object: %llu\n",
@@ -910,6 +937,7 @@ main(int argc, char **argv)
910937
int ret;
911938
int flags = 0;
912939
uint32_t dvas = 0;
940+
hrtime_t ready_delay = -1;
913941

914942
if ((g_zfs = libzfs_init()) == NULL) {
915943
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
@@ -940,7 +968,7 @@ main(int argc, char **argv)
940968
}
941969

942970
while ((c = getopt(argc, argv,
943-
":aA:b:C:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:")) != -1) {
971+
":aA:b:C:d:D:E:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:")) != -1) {
944972
switch (c) {
945973
case 'a':
946974
flags |= ZINJECT_FLUSH_ARC;
@@ -1113,6 +1141,18 @@ main(int argc, char **argv)
11131141
case 'u':
11141142
flags |= ZINJECT_UNLOAD_SPA;
11151143
break;
1144+
case 'E':
1145+
ready_delay = MSEC2NSEC(strtol(optarg, &end, 10));
1146+
if (ready_delay <= 0 || *end != '\0') {
1147+
(void) fprintf(stderr, "invalid delay '%s': "
1148+
"must be a positive duration\n", optarg);
1149+
usage();
1150+
libzfs_fini(g_zfs);
1151+
return (1);
1152+
}
1153+
record.zi_cmd = ZINJECT_DELAY_READY;
1154+
record.zi_timer = ready_delay;
1155+
break;
11161156
case 'L':
11171157
if ((label = name_to_type(optarg)) == TYPE_INVAL &&
11181158
!LABEL_TYPE(type)) {
@@ -1150,7 +1190,7 @@ main(int argc, char **argv)
11501190
*/
11511191
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
11521192
level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED ||
1153-
record.zi_freq > 0 || dvas != 0) {
1193+
record.zi_freq > 0 || dvas != 0 || ready_delay >= 0) {
11541194
(void) fprintf(stderr, "cancel (-c) incompatible with "
11551195
"any other options\n");
11561196
usage();
@@ -1186,7 +1226,7 @@ main(int argc, char **argv)
11861226
*/
11871227
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
11881228
level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED ||
1189-
dvas != 0) {
1229+
dvas != 0 || ready_delay >= 0) {
11901230
(void) fprintf(stderr, "device (-d) incompatible with "
11911231
"data error injection\n");
11921232
usage();
@@ -1276,13 +1316,23 @@ main(int argc, char **argv)
12761316
return (1);
12771317
}
12781318

1279-
record.zi_cmd = ZINJECT_DATA_FAULT;
1319+
if (record.zi_cmd == ZINJECT_UNINITIALIZED) {
1320+
record.zi_cmd = ZINJECT_DATA_FAULT;
1321+
if (!error)
1322+
error = EIO;
1323+
} else if (error != 0) {
1324+
(void) fprintf(stderr, "error type -e incompatible "
1325+
"with delay injection\n");
1326+
libzfs_fini(g_zfs);
1327+
return (1);
1328+
} else {
1329+
record.zi_iotype = io_type;
1330+
}
1331+
12801332
if (translate_raw(raw, &record) != 0) {
12811333
libzfs_fini(g_zfs);
12821334
return (1);
12831335
}
1284-
if (!error)
1285-
error = EIO;
12861336
} else if (record.zi_cmd == ZINJECT_PANIC) {
12871337
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
12881338
level != 0 || device != NULL || record.zi_freq > 0 ||
@@ -1410,6 +1460,13 @@ main(int argc, char **argv)
14101460
record.zi_dvas = dvas;
14111461
}
14121462

1463+
if (record.zi_cmd != ZINJECT_UNINITIALIZED && error != 0) {
1464+
(void) fprintf(stderr, "error type -e incompatible "
1465+
"with delay injection\n");
1466+
libzfs_fini(g_zfs);
1467+
return (1);
1468+
}
1469+
14131470
if (error == EACCES) {
14141471
if (type != TYPE_DATA) {
14151472
(void) fprintf(stderr, "decryption errors "
@@ -1425,17 +1482,19 @@ main(int argc, char **argv)
14251482
* not found.
14261483
*/
14271484
error = ECKSUM;
1428-
} else {
1485+
} else if (record.zi_cmd == ZINJECT_UNINITIALIZED) {
14291486
record.zi_cmd = ZINJECT_DATA_FAULT;
1487+
if (!error)
1488+
error = EIO;
1489+
} else {
1490+
record.zi_iotype = io_type;
14301491
}
14311492

14321493
if (translate_record(type, argv[0], range, level, &record, pool,
14331494
dataset) != 0) {
14341495
libzfs_fini(g_zfs);
14351496
return (1);
14361497
}
1437-
if (!error)
1438-
error = EIO;
14391498
}
14401499

14411500
/*

include/sys/zfs_ioctl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ typedef enum zinject_type {
455455
ZINJECT_DECRYPT_FAULT,
456456
ZINJECT_DELAY_IMPORT,
457457
ZINJECT_DELAY_EXPORT,
458+
ZINJECT_DELAY_READY,
458459
} zinject_type_t;
459460

460461
typedef enum zinject_iotype {

include/sys/zio.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ extern void zio_handle_ignored_writes(zio_t *zio);
718718
extern hrtime_t zio_handle_io_delay(zio_t *zio);
719719
extern void zio_handle_import_delay(spa_t *spa, hrtime_t elapsed);
720720
extern void zio_handle_export_delay(spa_t *spa, hrtime_t elapsed);
721+
extern void zio_handle_ready_delay(zio_t *zio);
721722

722723
/*
723724
* Checksum ereport functions

man/man8/zinject.8

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ This injector is automatically cleared after the import is finished.
138138
.
139139
.It Xo
140140
.Nm zinject
141+
.Fl E Ar delay
142+
.Op Fl a
143+
.Op Fl m
144+
.Op Fl f Ar freq
145+
.Op Fl l Ar level
146+
.Op Fl r Ar range
147+
.Op Fl T Ar iotype
148+
.Op Fl t Ar type Ns | Ns Fl b Ar bookmark
149+
.Xc
150+
Inject pipeline ready stage delays for the given object or bookmark.
151+
The delay is specified in milliseconds.
152+
.
153+
.It Xo
154+
.Nm zinject
141155
.Fl I
142156
.Op Fl s Ar seconds Ns | Ns Fl g Ar txgs
143157
.Ar pool

module/zfs/zio.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5305,6 +5305,9 @@ zio_ready(zio_t *zio)
53055305
return (NULL);
53065306
}
53075307

5308+
if (zio_injection_enabled)
5309+
zio_handle_ready_delay(zio);
5310+
53085311
if (zio->io_ready) {
53095312
ASSERT(IO_IS_ALLOCATING(zio));
53105313
ASSERT(BP_GET_BIRTH(bp) == zio->io_txg ||

module/zfs/zio_inject.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,45 @@ zio_handle_export_delay(spa_t *spa, hrtime_t elapsed)
827827
zio_handle_pool_delay(spa, elapsed, ZINJECT_DELAY_EXPORT);
828828
}
829829

830+
/*
831+
* For testing, inject a delay before ready state
832+
*/
833+
void
834+
zio_handle_ready_delay(zio_t *zio)
835+
{
836+
inject_handler_t *handler;
837+
uint64_t delay = 0;
838+
839+
/*
840+
* Ignore I/O not associated with any logical data.
841+
*/
842+
if (zio->io_logical == NULL)
843+
return;
844+
845+
rw_enter(&inject_lock, RW_READER);
846+
847+
for (handler = list_head(&inject_handlers); handler != NULL;
848+
handler = list_next(&inject_handlers, handler)) {
849+
if (zio->io_spa != handler->zi_spa ||
850+
handler->zi_record.zi_cmd != ZINJECT_DELAY_READY)
851+
continue;
852+
853+
/* If this handler matches, inject the delay */
854+
if (zio_match_iotype(zio, handler->zi_record.zi_iotype) &&
855+
zio_match_handler(&zio->io_logical->io_bookmark,
856+
zio->io_bp ? BP_GET_TYPE(zio->io_bp) : DMU_OT_NONE,
857+
zio_match_dva(zio), &handler->zi_record, zio->io_error)) {
858+
delay = handler->zi_record.zi_timer;
859+
break;
860+
}
861+
}
862+
863+
rw_exit(&inject_lock);
864+
865+
if (delay)
866+
zfs_sleep_until(gethrtime() + delay);
867+
}
868+
830869
static int
831870
zio_calculate_range(const char *pool, zinject_record_t *record)
832871
{

0 commit comments

Comments
 (0)