Skip to content

Commit b0b64e9

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 b0b64e9

File tree

6 files changed

+141
-9
lines changed

6 files changed

+141
-9
lines changed

cmd/zinject/zinject.c

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,13 @@ usage(void)
346346
"\t\tsuch that the operation takes a minimum of supplied seconds\n"
347347
"\t\tto complete.\n"
348348
"\n"
349+
"\tzinject -E <seconds> [-a] [-m] [-f freq] [-l level] [-r range]\n"
350+
"\t\t[-T iotype] [-t type object | -b bookmark pool]\n"
351+
"\n"
352+
"\t\tInject pipeline ready stage delays for the given object path\n"
353+
"\t\t(data or dnode) or raw bookmark. Arguments other than -E are\n"
354+
"\t\tthe same as for injecting errors documented below."
355+
"\n"
349356
"\tzinject -I [-s <seconds> | -g <txgs>] pool\n"
350357
"\t\tCause the pool to stop writing blocks yet not\n"
351358
"\t\treport errors for a duration. Simulates buggy hardware\n"
@@ -724,12 +731,15 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
724731
if (quiet) {
725732
(void) printf("%llu\n", (u_longlong_t)zc.zc_guid);
726733
} else {
734+
boolean_t show_object = B_FALSE;
735+
boolean_t show_iotype = B_FALSE;
727736
(void) printf("Added handler %llu with the following "
728737
"properties:\n", (u_longlong_t)zc.zc_guid);
729738
(void) printf(" pool: %s\n", pool);
730739
if (record->zi_guid) {
731740
(void) printf(" vdev: %llx\n",
732741
(u_longlong_t)record->zi_guid);
742+
show_iotype = B_TRUE;
733743
} else if (record->zi_func[0] != '\0') {
734744
(void) printf(" panic function: %s\n",
735745
record->zi_func);
@@ -742,7 +752,18 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
742752
} else if (record->zi_timer > 0) {
743753
(void) printf(" timer: %lld ms\n",
744754
(u_longlong_t)NSEC2MSEC(record->zi_timer));
755+
if (record->zi_cmd == ZINJECT_DELAY_READY) {
756+
show_object = B_TRUE;
757+
show_iotype = B_TRUE;
758+
}
745759
} else {
760+
show_object = B_TRUE;
761+
}
762+
if (show_iotype) {
763+
(void) printf("iotype: %s\n",
764+
iotype_to_str(record->zi_iotype));
765+
}
766+
if (show_object) {
746767
(void) printf("objset: %llu\n",
747768
(u_longlong_t)record->zi_objset);
748769
(void) printf("object: %llu\n",
@@ -830,6 +851,25 @@ parse_frequency(const char *str, uint32_t *percent)
830851
return (0);
831852
}
832853

854+
static int
855+
parse_duration(const char *str, hrtime_t *time)
856+
{
857+
double val;
858+
char *end;
859+
860+
val = strtod(str, &end);
861+
if (end == NULL || *end != '\0')
862+
return (EINVAL);
863+
864+
/* valid range is [0, LLONG_MAX] */
865+
val *= NANOSEC;
866+
if (val < 0 || val > LLONG_MAX)
867+
return (ERANGE);
868+
869+
*time = (hrtime_t)val;
870+
return (0);
871+
}
872+
833873
/*
834874
* This function converts a string specifier for DVAs into a bit mask.
835875
* The dva's provided by the user should be 0 indexed and separated by
@@ -910,6 +950,7 @@ main(int argc, char **argv)
910950
int ret;
911951
int flags = 0;
912952
uint32_t dvas = 0;
953+
hrtime_t ready_delay = -1;
913954

914955
if ((g_zfs = libzfs_init()) == NULL) {
915956
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
@@ -940,7 +981,7 @@ main(int argc, char **argv)
940981
}
941982

942983
while ((c = getopt(argc, argv,
943-
":aA:b:C:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:")) != -1) {
984+
":aA:b:C:d:D:E:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:")) != -1) {
944985
switch (c) {
945986
case 'a':
946987
flags |= ZINJECT_FLUSH_ARC;
@@ -1113,6 +1154,18 @@ main(int argc, char **argv)
11131154
case 'u':
11141155
flags |= ZINJECT_UNLOAD_SPA;
11151156
break;
1157+
case 'E':
1158+
ret = parse_duration(optarg, &ready_delay);
1159+
if (ret != 0) {
1160+
(void) fprintf(stderr, "invalid delay '%s': "
1161+
"must be a positive duration\n", optarg);
1162+
usage();
1163+
libzfs_fini(g_zfs);
1164+
return (1);
1165+
}
1166+
record.zi_cmd = ZINJECT_DELAY_READY;
1167+
record.zi_timer = ready_delay;
1168+
break;
11161169
case 'L':
11171170
if ((label = name_to_type(optarg)) == TYPE_INVAL &&
11181171
!LABEL_TYPE(type)) {
@@ -1150,7 +1203,7 @@ main(int argc, char **argv)
11501203
*/
11511204
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
11521205
level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED ||
1153-
record.zi_freq > 0 || dvas != 0) {
1206+
record.zi_freq > 0 || dvas != 0 || ready_delay >= 0) {
11541207
(void) fprintf(stderr, "cancel (-c) incompatible with "
11551208
"any other options\n");
11561209
usage();
@@ -1186,7 +1239,7 @@ main(int argc, char **argv)
11861239
*/
11871240
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
11881241
level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED ||
1189-
dvas != 0) {
1242+
dvas != 0 || ready_delay >= 0) {
11901243
(void) fprintf(stderr, "device (-d) incompatible with "
11911244
"data error injection\n");
11921245
usage();
@@ -1276,13 +1329,23 @@ main(int argc, char **argv)
12761329
return (1);
12771330
}
12781331

1279-
record.zi_cmd = ZINJECT_DATA_FAULT;
1332+
if (record.zi_cmd == ZINJECT_UNINITIALIZED) {
1333+
record.zi_cmd = ZINJECT_DATA_FAULT;
1334+
if (!error)
1335+
error = EIO;
1336+
} else if (error != 0) {
1337+
(void) fprintf(stderr, "error type -e incompatible "
1338+
"with delay injection\n");
1339+
libzfs_fini(g_zfs);
1340+
return (1);
1341+
} else {
1342+
record.zi_iotype = io_type;
1343+
}
1344+
12801345
if (translate_raw(raw, &record) != 0) {
12811346
libzfs_fini(g_zfs);
12821347
return (1);
12831348
}
1284-
if (!error)
1285-
error = EIO;
12861349
} else if (record.zi_cmd == ZINJECT_PANIC) {
12871350
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
12881351
level != 0 || device != NULL || record.zi_freq > 0 ||
@@ -1410,6 +1473,13 @@ main(int argc, char **argv)
14101473
record.zi_dvas = dvas;
14111474
}
14121475

1476+
if (record.zi_cmd != ZINJECT_UNINITIALIZED && error != 0) {
1477+
(void) fprintf(stderr, "error type -e incompatible "
1478+
"with delay injection\n");
1479+
libzfs_fini(g_zfs);
1480+
return (1);
1481+
}
1482+
14131483
if (error == EACCES) {
14141484
if (type != TYPE_DATA) {
14151485
(void) fprintf(stderr, "decryption errors "
@@ -1425,17 +1495,19 @@ main(int argc, char **argv)
14251495
* not found.
14261496
*/
14271497
error = ECKSUM;
1428-
} else {
1498+
} else if (record.zi_cmd == ZINJECT_UNINITIALIZED) {
14291499
record.zi_cmd = ZINJECT_DATA_FAULT;
1500+
if (!error)
1501+
error = EIO;
1502+
} else {
1503+
record.zi_iotype = io_type;
14301504
}
14311505

14321506
if (translate_record(type, argv[0], range, level, &record, pool,
14331507
dataset) != 0) {
14341508
libzfs_fini(g_zfs);
14351509
return (1);
14361510
}
1437-
if (!error)
1438-
error = EIO;
14391511
}
14401512

14411513
/*

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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,22 @@ This injector is automatically cleared after the import is finished.
138138
.
139139
.It Xo
140140
.Nm zinject
141+
.Fl E Ar seconds
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 block.
151+
Arguments other than
152+
.Fl E
153+
are described for injecting errors below.
154+
.
155+
.It Xo
156+
.Nm zinject
141157
.Fl I
142158
.Op Fl s Ar seconds Ns | Ns Fl g Ar txgs
143159
.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)