Skip to content

Commit 0f2b939

Browse files
committed
zinject: Introduce waiting on injection events
This is useful for reproducing races during the sync pipeline Signed-off-by: Robert Evans <[email protected]>
1 parent 6e5b836 commit 0f2b939

File tree

8 files changed

+251
-10
lines changed

8 files changed

+251
-10
lines changed

cmd/zinject/zinject.c

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
* zinject
108108
* zinject <-a | -u pool>
109109
* zinject -c <id|all>
110+
* zinject -w <state|0> [-W <delay>]
110111
* zinject -E <delay> [-a] [-m] [-f freq] [-l level] [-r range]
111112
* [-T iotype] [-t type object | -b bookmark pool]
112113
* zinject [-q] <-t type> [-f freq] [-u] [-a] [-m] [-e errno] [-l level]
@@ -119,6 +120,11 @@
119120
* The '-c' option will clear the given handler, or all handlers if 'all' is
120121
* specified.
121122
*
123+
* The '-w' flag waits until an injection event occurs. Wait calls accept a
124+
* state value to ensure no events are lost. Use '-w 0 -W 0' initially and
125+
* then pass the state value printed on stdout to subsequent wait calls.
126+
* The optional '-W' flag sets an optional timeout in seconds.
127+
*
122128
* The '-e' option takes a string describing the errno to simulate. This must
123129
* be one of 'io', 'checksum', 'decompress', or 'decrypt'. In most cases this
124130
* will result in the same behavior, but RAID-Z will produce a different set of
@@ -297,6 +303,14 @@ usage(void)
297303
"\t\tClear the particular record (if given a numeric ID), or\n"
298304
"\t\tall records if 'all' is specified.\n"
299305
"\n"
306+
"\tzinject -w <state|0> [-W delay]\n"
307+
"\n"
308+
"\t\tWait for an injection event to occur. The 'state' parameter\n"
309+
"\t\tshould be set to zero initially then the value printed to\n"
310+
"\t\tstdout after each call to synchronize with kernel state.\n"
311+
"\t\tThe optional timeout is specified in milliseconds.\n"
312+
"\t\tWaits forever if timeout is omitted.\n"
313+
"\n"
300314
"\tzinject -p <function name> pool\n"
301315
"\t\tInject a panic fault at the specified function. Only \n"
302316
"\t\tfunctions which call spa_vdev_config_exit(), or \n"
@@ -938,6 +952,8 @@ main(int argc, char **argv)
938952
int flags = 0;
939953
uint32_t dvas = 0;
940954
hrtime_t ready_delay = -1;
955+
char *wait = NULL;
956+
hrtime_t wait_timeout = -1;
941957

942958
if ((g_zfs = libzfs_init()) == NULL) {
943959
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
@@ -968,7 +984,7 @@ main(int argc, char **argv)
968984
}
969985

970986
while ((c = getopt(argc, argv,
971-
":aA:b:C:d:D:E:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:")) != -1) {
987+
":aA:b:C:d:D:E:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:w:W:")) != -1) {
972988
switch (c) {
973989
case 'a':
974990
flags |= ZINJECT_FLUSH_ARC;
@@ -1141,6 +1157,9 @@ main(int argc, char **argv)
11411157
case 'u':
11421158
flags |= ZINJECT_UNLOAD_SPA;
11431159
break;
1160+
case 'w':
1161+
wait = optarg;
1162+
break;
11441163
case 'E':
11451164
ready_delay = MSEC2NSEC(strtol(optarg, &end, 10));
11461165
if (ready_delay <= 0 || *end != '\0') {
@@ -1163,6 +1182,16 @@ main(int argc, char **argv)
11631182
return (1);
11641183
}
11651184
break;
1185+
case 'W':
1186+
wait_timeout = MSEC2NSEC(strtol(optarg, &end, 10));
1187+
if (wait_timeout < 0 || *end != '\0') {
1188+
(void) fprintf(stderr, "invalid timeout '%s': "
1189+
"must be a non-negative integer\n", optarg);
1190+
usage();
1191+
libzfs_fini(g_zfs);
1192+
return (1);
1193+
}
1194+
break;
11661195
case ':':
11671196
(void) fprintf(stderr, "option -%c requires an "
11681197
"operand\n", optopt);
@@ -1184,19 +1213,36 @@ main(int argc, char **argv)
11841213
if (record.zi_duration != 0 && record.zi_cmd == 0)
11851214
record.zi_cmd = ZINJECT_IGNORED_WRITES;
11861215

1187-
if (cancel != NULL) {
1188-
/*
1189-
* '-c' is invalid with any other options.
1190-
*/
1191-
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
1192-
level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED ||
1193-
record.zi_freq > 0 || dvas != 0 || ready_delay >= 0) {
1216+
/*
1217+
* '-c' and '-w' are invalid with any other options.
1218+
*/
1219+
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
1220+
level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED ||
1221+
record.zi_freq > 0 || dvas != 0 || ready_delay >= 0) {
1222+
if (cancel != NULL) {
11941223
(void) fprintf(stderr, "cancel (-c) incompatible with "
11951224
"any other options\n");
11961225
usage();
11971226
libzfs_fini(g_zfs);
11981227
return (2);
11991228
}
1229+
if (wait != NULL) {
1230+
(void) fprintf(stderr, "wait (-w) incompatible with "
1231+
"any other options\n");
1232+
usage();
1233+
libzfs_fini(g_zfs);
1234+
return (2);
1235+
}
1236+
}
1237+
1238+
if (cancel != NULL) {
1239+
if (wait != NULL) {
1240+
(void) fprintf(stderr, "cancel (-c) incompatible with "
1241+
"wait (-w) option\n");
1242+
usage();
1243+
libzfs_fini(g_zfs);
1244+
return (2);
1245+
}
12001246
if (argc != 0) {
12011247
(void) fprintf(stderr, "extraneous argument to '-c'\n");
12021248
usage();
@@ -1219,6 +1265,32 @@ main(int argc, char **argv)
12191265
}
12201266
}
12211267

1268+
if (wait != NULL) {
1269+
uint64_t state;
1270+
if (argc != 0) {
1271+
(void) fprintf(stderr, "extraneous argument to '-w'\n");
1272+
usage();
1273+
libzfs_fini(g_zfs);
1274+
return (2);
1275+
}
1276+
state = (uint64_t)strtoull(wait, &end, 10);
1277+
if (*end != 0) {
1278+
(void) fprintf(stderr, "invalid state '%s': "
1279+
"must be an unsigned integer\n", wait);
1280+
usage();
1281+
libzfs_fini(g_zfs);
1282+
return (1);
1283+
}
1284+
error = lzc_wait_inject(&state, wait_timeout);
1285+
if (error == ETIMEDOUT)
1286+
(void) printf("wait timeout\n");
1287+
else if (error != 0)
1288+
(void) printf("wait failed: %s\n", strerror(error));
1289+
else
1290+
(void) printf("%"PRIu64"\n", state);
1291+
return (error == 0 ? 0 : 1);
1292+
}
1293+
12221294
if (device != NULL) {
12231295
/*
12241296
* Device (-d) injection uses a completely different mechanism

include/libzfs_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ _LIBZFS_CORE_H int lzc_scrub(zfs_ioc_t, const char *, nvlist_t *, nvlist_t **);
165165
_LIBZFS_CORE_H int lzc_ddt_prune(const char *, zpool_ddt_prune_unit_t,
166166
uint64_t);
167167

168+
_LIBZFS_CORE_H int lzc_wait_inject(uint64_t *state, hrtime_t timeout);
169+
168170
#ifdef __cplusplus
169171
}
170172
#endif

include/sys/fs/zfs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,6 +1566,7 @@ typedef enum zfs_ioc {
15661566
ZFS_IOC_POOL_SCRUB, /* 0x5a57 */
15671567
ZFS_IOC_POOL_PREFETCH, /* 0x5a58 */
15681568
ZFS_IOC_DDT_PRUNE, /* 0x5a59 */
1569+
ZFS_IOC_WAIT_INJECT, /* 0x5a5a */
15691570

15701571
/*
15711572
* Per-platform (Optional) - 8/128 numbers reserved.
@@ -1826,6 +1827,12 @@ typedef enum {
18261827
#define DDT_PRUNE_UNIT "ddt_prune_unit"
18271828
#define DDT_PRUNE_AMOUNT "ddt_prune_amount"
18281829

1830+
/*
1831+
* The following are names used when invoking ZFS_IOC_WAIT_INJECT.
1832+
*/
1833+
#define WAIT_INJECT_STATE "state"
1834+
#define WAIT_INJECT_TIMEOUT "timeout"
1835+
18291836
/*
18301837
* Flags for ZFS_IOC_VDEV_SET_STATE
18311838
*/

include/sys/zio.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ extern int zio_inject_fault(char *name, int flags, int *id,
704704
struct zinject_record *record);
705705
extern int zio_inject_list_next(int *id, char *name, size_t buflen,
706706
struct zinject_record *record);
707+
extern int zio_inject_wait(uint64_t *state, hrtime_t timeout);
707708
extern int zio_clear_fault(int id);
708709
extern void zio_handle_panic_injection(spa_t *spa, const char *tag,
709710
uint64_t type);

lib/libzfs_core/libzfs_core.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,3 +1994,28 @@ lzc_ddt_prune(const char *pool, zpool_ddt_prune_unit_t unit, uint64_t amount)
19941994

19951995
return (error);
19961996
}
1997+
1998+
/*
1999+
* Wait for injection events. See zio_inject_wait.
2000+
*/
2001+
int
2002+
lzc_wait_inject(uint64_t *state, hrtime_t timeout)
2003+
{
2004+
int error;
2005+
2006+
nvlist_t *result = NULL;
2007+
nvlist_t *args = fnvlist_alloc();
2008+
2009+
fnvlist_add_uint64(args, WAIT_INJECT_STATE, *state);
2010+
if (timeout >= 0)
2011+
VERIFY0(nvlist_add_hrtime(args, WAIT_INJECT_TIMEOUT, timeout));
2012+
2013+
error = lzc_ioctl(ZFS_IOC_WAIT_INJECT, NULL, args, &result);
2014+
if (error == 0)
2015+
*state = fnvlist_lookup_uint64(result, WAIT_INJECT_STATE);
2016+
2017+
fnvlist_free(args);
2018+
fnvlist_free(result);
2019+
2020+
return (error);
2021+
}

man/man8/zinject.8

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,21 @@ Cancel injection records.
6161
.
6262
.It Xo
6363
.Nm zinject
64+
.Fl w Ar state Ns | Ns Sy 0
65+
.Op Fl W Ar delay
66+
.Xc
67+
Wait until an injection event occurs.
68+
The
69+
.Ar state
70+
parameter synchronizes with kernel state and
71+
should be set to 0 for first call with zero timeout,
72+
then the value printed to stdout after each wait.
73+
.Fl W Ar delay
74+
sets an optional timeout in milliseconds.
75+
If omitted, waits forever.
76+
.
77+
.It Xo
78+
.Nm zinject
6479
.Fl d Ar vdev
6580
.Fl A Sy degrade Ns | Ns Sy fault
6681
.Ar pool

module/zfs/zfs_ioctl.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6094,6 +6094,41 @@ zfs_ioc_inject_list_next(zfs_cmd_t *zc)
60946094
return (error);
60956095
}
60966096

6097+
/*
6098+
* Waits for an injection event to occur (event injected or handler add/remove).
6099+
* innvl: {
6100+
* "state": wait state token
6101+
* "timeout": how long to wait in nanoseconds
6102+
* }
6103+
* outnvl: {
6104+
* "state": wait state token
6105+
* }
6106+
* Returns 0, EINTR, or ETIMEDOUT.
6107+
*/
6108+
static const zfs_ioc_key_t zfs_keys_wait_inject[] = {
6109+
{"state", DATA_TYPE_UINT64, ZK_OPTIONAL},
6110+
{"timeout", DATA_TYPE_HRTIME, ZK_OPTIONAL},
6111+
};
6112+
6113+
static int
6114+
zfs_ioc_wait_inject(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
6115+
{
6116+
(void) name;
6117+
uint64_t state;
6118+
hrtime_t timeout;
6119+
int error;
6120+
6121+
if (nvlist_lookup_uint64(innvl, "state", &state) != 0)
6122+
state = 0;
6123+
if (nvlist_lookup_hrtime(innvl, "timeout", &timeout) != 0)
6124+
timeout = TIME_MAX;
6125+
6126+
error = zio_inject_wait(&state, timeout);
6127+
fnvlist_add_uint64(outnvl, "state", state);
6128+
6129+
return (error);
6130+
}
6131+
60976132
static int
60986133
zfs_ioc_error_log(zfs_cmd_t *zc)
60996134
{
@@ -7687,6 +7722,10 @@ zfs_ioctl_init(void)
76877722
zfs_ioc_clear_fault, zfs_secpolicy_inject);
76887723
zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_LIST_NEXT,
76897724
zfs_ioc_inject_list_next, zfs_secpolicy_inject);
7725+
zfs_ioctl_register("wait_inject", ZFS_IOC_WAIT_INJECT,
7726+
zfs_ioc_wait_inject, zfs_secpolicy_inject,
7727+
NO_NAME, POOL_CHECK_NONE, B_FALSE, B_FALSE,
7728+
zfs_keys_wait_inject, ARRAY_SIZE(zfs_keys_wait_inject));
76907729

76917730
/*
76927731
* pool destroy, and export don't log the history as part of

0 commit comments

Comments
 (0)