1185029Spjd/*
2185029Spjd * CDDL HEADER START
3185029Spjd *
4185029Spjd * The contents of this file are subject to the terms of the
5185029Spjd * Common Development and Distribution License (the "License").
6185029Spjd * You may not use this file except in compliance with the License.
7185029Spjd *
8185029Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9185029Spjd * or http://www.opensolaris.org/os/licensing.
10185029Spjd * See the License for the specific language governing permissions
11185029Spjd * and limitations under the License.
12185029Spjd *
13185029Spjd * When distributing Covered Code, include this CDDL HEADER in each
14185029Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15185029Spjd * If applicable, add the following below this CDDL HEADER, with the
16185029Spjd * fields enclosed by brackets "[]" replaced with your own identifying
17185029Spjd * information: Portions Copyright [yyyy] [name of copyright owner]
18185029Spjd *
19185029Spjd * CDDL HEADER END
20185029Spjd */
21185029Spjd/*
22219089Spjd * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23297108Smav * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
24185029Spjd */
25185029Spjd
26185029Spjd/*
27185029Spjd * ZFS Fault Injector
28185029Spjd *
29185029Spjd * This userland component takes a set of options and uses libzpool to translate
30185029Spjd * from a user-visible object type and name to an internal representation.
31185029Spjd * There are two basic types of faults: device faults and data faults.
32185029Spjd *
33185029Spjd *
34185029Spjd * DEVICE FAULTS
35185029Spjd *
36185029Spjd * Errors can be injected into a particular vdev using the '-d' option.  This
37185029Spjd * option takes a path or vdev GUID to uniquely identify the device within a
38185029Spjd * pool.  There are two types of errors that can be injected, EIO and ENXIO,
39185029Spjd * that can be controlled through the '-e' option.  The default is ENXIO.  For
40185029Spjd * EIO failures, any attempt to read data from the device will return EIO, but
41185029Spjd * subsequent attempt to reopen the device will succeed.  For ENXIO failures,
42185029Spjd * any attempt to read from the device will return EIO, but any attempt to
43185029Spjd * reopen the device will also return ENXIO.
44185029Spjd * For label faults, the -L option must be specified. This allows faults
45219089Spjd * to be injected into either the nvlist, uberblock, pad1, or pad2 region
46219089Spjd * of all the labels for the specified device.
47185029Spjd *
48185029Spjd * This form of the command looks like:
49185029Spjd *
50219089Spjd * 	zinject -d device [-e errno] [-L <uber | nvlist | pad1 | pad2>] pool
51185029Spjd *
52185029Spjd *
53185029Spjd * DATA FAULTS
54185029Spjd *
55185029Spjd * We begin with a tuple of the form:
56185029Spjd *
57185029Spjd * 	<type,level,range,object>
58185029Spjd *
59185029Spjd * 	type	A string describing the type of data to target.  Each type
60185029Spjd * 		implicitly describes how to interpret 'object'. Currently,
61185029Spjd * 		the following values are supported:
62185029Spjd *
63185029Spjd * 		data		User data for a file
64185029Spjd * 		dnode		Dnode for a file or directory
65185029Spjd *
66185029Spjd *		The following MOS objects are special.  Instead of injecting
67185029Spjd *		errors on a particular object or blkid, we inject errors across
68185029Spjd *		all objects of the given type.
69185029Spjd *
70185029Spjd * 		mos		Any data in the MOS
71185029Spjd * 		mosdir		object directory
72185029Spjd * 		config		pool configuration
73219089Spjd * 		bpobj		blkptr list
74185029Spjd * 		spacemap	spacemap
75185029Spjd * 		metaslab	metaslab
76185029Spjd * 		errlog		persistent error log
77185029Spjd *
78185029Spjd * 	level	Object level.  Defaults to '0', not applicable to all types.  If
79185029Spjd * 		a range is given, this corresponds to the indirect block
80185029Spjd * 		corresponding to the specific range.
81185029Spjd *
82185029Spjd *	range	A numerical range [start,end) within the object.  Defaults to
83185029Spjd *		the full size of the file.
84185029Spjd *
85185029Spjd * 	object	A string describing the logical location of the object.  For
86185029Spjd * 		files and directories (currently the only supported types),
87185029Spjd * 		this is the path of the object on disk.
88185029Spjd *
89185029Spjd * This is translated, via libzpool, into the following internal representation:
90185029Spjd *
91185029Spjd * 	<type,objset,object,level,range>
92185029Spjd *
93185029Spjd * These types should be self-explanatory.  This tuple is then passed to the
94185029Spjd * kernel via a special ioctl() to initiate fault injection for the given
95185029Spjd * object.  Note that 'type' is not strictly necessary for fault injection, but
96185029Spjd * is used when translating existing faults into a human-readable string.
97185029Spjd *
98185029Spjd *
99185029Spjd * The command itself takes one of the forms:
100185029Spjd *
101185029Spjd * 	zinject
102185029Spjd * 	zinject <-a | -u pool>
103185029Spjd * 	zinject -c <id|all>
104185029Spjd * 	zinject [-q] <-t type> [-f freq] [-u] [-a] [-m] [-e errno] [-l level]
105185029Spjd *	    [-r range] <object>
106185029Spjd * 	zinject [-f freq] [-a] [-m] [-u] -b objset:object:level:start:end pool
107185029Spjd *
108185029Spjd * With no arguments, the command prints all currently registered injection
109185029Spjd * handlers, with their numeric identifiers.
110185029Spjd *
111185029Spjd * The '-c' option will clear the given handler, or all handlers if 'all' is
112185029Spjd * specified.
113185029Spjd *
114185029Spjd * The '-e' option takes a string describing the errno to simulate.  This must
115185029Spjd * be either 'io' or 'checksum'.  In most cases this will result in the same
116185029Spjd * behavior, but RAID-Z will produce a different set of ereports for this
117185029Spjd * situation.
118185029Spjd *
119185029Spjd * The '-a', '-u', and '-m' flags toggle internal flush behavior.  If '-a' is
120185029Spjd * specified, then the ARC cache is flushed appropriately.  If '-u' is
121185029Spjd * specified, then the underlying SPA is unloaded.  Either of these flags can be
122185029Spjd * specified independently of any other handlers.  The '-m' flag automatically
123185029Spjd * does an unmount and remount of the underlying dataset to aid in flushing the
124185029Spjd * cache.
125185029Spjd *
126185029Spjd * The '-f' flag controls the frequency of errors injected, expressed as a
127185029Spjd * integer percentage between 1 and 100.  The default is 100.
128185029Spjd *
129185029Spjd * The this form is responsible for actually injecting the handler into the
130185029Spjd * framework.  It takes the arguments described above, translates them to the
131185029Spjd * internal tuple using libzpool, and then issues an ioctl() to register the
132185029Spjd * handler.
133185029Spjd *
134185029Spjd * The final form can target a specific bookmark, regardless of whether a
135185029Spjd * human-readable interface has been designed.  It allows developers to specify
136185029Spjd * a particular block by number.
137185029Spjd */
138185029Spjd
139185029Spjd#include <errno.h>
140185029Spjd#include <fcntl.h>
141185029Spjd#include <stdio.h>
142185029Spjd#include <stdlib.h>
143185029Spjd#include <strings.h>
144185029Spjd#include <unistd.h>
145185029Spjd
146185029Spjd#include <sys/fs/zfs.h>
147186568Srwatson#include <sys/param.h>
148185029Spjd#include <sys/mount.h>
149185029Spjd
150185029Spjd#include <libzfs.h>
151262101Savg#include <libzfs_compat.h>
152185029Spjd
153185029Spjd#undef verify	/* both libzfs.h and zfs_context.h want to define this */
154185029Spjd
155185029Spjd#include "zinject.h"
156185029Spjd
157185029Spjdlibzfs_handle_t *g_zfs;
158185029Spjdint zfs_fd;
159185029Spjd
160185029Spjd#ifndef ECKSUM
161185029Spjd#define	ECKSUM	EBADE
162185029Spjd#endif
163185029Spjd
164185029Spjdstatic const char *errtable[TYPE_INVAL] = {
165185029Spjd	"data",
166185029Spjd	"dnode",
167185029Spjd	"mos",
168185029Spjd	"mosdir",
169185029Spjd	"metaslab",
170185029Spjd	"config",
171219089Spjd	"bpobj",
172185029Spjd	"spacemap",
173185029Spjd	"errlog",
174185029Spjd	"uber",
175219089Spjd	"nvlist",
176219089Spjd	"pad1",
177219089Spjd	"pad2"
178185029Spjd};
179185029Spjd
180185029Spjdstatic err_type_t
181185029Spjdname_to_type(const char *arg)
182185029Spjd{
183185029Spjd	int i;
184185029Spjd	for (i = 0; i < TYPE_INVAL; i++)
185185029Spjd		if (strcmp(errtable[i], arg) == 0)
186185029Spjd			return (i);
187185029Spjd
188185029Spjd	return (TYPE_INVAL);
189185029Spjd}
190185029Spjd
191185029Spjdstatic const char *
192185029Spjdtype_to_name(uint64_t type)
193185029Spjd{
194185029Spjd	switch (type) {
195185029Spjd	case DMU_OT_OBJECT_DIRECTORY:
196185029Spjd		return ("mosdir");
197185029Spjd	case DMU_OT_OBJECT_ARRAY:
198185029Spjd		return ("metaslab");
199185029Spjd	case DMU_OT_PACKED_NVLIST:
200185029Spjd		return ("config");
201219089Spjd	case DMU_OT_BPOBJ:
202219089Spjd		return ("bpobj");
203185029Spjd	case DMU_OT_SPACE_MAP:
204185029Spjd		return ("spacemap");
205185029Spjd	case DMU_OT_ERROR_LOG:
206185029Spjd		return ("errlog");
207185029Spjd	default:
208185029Spjd		return ("-");
209185029Spjd	}
210185029Spjd}
211185029Spjd
212185029Spjd
213185029Spjd/*
214185029Spjd * Print usage message.
215185029Spjd */
216185029Spjdvoid
217185029Spjdusage(void)
218185029Spjd{
219185029Spjd	(void) printf(
220185029Spjd	    "usage:\n"
221185029Spjd	    "\n"
222185029Spjd	    "\tzinject\n"
223185029Spjd	    "\n"
224185029Spjd	    "\t\tList all active injection records.\n"
225185029Spjd	    "\n"
226185029Spjd	    "\tzinject -c <id|all>\n"
227185029Spjd	    "\n"
228185029Spjd	    "\t\tClear the particular record (if given a numeric ID), or\n"
229185029Spjd	    "\t\tall records if 'all' is specificed.\n"
230185029Spjd	    "\n"
231219089Spjd	    "\tzinject -p <function name> pool\n"
232297108Smav	    "\n"
233219089Spjd	    "\t\tInject a panic fault at the specified function. Only \n"
234219089Spjd	    "\t\tfunctions which call spa_vdev_config_exit(), or \n"
235219089Spjd	    "\t\tspa_vdev_exit() will trigger a panic.\n"
236219089Spjd	    "\n"
237219089Spjd	    "\tzinject -d device [-e errno] [-L <nvlist|uber|pad1|pad2>] [-F]\n"
238219089Spjd	    "\t    [-T <read|write|free|claim|all> pool\n"
239297108Smav	    "\n"
240185029Spjd	    "\t\tInject a fault into a particular device or the device's\n"
241219089Spjd	    "\t\tlabel.  Label injection can either be 'nvlist', 'uber',\n "
242219089Spjd	    "\t\t'pad1', or 'pad2'.\n"
243219089Spjd	    "\t\t'errno' can be 'nxio' (the default), 'io', or 'dtl'.\n"
244185029Spjd	    "\n"
245219089Spjd	    "\tzinject -d device -A <degrade|fault> pool\n"
246297108Smav	    "\n"
247219089Spjd	    "\t\tPerform a specific action on a particular device\n"
248219089Spjd	    "\n"
249297108Smav	    "\tzinject -d device -D latency:lanes pool\n"
250297108Smav	    "\n"
251297108Smav	    "\t\tAdd an artificial delay to IO requests on a particular\n"
252297108Smav	    "\t\tdevice, such that the requests take a minimum of 'latency'\n"
253297108Smav	    "\t\tmilliseconds to complete. Each delay has an associated\n"
254297108Smav	    "\t\tnumber of 'lanes' which defines the number of concurrent\n"
255297108Smav	    "\t\tIO requests that can be processed.\n"
256297108Smav	    "\n"
257297108Smav	    "\t\tFor example, with a single lane delay of 10 ms (-D 10:1),\n"
258297108Smav	    "\t\tthe device will only be able to service a single IO request\n"
259297108Smav	    "\t\tat a time with each request taking 10 ms to complete. So,\n"
260297108Smav	    "\t\tif only a single request is submitted every 10 ms, the\n"
261297108Smav	    "\t\taverage latency will be 10 ms; but if more than one request\n"
262297108Smav	    "\t\tis submitted every 10 ms, the average latency will be more\n"
263297108Smav	    "\t\tthan 10 ms.\n"
264297108Smav	    "\n"
265297108Smav	    "\t\tSimilarly, if a delay of 10 ms is specified to have two\n"
266297108Smav	    "\t\tlanes (-D 10:2), then the device will be able to service\n"
267297108Smav	    "\t\ttwo requests at a time, each with a minimum latency of\n"
268297108Smav	    "\t\t10 ms. So, if two requests are submitted every 10 ms, then\n"
269297108Smav	    "\t\tthe average latency will be 10 ms; but if more than two\n"
270297108Smav	    "\t\trequests are submitted every 10 ms, the average latency\n"
271297108Smav	    "\t\twill be more than 10 ms.\n"
272297108Smav	    "\n"
273297108Smav	    "\t\tAlso note, these delays are additive. So two invocations\n"
274297108Smav	    "\t\tof '-D 10:1', is roughly equivalent to a single invocation\n"
275297108Smav	    "\t\tof '-D 10:2'. This also means, one can specify multiple\n"
276297108Smav	    "\t\tlanes with differing target latencies. For example, an\n"
277297108Smav	    "\t\tinvocation of '-D 10:1' followed by '-D 25:2' will\n"
278297108Smav	    "\t\tcreate 3 lanes on the device; one lane with a latency\n"
279297108Smav	    "\t\tof 10 ms and two lanes with a 25 ms latency.\n"
280297108Smav	    "\n"
281219089Spjd	    "\tzinject -I [-s <seconds> | -g <txgs>] pool\n"
282297108Smav	    "\n"
283219089Spjd	    "\t\tCause the pool to stop writing blocks yet not\n"
284219089Spjd	    "\t\treport errors for a duration.  Simulates buggy hardware\n"
285219089Spjd	    "\t\tthat fails to honor cache flush requests.\n"
286219089Spjd	    "\t\tDefault duration is 30 seconds.  The machine is panicked\n"
287219089Spjd	    "\t\tat the end of the duration.\n"
288219089Spjd	    "\n"
289185029Spjd	    "\tzinject -b objset:object:level:blkid pool\n"
290185029Spjd	    "\n"
291185029Spjd	    "\t\tInject an error into pool 'pool' with the numeric bookmark\n"
292185029Spjd	    "\t\tspecified by the remaining tuple.  Each number is in\n"
293185029Spjd	    "\t\thexidecimal, and only one block can be specified.\n"
294185029Spjd	    "\n"
295185029Spjd	    "\tzinject [-q] <-t type> [-e errno] [-l level] [-r range]\n"
296185029Spjd	    "\t    [-a] [-m] [-u] [-f freq] <object>\n"
297185029Spjd	    "\n"
298185029Spjd	    "\t\tInject an error into the object specified by the '-t' option\n"
299185029Spjd	    "\t\tand the object descriptor.  The 'object' parameter is\n"
300185029Spjd	    "\t\tinterperted depending on the '-t' option.\n"
301185029Spjd	    "\n"
302185029Spjd	    "\t\t-q\tQuiet mode.  Only print out the handler number added.\n"
303185029Spjd	    "\t\t-e\tInject a specific error.  Must be either 'io' or\n"
304185029Spjd	    "\t\t\t'checksum'.  Default is 'io'.\n"
305185029Spjd	    "\t\t-l\tInject error at a particular block level. Default is "
306185029Spjd	    "0.\n"
307185029Spjd	    "\t\t-m\tAutomatically remount underlying filesystem.\n"
308185029Spjd	    "\t\t-r\tInject error over a particular logical range of an\n"
309185029Spjd	    "\t\t\tobject.  Will be translated to the appropriate blkid\n"
310185029Spjd	    "\t\t\trange according to the object's properties.\n"
311185029Spjd	    "\t\t-a\tFlush the ARC cache.  Can be specified without any\n"
312185029Spjd	    "\t\t\tassociated object.\n"
313185029Spjd	    "\t\t-u\tUnload the associated pool.  Can be specified with only\n"
314185029Spjd	    "\t\t\ta pool object.\n"
315185029Spjd	    "\t\t-f\tOnly inject errors a fraction of the time.  Expressed as\n"
316185029Spjd	    "\t\t\ta percentage between 1 and 100.\n"
317185029Spjd	    "\n"
318185029Spjd	    "\t-t data\t\tInject an error into the plain file contents of a\n"
319185029Spjd	    "\t\t\tfile.  The object must be specified as a complete path\n"
320185029Spjd	    "\t\t\tto a file on a ZFS filesystem.\n"
321185029Spjd	    "\n"
322185029Spjd	    "\t-t dnode\tInject an error into the metadnode in the block\n"
323185029Spjd	    "\t\t\tcorresponding to the dnode for a file or directory.  The\n"
324185029Spjd	    "\t\t\t'-r' option is incompatible with this mode.  The object\n"
325185029Spjd	    "\t\t\tis specified as a complete path to a file or directory\n"
326185029Spjd	    "\t\t\ton a ZFS filesystem.\n"
327185029Spjd	    "\n"
328185029Spjd	    "\t-t <mos>\tInject errors into the MOS for objects of the given\n"
329219089Spjd	    "\t\t\ttype.  Valid types are: mos, mosdir, config, bpobj,\n"
330185029Spjd	    "\t\t\tspacemap, metaslab, errlog.  The only valid <object> is\n"
331185029Spjd	    "\t\t\tthe poolname.\n");
332185029Spjd}
333185029Spjd
334185029Spjdstatic int
335185029Spjditer_handlers(int (*func)(int, const char *, zinject_record_t *, void *),
336185029Spjd    void *data)
337185029Spjd{
338239774Smm	zfs_cmd_t zc = { 0 };
339185029Spjd	int ret;
340185029Spjd
341185029Spjd	while (ioctl(zfs_fd, ZFS_IOC_INJECT_LIST_NEXT, &zc) == 0)
342185029Spjd		if ((ret = func((int)zc.zc_guid, zc.zc_name,
343185029Spjd		    &zc.zc_inject_record, data)) != 0)
344185029Spjd			return (ret);
345185029Spjd
346219089Spjd	if (errno != ENOENT) {
347219089Spjd		(void) fprintf(stderr, "Unable to list handlers: %s\n",
348219089Spjd		    strerror(errno));
349219089Spjd		return (-1);
350219089Spjd	}
351219089Spjd
352185029Spjd	return (0);
353185029Spjd}
354185029Spjd
355185029Spjdstatic int
356185029Spjdprint_data_handler(int id, const char *pool, zinject_record_t *record,
357185029Spjd    void *data)
358185029Spjd{
359185029Spjd	int *count = data;
360185029Spjd
361219089Spjd	if (record->zi_guid != 0 || record->zi_func[0] != '\0')
362185029Spjd		return (0);
363185029Spjd
364185029Spjd	if (*count == 0) {
365185029Spjd		(void) printf("%3s  %-15s  %-6s  %-6s  %-8s  %3s  %-15s\n",
366185029Spjd		    "ID", "POOL", "OBJSET", "OBJECT", "TYPE", "LVL",  "RANGE");
367185029Spjd		(void) printf("---  ---------------  ------  "
368185029Spjd		    "------  --------  ---  ---------------\n");
369185029Spjd	}
370185029Spjd
371185029Spjd	*count += 1;
372185029Spjd
373185029Spjd	(void) printf("%3d  %-15s  %-6llu  %-6llu  %-8s  %3d  ", id, pool,
374185029Spjd	    (u_longlong_t)record->zi_objset, (u_longlong_t)record->zi_object,
375185029Spjd	    type_to_name(record->zi_type), record->zi_level);
376185029Spjd
377185029Spjd	if (record->zi_start == 0 &&
378185029Spjd	    record->zi_end == -1ULL)
379185029Spjd		(void) printf("all\n");
380185029Spjd	else
381185029Spjd		(void) printf("[%llu, %llu]\n", (u_longlong_t)record->zi_start,
382185029Spjd		    (u_longlong_t)record->zi_end);
383185029Spjd
384185029Spjd	return (0);
385185029Spjd}
386185029Spjd
387185029Spjdstatic int
388185029Spjdprint_device_handler(int id, const char *pool, zinject_record_t *record,
389185029Spjd    void *data)
390185029Spjd{
391185029Spjd	int *count = data;
392185029Spjd
393219089Spjd	if (record->zi_guid == 0 || record->zi_func[0] != '\0')
394185029Spjd		return (0);
395185029Spjd
396297108Smav	if (record->zi_cmd == ZINJECT_DELAY_IO)
397297108Smav		return (0);
398297108Smav
399185029Spjd	if (*count == 0) {
400185029Spjd		(void) printf("%3s  %-15s  %s\n", "ID", "POOL", "GUID");
401185029Spjd		(void) printf("---  ---------------  ----------------\n");
402185029Spjd	}
403185029Spjd
404185029Spjd	*count += 1;
405185029Spjd
406185029Spjd	(void) printf("%3d  %-15s  %llx\n", id, pool,
407185029Spjd	    (u_longlong_t)record->zi_guid);
408185029Spjd
409185029Spjd	return (0);
410185029Spjd}
411185029Spjd
412219089Spjdstatic int
413297108Smavprint_delay_handler(int id, const char *pool, zinject_record_t *record,
414297108Smav    void *data)
415297108Smav{
416297108Smav	int *count = data;
417297108Smav
418297108Smav	if (record->zi_guid == 0 || record->zi_func[0] != '\0')
419297108Smav		return (0);
420297108Smav
421297108Smav	if (record->zi_cmd != ZINJECT_DELAY_IO)
422297108Smav		return (0);
423297108Smav
424297108Smav	if (*count == 0) {
425297108Smav		(void) printf("%3s  %-15s  %-15s  %-15s  %s\n",
426297108Smav		    "ID", "POOL", "DELAY (ms)", "LANES", "GUID");
427297108Smav		(void) printf("---  ---------------  ---------------  "
428297108Smav		    "---------------  ----------------\n");
429297108Smav	}
430297108Smav
431297108Smav	*count += 1;
432297108Smav
433297108Smav	(void) printf("%3d  %-15s  %-15llu  %-15llu  %llx\n", id, pool,
434297108Smav	    (u_longlong_t)NSEC2MSEC(record->zi_timer),
435297108Smav	    (u_longlong_t)record->zi_nlanes,
436297108Smav	    (u_longlong_t)record->zi_guid);
437297108Smav
438297108Smav	return (0);
439297108Smav}
440297108Smav
441297108Smavstatic int
442219089Spjdprint_panic_handler(int id, const char *pool, zinject_record_t *record,
443219089Spjd    void *data)
444219089Spjd{
445219089Spjd	int *count = data;
446219089Spjd
447219089Spjd	if (record->zi_func[0] == '\0')
448219089Spjd		return (0);
449219089Spjd
450219089Spjd	if (*count == 0) {
451219089Spjd		(void) printf("%3s  %-15s  %s\n", "ID", "POOL", "FUNCTION");
452219089Spjd		(void) printf("---  ---------------  ----------------\n");
453219089Spjd	}
454219089Spjd
455219089Spjd	*count += 1;
456219089Spjd
457219089Spjd	(void) printf("%3d  %-15s  %s\n", id, pool, record->zi_func);
458219089Spjd
459219089Spjd	return (0);
460219089Spjd}
461219089Spjd
462185029Spjd/*
463185029Spjd * Print all registered error handlers.  Returns the number of handlers
464185029Spjd * registered.
465185029Spjd */
466185029Spjdstatic int
467185029Spjdprint_all_handlers(void)
468185029Spjd{
469219089Spjd	int count = 0, total = 0;
470185029Spjd
471185029Spjd	(void) iter_handlers(print_device_handler, &count);
472219089Spjd	if (count > 0) {
473219089Spjd		total += count;
474219089Spjd		(void) printf("\n");
475219089Spjd		count = 0;
476219089Spjd	}
477219089Spjd
478297108Smav	(void) iter_handlers(print_delay_handler, &count);
479297108Smav	if (count > 0) {
480297108Smav		total += count;
481297108Smav		(void) printf("\n");
482297108Smav		count = 0;
483297108Smav	}
484297108Smav
485185029Spjd	(void) iter_handlers(print_data_handler, &count);
486219089Spjd	if (count > 0) {
487219089Spjd		total += count;
488219089Spjd		(void) printf("\n");
489219089Spjd		count = 0;
490219089Spjd	}
491185029Spjd
492219089Spjd	(void) iter_handlers(print_panic_handler, &count);
493219089Spjd
494219089Spjd	return (count + total);
495185029Spjd}
496185029Spjd
497185029Spjd/* ARGSUSED */
498185029Spjdstatic int
499185029Spjdcancel_one_handler(int id, const char *pool, zinject_record_t *record,
500185029Spjd    void *data)
501185029Spjd{
502239774Smm	zfs_cmd_t zc = { 0 };
503185029Spjd
504185029Spjd	zc.zc_guid = (uint64_t)id;
505185029Spjd
506185029Spjd	if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
507185029Spjd		(void) fprintf(stderr, "failed to remove handler %d: %s\n",
508185029Spjd		    id, strerror(errno));
509185029Spjd		return (1);
510185029Spjd	}
511185029Spjd
512185029Spjd	return (0);
513185029Spjd}
514185029Spjd
515185029Spjd/*
516185029Spjd * Remove all fault injection handlers.
517185029Spjd */
518185029Spjdstatic int
519185029Spjdcancel_all_handlers(void)
520185029Spjd{
521185029Spjd	int ret = iter_handlers(cancel_one_handler, NULL);
522185029Spjd
523219089Spjd	if (ret == 0)
524219089Spjd		(void) printf("removed all registered handlers\n");
525185029Spjd
526185029Spjd	return (ret);
527185029Spjd}
528185029Spjd
529185029Spjd/*
530185029Spjd * Remove a specific fault injection handler.
531185029Spjd */
532185029Spjdstatic int
533185029Spjdcancel_handler(int id)
534185029Spjd{
535239774Smm	zfs_cmd_t zc = { 0 };
536185029Spjd
537185029Spjd	zc.zc_guid = (uint64_t)id;
538185029Spjd
539185029Spjd	if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
540185029Spjd		(void) fprintf(stderr, "failed to remove handler %d: %s\n",
541185029Spjd		    id, strerror(errno));
542185029Spjd		return (1);
543185029Spjd	}
544185029Spjd
545185029Spjd	(void) printf("removed handler %d\n", id);
546185029Spjd
547185029Spjd	return (0);
548185029Spjd}
549185029Spjd
550185029Spjd/*
551185029Spjd * Register a new fault injection handler.
552185029Spjd */
553185029Spjdstatic int
554185029Spjdregister_handler(const char *pool, int flags, zinject_record_t *record,
555185029Spjd    int quiet)
556185029Spjd{
557239774Smm	zfs_cmd_t zc = { 0 };
558185029Spjd
559185029Spjd	(void) strcpy(zc.zc_name, pool);
560185029Spjd	zc.zc_inject_record = *record;
561185029Spjd	zc.zc_guid = flags;
562185029Spjd
563185029Spjd	if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
564185029Spjd		(void) fprintf(stderr, "failed to add handler: %s\n",
565185029Spjd		    strerror(errno));
566185029Spjd		return (1);
567185029Spjd	}
568185029Spjd
569185029Spjd	if (flags & ZINJECT_NULL)
570185029Spjd		return (0);
571185029Spjd
572185029Spjd	if (quiet) {
573185029Spjd		(void) printf("%llu\n", (u_longlong_t)zc.zc_guid);
574185029Spjd	} else {
575185029Spjd		(void) printf("Added handler %llu with the following "
576185029Spjd		    "properties:\n", (u_longlong_t)zc.zc_guid);
577185029Spjd		(void) printf("  pool: %s\n", pool);
578185029Spjd		if (record->zi_guid) {
579185029Spjd			(void) printf("  vdev: %llx\n",
580185029Spjd			    (u_longlong_t)record->zi_guid);
581219089Spjd		} else if (record->zi_func[0] != '\0') {
582219089Spjd			(void) printf("  panic function: %s\n",
583219089Spjd			    record->zi_func);
584219089Spjd		} else if (record->zi_duration > 0) {
585219089Spjd			(void) printf(" time: %lld seconds\n",
586219089Spjd			    (u_longlong_t)record->zi_duration);
587219089Spjd		} else if (record->zi_duration < 0) {
588219089Spjd			(void) printf(" txgs: %lld \n",
589219089Spjd			    (u_longlong_t)-record->zi_duration);
590185029Spjd		} else {
591185029Spjd			(void) printf("objset: %llu\n",
592185029Spjd			    (u_longlong_t)record->zi_objset);
593185029Spjd			(void) printf("object: %llu\n",
594185029Spjd			    (u_longlong_t)record->zi_object);
595185029Spjd			(void) printf("  type: %llu\n",
596185029Spjd			    (u_longlong_t)record->zi_type);
597185029Spjd			(void) printf(" level: %d\n", record->zi_level);
598185029Spjd			if (record->zi_start == 0 &&
599185029Spjd			    record->zi_end == -1ULL)
600185029Spjd				(void) printf(" range: all\n");
601185029Spjd			else
602185029Spjd				(void) printf(" range: [%llu, %llu)\n",
603185029Spjd				    (u_longlong_t)record->zi_start,
604185029Spjd				    (u_longlong_t)record->zi_end);
605185029Spjd		}
606185029Spjd	}
607185029Spjd
608185029Spjd	return (0);
609185029Spjd}
610185029Spjd
611185029Spjdint
612219089Spjdperform_action(const char *pool, zinject_record_t *record, int cmd)
613219089Spjd{
614239774Smm	zfs_cmd_t zc = { 0 };
615219089Spjd
616219089Spjd	ASSERT(cmd == VDEV_STATE_DEGRADED || cmd == VDEV_STATE_FAULTED);
617219089Spjd	(void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
618219089Spjd	zc.zc_guid = record->zi_guid;
619219089Spjd	zc.zc_cookie = cmd;
620219089Spjd
621219089Spjd	if (ioctl(zfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
622219089Spjd		return (0);
623219089Spjd
624219089Spjd	return (1);
625219089Spjd}
626219089Spjd
627297108Smavstatic int
628297108Smavparse_delay(char *str, uint64_t *delay, uint64_t *nlanes)
629297108Smav{
630297108Smav	unsigned long scan_delay;
631297108Smav	unsigned long scan_nlanes;
632297108Smav
633297108Smav	if (sscanf(str, "%lu:%lu", &scan_delay, &scan_nlanes) != 2)
634297108Smav		return (1);
635297108Smav
636297108Smav	/*
637297108Smav	 * We explicitly disallow a delay of zero here, because we key
638297108Smav	 * off this value being non-zero in translate_device(), to
639297108Smav	 * determine if the fault is a ZINJECT_DELAY_IO fault or not.
640297108Smav	 */
641297108Smav	if (scan_delay == 0)
642297108Smav		return (1);
643297108Smav
644297108Smav	/*
645297108Smav	 * The units for the CLI delay parameter is milliseconds, but
646297108Smav	 * the data passed to the kernel is interpreted as nanoseconds.
647297108Smav	 * Thus we scale the milliseconds to nanoseconds here, and this
648297108Smav	 * nanosecond value is used to pass the delay to the kernel.
649297108Smav	 */
650297108Smav	*delay = MSEC2NSEC(scan_delay);
651297108Smav	*nlanes = scan_nlanes;
652297108Smav
653297108Smav	return (0);
654297108Smav}
655297108Smav
656219089Spjdint
657185029Spjdmain(int argc, char **argv)
658185029Spjd{
659185029Spjd	int c;
660185029Spjd	char *range = NULL;
661185029Spjd	char *cancel = NULL;
662185029Spjd	char *end;
663185029Spjd	char *raw = NULL;
664185029Spjd	char *device = NULL;
665185029Spjd	int level = 0;
666185029Spjd	int quiet = 0;
667185029Spjd	int error = 0;
668185029Spjd	int domount = 0;
669219089Spjd	int io_type = ZIO_TYPES;
670219089Spjd	int action = VDEV_STATE_UNKNOWN;
671185029Spjd	err_type_t type = TYPE_INVAL;
672185029Spjd	err_type_t label = TYPE_INVAL;
673185029Spjd	zinject_record_t record = { 0 };
674185029Spjd	char pool[MAXNAMELEN];
675185029Spjd	char dataset[MAXNAMELEN];
676185029Spjd	zfs_handle_t *zhp;
677219089Spjd	int nowrites = 0;
678219089Spjd	int dur_txg = 0;
679219089Spjd	int dur_secs = 0;
680185029Spjd	int ret;
681185029Spjd	int flags = 0;
682185029Spjd
683185029Spjd	if ((g_zfs = libzfs_init()) == NULL) {
684185029Spjd		(void) fprintf(stderr, "internal error: failed to "
685185029Spjd		    "initialize ZFS library\n");
686185029Spjd		return (1);
687185029Spjd	}
688185029Spjd
689185029Spjd	libzfs_print_on_error(g_zfs, B_TRUE);
690185029Spjd
691185029Spjd	if ((zfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
692185029Spjd		(void) fprintf(stderr, "failed to open ZFS device\n");
693185029Spjd		return (1);
694185029Spjd	}
695185029Spjd
696185029Spjd	if (argc == 1) {
697185029Spjd		/*
698185029Spjd		 * No arguments.  Print the available handlers.  If there are no
699185029Spjd		 * available handlers, direct the user to '-h' for help
700185029Spjd		 * information.
701185029Spjd		 */
702185029Spjd		if (print_all_handlers() == 0) {
703185029Spjd			(void) printf("No handlers registered.\n");
704185029Spjd			(void) printf("Run 'zinject -h' for usage "
705185029Spjd			    "information.\n");
706185029Spjd		}
707185029Spjd
708185029Spjd		return (0);
709185029Spjd	}
710185029Spjd
711219089Spjd	while ((c = getopt(argc, argv,
712247265Smm	    ":aA:b:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:")) != -1) {
713185029Spjd		switch (c) {
714185029Spjd		case 'a':
715185029Spjd			flags |= ZINJECT_FLUSH_ARC;
716185029Spjd			break;
717219089Spjd		case 'A':
718219089Spjd			if (strcasecmp(optarg, "degrade") == 0) {
719219089Spjd				action = VDEV_STATE_DEGRADED;
720219089Spjd			} else if (strcasecmp(optarg, "fault") == 0) {
721219089Spjd				action = VDEV_STATE_FAULTED;
722219089Spjd			} else {
723219089Spjd				(void) fprintf(stderr, "invalid action '%s': "
724219089Spjd				    "must be 'degrade' or 'fault'\n", optarg);
725219089Spjd				usage();
726219089Spjd				return (1);
727219089Spjd			}
728219089Spjd			break;
729185029Spjd		case 'b':
730185029Spjd			raw = optarg;
731185029Spjd			break;
732185029Spjd		case 'c':
733185029Spjd			cancel = optarg;
734185029Spjd			break;
735185029Spjd		case 'd':
736185029Spjd			device = optarg;
737185029Spjd			break;
738247265Smm		case 'D':
739297108Smav			ret = parse_delay(optarg, &record.zi_timer,
740297108Smav			    &record.zi_nlanes);
741297108Smav			if (ret != 0) {
742247265Smm				(void) fprintf(stderr, "invalid i/o delay "
743247265Smm				    "value: '%s'\n", optarg);
744247265Smm				usage();
745247265Smm				return (1);
746247265Smm			}
747247265Smm			break;
748185029Spjd		case 'e':
749185029Spjd			if (strcasecmp(optarg, "io") == 0) {
750185029Spjd				error = EIO;
751185029Spjd			} else if (strcasecmp(optarg, "checksum") == 0) {
752185029Spjd				error = ECKSUM;
753185029Spjd			} else if (strcasecmp(optarg, "nxio") == 0) {
754185029Spjd				error = ENXIO;
755219089Spjd			} else if (strcasecmp(optarg, "dtl") == 0) {
756219089Spjd				error = ECHILD;
757185029Spjd			} else {
758185029Spjd				(void) fprintf(stderr, "invalid error type "
759185029Spjd				    "'%s': must be 'io', 'checksum' or "
760185029Spjd				    "'nxio'\n", optarg);
761185029Spjd				usage();
762185029Spjd				return (1);
763185029Spjd			}
764185029Spjd			break;
765185029Spjd		case 'f':
766185029Spjd			record.zi_freq = atoi(optarg);
767185029Spjd			if (record.zi_freq < 1 || record.zi_freq > 100) {
768185029Spjd				(void) fprintf(stderr, "frequency range must "
769185029Spjd				    "be in the range (0, 100]\n");
770185029Spjd				return (1);
771185029Spjd			}
772185029Spjd			break;
773213198Smm		case 'F':
774213198Smm			record.zi_failfast = B_TRUE;
775213198Smm			break;
776219089Spjd		case 'g':
777219089Spjd			dur_txg = 1;
778219089Spjd			record.zi_duration = (int)strtol(optarg, &end, 10);
779219089Spjd			if (record.zi_duration <= 0 || *end != '\0') {
780219089Spjd				(void) fprintf(stderr, "invalid duration '%s': "
781219089Spjd				    "must be a positive integer\n", optarg);
782219089Spjd				usage();
783219089Spjd				return (1);
784219089Spjd			}
785219089Spjd			/* store duration of txgs as its negative */
786219089Spjd			record.zi_duration *= -1;
787219089Spjd			break;
788185029Spjd		case 'h':
789185029Spjd			usage();
790185029Spjd			return (0);
791219089Spjd		case 'I':
792219089Spjd			/* default duration, if one hasn't yet been defined */
793219089Spjd			nowrites = 1;
794219089Spjd			if (dur_secs == 0 && dur_txg == 0)
795219089Spjd				record.zi_duration = 30;
796219089Spjd			break;
797185029Spjd		case 'l':
798185029Spjd			level = (int)strtol(optarg, &end, 10);
799185029Spjd			if (*end != '\0') {
800185029Spjd				(void) fprintf(stderr, "invalid level '%s': "
801185029Spjd				    "must be an integer\n", optarg);
802185029Spjd				usage();
803185029Spjd				return (1);
804185029Spjd			}
805185029Spjd			break;
806185029Spjd		case 'm':
807185029Spjd			domount = 1;
808185029Spjd			break;
809219089Spjd		case 'p':
810219089Spjd			(void) strlcpy(record.zi_func, optarg,
811219089Spjd			    sizeof (record.zi_func));
812247265Smm			record.zi_cmd = ZINJECT_PANIC;
813219089Spjd			break;
814185029Spjd		case 'q':
815185029Spjd			quiet = 1;
816185029Spjd			break;
817185029Spjd		case 'r':
818185029Spjd			range = optarg;
819185029Spjd			break;
820219089Spjd		case 's':
821219089Spjd			dur_secs = 1;
822219089Spjd			record.zi_duration = (int)strtol(optarg, &end, 10);
823219089Spjd			if (record.zi_duration <= 0 || *end != '\0') {
824219089Spjd				(void) fprintf(stderr, "invalid duration '%s': "
825219089Spjd				    "must be a positive integer\n", optarg);
826219089Spjd				usage();
827219089Spjd				return (1);
828219089Spjd			}
829219089Spjd			break;
830219089Spjd		case 'T':
831219089Spjd			if (strcasecmp(optarg, "read") == 0) {
832219089Spjd				io_type = ZIO_TYPE_READ;
833219089Spjd			} else if (strcasecmp(optarg, "write") == 0) {
834219089Spjd				io_type = ZIO_TYPE_WRITE;
835219089Spjd			} else if (strcasecmp(optarg, "free") == 0) {
836219089Spjd				io_type = ZIO_TYPE_FREE;
837219089Spjd			} else if (strcasecmp(optarg, "claim") == 0) {
838219089Spjd				io_type = ZIO_TYPE_CLAIM;
839219089Spjd			} else if (strcasecmp(optarg, "all") == 0) {
840219089Spjd				io_type = ZIO_TYPES;
841219089Spjd			} else {
842219089Spjd				(void) fprintf(stderr, "invalid I/O type "
843219089Spjd				    "'%s': must be 'read', 'write', 'free', "
844219089Spjd				    "'claim' or 'all'\n", optarg);
845219089Spjd				usage();
846219089Spjd				return (1);
847219089Spjd			}
848219089Spjd			break;
849185029Spjd		case 't':
850185029Spjd			if ((type = name_to_type(optarg)) == TYPE_INVAL &&
851185029Spjd			    !MOS_TYPE(type)) {
852185029Spjd				(void) fprintf(stderr, "invalid type '%s'\n",
853185029Spjd				    optarg);
854185029Spjd				usage();
855185029Spjd				return (1);
856185029Spjd			}
857185029Spjd			break;
858185029Spjd		case 'u':
859185029Spjd			flags |= ZINJECT_UNLOAD_SPA;
860185029Spjd			break;
861185029Spjd		case 'L':
862185029Spjd			if ((label = name_to_type(optarg)) == TYPE_INVAL &&
863185029Spjd			    !LABEL_TYPE(type)) {
864185029Spjd				(void) fprintf(stderr, "invalid label type "
865185029Spjd				    "'%s'\n", optarg);
866185029Spjd				usage();
867185029Spjd				return (1);
868185029Spjd			}
869185029Spjd			break;
870185029Spjd		case ':':
871185029Spjd			(void) fprintf(stderr, "option -%c requires an "
872185029Spjd			    "operand\n", optopt);
873185029Spjd			usage();
874185029Spjd			return (1);
875185029Spjd		case '?':
876185029Spjd			(void) fprintf(stderr, "invalid option '%c'\n",
877185029Spjd			    optopt);
878185029Spjd			usage();
879185029Spjd			return (2);
880185029Spjd		}
881185029Spjd	}
882185029Spjd
883185029Spjd	argc -= optind;
884185029Spjd	argv += optind;
885185029Spjd
886247265Smm	if (record.zi_duration != 0)
887247265Smm		record.zi_cmd = ZINJECT_IGNORED_WRITES;
888247265Smm
889185029Spjd	if (cancel != NULL) {
890185029Spjd		/*
891185029Spjd		 * '-c' is invalid with any other options.
892185029Spjd		 */
893185029Spjd		if (raw != NULL || range != NULL || type != TYPE_INVAL ||
894247265Smm		    level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED) {
895185029Spjd			(void) fprintf(stderr, "cancel (-c) incompatible with "
896185029Spjd			    "any other options\n");
897185029Spjd			usage();
898185029Spjd			return (2);
899185029Spjd		}
900185029Spjd		if (argc != 0) {
901185029Spjd			(void) fprintf(stderr, "extraneous argument to '-c'\n");
902185029Spjd			usage();
903185029Spjd			return (2);
904185029Spjd		}
905185029Spjd
906185029Spjd		if (strcmp(cancel, "all") == 0) {
907185029Spjd			return (cancel_all_handlers());
908185029Spjd		} else {
909185029Spjd			int id = (int)strtol(cancel, &end, 10);
910185029Spjd			if (*end != '\0') {
911185029Spjd				(void) fprintf(stderr, "invalid handle id '%s':"
912185029Spjd				    " must be an integer or 'all'\n", cancel);
913185029Spjd				usage();
914185029Spjd				return (1);
915185029Spjd			}
916185029Spjd			return (cancel_handler(id));
917185029Spjd		}
918185029Spjd	}
919185029Spjd
920185029Spjd	if (device != NULL) {
921185029Spjd		/*
922185029Spjd		 * Device (-d) injection uses a completely different mechanism
923185029Spjd		 * for doing injection, so handle it separately here.
924185029Spjd		 */
925185029Spjd		if (raw != NULL || range != NULL || type != TYPE_INVAL ||
926247265Smm		    level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED) {
927185029Spjd			(void) fprintf(stderr, "device (-d) incompatible with "
928185029Spjd			    "data error injection\n");
929185029Spjd			usage();
930185029Spjd			return (2);
931185029Spjd		}
932185029Spjd
933185029Spjd		if (argc != 1) {
934185029Spjd			(void) fprintf(stderr, "device (-d) injection requires "
935185029Spjd			    "a single pool name\n");
936185029Spjd			usage();
937185029Spjd			return (2);
938185029Spjd		}
939185029Spjd
940185029Spjd		(void) strcpy(pool, argv[0]);
941185029Spjd		dataset[0] = '\0';
942185029Spjd
943185029Spjd		if (error == ECKSUM) {
944185029Spjd			(void) fprintf(stderr, "device error type must be "
945185029Spjd			    "'io' or 'nxio'\n");
946185029Spjd			return (1);
947185029Spjd		}
948185029Spjd
949219089Spjd		record.zi_iotype = io_type;
950185029Spjd		if (translate_device(pool, device, label, &record) != 0)
951185029Spjd			return (1);
952185029Spjd		if (!error)
953185029Spjd			error = ENXIO;
954219089Spjd
955219089Spjd		if (action != VDEV_STATE_UNKNOWN)
956219089Spjd			return (perform_action(pool, &record, action));
957219089Spjd
958185029Spjd	} else if (raw != NULL) {
959219089Spjd		if (range != NULL || type != TYPE_INVAL || level != 0 ||
960247265Smm		    record.zi_cmd != ZINJECT_UNINITIALIZED) {
961185029Spjd			(void) fprintf(stderr, "raw (-b) format with "
962185029Spjd			    "any other options\n");
963185029Spjd			usage();
964185029Spjd			return (2);
965185029Spjd		}
966185029Spjd
967185029Spjd		if (argc != 1) {
968185029Spjd			(void) fprintf(stderr, "raw (-b) format expects a "
969185029Spjd			    "single pool name\n");
970185029Spjd			usage();
971185029Spjd			return (2);
972185029Spjd		}
973185029Spjd
974185029Spjd		(void) strcpy(pool, argv[0]);
975185029Spjd		dataset[0] = '\0';
976185029Spjd
977185029Spjd		if (error == ENXIO) {
978185029Spjd			(void) fprintf(stderr, "data error type must be "
979185029Spjd			    "'checksum' or 'io'\n");
980185029Spjd			return (1);
981185029Spjd		}
982185029Spjd
983247265Smm		record.zi_cmd = ZINJECT_DATA_FAULT;
984185029Spjd		if (translate_raw(raw, &record) != 0)
985185029Spjd			return (1);
986185029Spjd		if (!error)
987185029Spjd			error = EIO;
988247265Smm	} else if (record.zi_cmd == ZINJECT_PANIC) {
989219089Spjd		if (raw != NULL || range != NULL || type != TYPE_INVAL ||
990247265Smm		    level != 0 || device != NULL) {
991219089Spjd			(void) fprintf(stderr, "panic (-p) incompatible with "
992219089Spjd			    "other options\n");
993219089Spjd			usage();
994219089Spjd			return (2);
995219089Spjd		}
996219089Spjd
997219089Spjd		if (argc < 1 || argc > 2) {
998219089Spjd			(void) fprintf(stderr, "panic (-p) injection requires "
999219089Spjd			    "a single pool name and an optional id\n");
1000219089Spjd			usage();
1001219089Spjd			return (2);
1002219089Spjd		}
1003219089Spjd
1004219089Spjd		(void) strcpy(pool, argv[0]);
1005219089Spjd		if (argv[1] != NULL)
1006219089Spjd			record.zi_type = atoi(argv[1]);
1007219089Spjd		dataset[0] = '\0';
1008247265Smm	} else if (record.zi_cmd == ZINJECT_IGNORED_WRITES) {
1009219089Spjd		if (nowrites == 0) {
1010219089Spjd			(void) fprintf(stderr, "-s or -g meaningless "
1011219089Spjd			    "without -I (ignore writes)\n");
1012219089Spjd			usage();
1013219089Spjd			return (2);
1014219089Spjd		} else if (dur_secs && dur_txg) {
1015219089Spjd			(void) fprintf(stderr, "choose a duration either "
1016219089Spjd			    "in seconds (-s) or a number of txgs (-g) "
1017219089Spjd			    "but not both\n");
1018219089Spjd			usage();
1019219089Spjd			return (2);
1020219089Spjd		} else if (argc != 1) {
1021219089Spjd			(void) fprintf(stderr, "ignore writes (-I) "
1022219089Spjd			    "injection requires a single pool name\n");
1023219089Spjd			usage();
1024219089Spjd			return (2);
1025219089Spjd		}
1026219089Spjd
1027219089Spjd		(void) strcpy(pool, argv[0]);
1028219089Spjd		dataset[0] = '\0';
1029185029Spjd	} else if (type == TYPE_INVAL) {
1030185029Spjd		if (flags == 0) {
1031185029Spjd			(void) fprintf(stderr, "at least one of '-b', '-d', "
1032219089Spjd			    "'-t', '-a', '-p', '-I' or '-u' "
1033219089Spjd			    "must be specified\n");
1034185029Spjd			usage();
1035185029Spjd			return (2);
1036185029Spjd		}
1037185029Spjd
1038185029Spjd		if (argc == 1 && (flags & ZINJECT_UNLOAD_SPA)) {
1039185029Spjd			(void) strcpy(pool, argv[0]);
1040185029Spjd			dataset[0] = '\0';
1041185029Spjd		} else if (argc != 0) {
1042185029Spjd			(void) fprintf(stderr, "extraneous argument for "
1043185029Spjd			    "'-f'\n");
1044185029Spjd			usage();
1045185029Spjd			return (2);
1046185029Spjd		}
1047185029Spjd
1048185029Spjd		flags |= ZINJECT_NULL;
1049185029Spjd	} else {
1050185029Spjd		if (argc != 1) {
1051185029Spjd			(void) fprintf(stderr, "missing object\n");
1052185029Spjd			usage();
1053185029Spjd			return (2);
1054185029Spjd		}
1055185029Spjd
1056185029Spjd		if (error == ENXIO) {
1057185029Spjd			(void) fprintf(stderr, "data error type must be "
1058185029Spjd			    "'checksum' or 'io'\n");
1059185029Spjd			return (1);
1060185029Spjd		}
1061185029Spjd
1062247265Smm		record.zi_cmd = ZINJECT_DATA_FAULT;
1063185029Spjd		if (translate_record(type, argv[0], range, level, &record, pool,
1064185029Spjd		    dataset) != 0)
1065185029Spjd			return (1);
1066185029Spjd		if (!error)
1067185029Spjd			error = EIO;
1068185029Spjd	}
1069185029Spjd
1070185029Spjd	/*
1071185029Spjd	 * If this is pool-wide metadata, unmount everything.  The ioctl() will
1072185029Spjd	 * unload the pool, so that we trigger spa-wide reopen of metadata next
1073185029Spjd	 * time we access the pool.
1074185029Spjd	 */
1075185029Spjd	if (dataset[0] != '\0' && domount) {
1076185029Spjd		if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
1077185029Spjd			return (1);
1078185029Spjd
1079185029Spjd		if (zfs_unmount(zhp, NULL, 0) != 0)
1080185029Spjd			return (1);
1081185029Spjd	}
1082185029Spjd
1083185029Spjd	record.zi_error = error;
1084185029Spjd
1085185029Spjd	ret = register_handler(pool, flags, &record, quiet);
1086185029Spjd
1087185029Spjd	if (dataset[0] != '\0' && domount)
1088185029Spjd		ret = (zfs_mount(zhp, NULL, 0) != 0);
1089185029Spjd
1090185029Spjd	libzfs_fini(g_zfs);
1091185029Spjd
1092185029Spjd	return (ret);
1093185029Spjd}
1094