xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_wdc.c (revision 7a27a99a5b13d5aa203a174e41ab2d4fd5f6566c)
1533affcbSRobert Mustacchi /*
2533affcbSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3533affcbSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4533affcbSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5533affcbSRobert Mustacchi  * 1.0 of the CDDL.
6533affcbSRobert Mustacchi  *
7533affcbSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8533affcbSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9533affcbSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10533affcbSRobert Mustacchi  */
11533affcbSRobert Mustacchi 
12533affcbSRobert Mustacchi /*
13533affcbSRobert Mustacchi  * Copyright 2024 Oxide Computer Company
14533affcbSRobert Mustacchi  */
15533affcbSRobert Mustacchi 
16533affcbSRobert Mustacchi /*
17533affcbSRobert Mustacchi  * WDC vendor-specific commands
18533affcbSRobert Mustacchi  */
19533affcbSRobert Mustacchi 
20533affcbSRobert Mustacchi #include <err.h>
21533affcbSRobert Mustacchi #include <sys/types.h>
22533affcbSRobert Mustacchi #include <sys/stat.h>
23533affcbSRobert Mustacchi #include <fcntl.h>
24533affcbSRobert Mustacchi #include <unistd.h>
25533affcbSRobert Mustacchi #include <sys/sysmacros.h>
26533affcbSRobert Mustacchi #include <stdbool.h>
27533affcbSRobert Mustacchi #include <endian.h>
28533affcbSRobert Mustacchi #include <sys/nvme/wdc.h>
29533affcbSRobert Mustacchi 
30533affcbSRobert Mustacchi #include "nvmeadm.h"
31533affcbSRobert Mustacchi 
32533affcbSRobert Mustacchi /*
33533affcbSRobert Mustacchi  * This is the default chunk size that we'll read the e6 log in. This generally
34533affcbSRobert Mustacchi  * should fit within the maximum transfer size for a device. If we wanted to
35533affcbSRobert Mustacchi  * improve this, we could expose what the kernel's maximum transfer size is for
36533affcbSRobert Mustacchi  * a device and then use that as a larger upper bound. Currently the value is 64
37533affcbSRobert Mustacchi  * KiB.
38533affcbSRobert Mustacchi  */
39533affcbSRobert Mustacchi #define	E6_BUFSIZE	0x10000
40533affcbSRobert Mustacchi 
41533affcbSRobert Mustacchi typedef struct nvmeadm_e6_dump {
42533affcbSRobert Mustacchi 	const char *e6_output;
43533affcbSRobert Mustacchi } nvmeadm_e6_dump_t;
44533affcbSRobert Mustacchi 
45533affcbSRobert Mustacchi typedef struct nvmeadm_wdc_resize {
46533affcbSRobert Mustacchi 	bool wr_query;
47533affcbSRobert Mustacchi 	uint32_t wr_set;
48533affcbSRobert Mustacchi } nvmeadm_wdc_resize_t;
49533affcbSRobert Mustacchi 
50533affcbSRobert Mustacchi void
usage_wdc_e6dump(const char * c_name)51533affcbSRobert Mustacchi usage_wdc_e6dump(const char *c_name)
52533affcbSRobert Mustacchi {
53533affcbSRobert Mustacchi 	(void) fprintf(stderr, "%s -o output <ctl>\n\n"
54533affcbSRobert Mustacchi 	    "  Dump WDC e6 diagnostic log from a device.\n", c_name);
55533affcbSRobert Mustacchi }
56533affcbSRobert Mustacchi 
57533affcbSRobert Mustacchi void
optparse_wdc_e6dump(nvme_process_arg_t * npa)58533affcbSRobert Mustacchi optparse_wdc_e6dump(nvme_process_arg_t *npa)
59533affcbSRobert Mustacchi {
60533affcbSRobert Mustacchi 	int c;
61533affcbSRobert Mustacchi 	nvmeadm_e6_dump_t *e6;
62533affcbSRobert Mustacchi 
63533affcbSRobert Mustacchi 	if ((e6 = calloc(1, sizeof (nvmeadm_e6_dump_t))) == NULL) {
64533affcbSRobert Mustacchi 		err(-1, "failed to allocate memory for e6 options structure");
65533affcbSRobert Mustacchi 	}
66533affcbSRobert Mustacchi 
67533affcbSRobert Mustacchi 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":o:")) != -1) {
68533affcbSRobert Mustacchi 		switch (c) {
69533affcbSRobert Mustacchi 		case 'o':
70533affcbSRobert Mustacchi 			e6->e6_output = optarg;
71533affcbSRobert Mustacchi 			break;
72533affcbSRobert Mustacchi 		case '?':
73533affcbSRobert Mustacchi 			errx(-1, "unknown option: -%c", optopt);
74533affcbSRobert Mustacchi 		case ':':
75533affcbSRobert Mustacchi 			errx(-1, "option -%c requires an argument", optopt);
76533affcbSRobert Mustacchi 		}
77533affcbSRobert Mustacchi 	}
78533affcbSRobert Mustacchi 
79533affcbSRobert Mustacchi 	if (e6->e6_output == NULL) {
80533affcbSRobert Mustacchi 		errx(-1, "missing required e6dump output file, specify with "
81533affcbSRobert Mustacchi 		    "-o");
82533affcbSRobert Mustacchi 	}
83533affcbSRobert Mustacchi 
84533affcbSRobert Mustacchi 	npa->npa_cmd_arg = e6;
85533affcbSRobert Mustacchi }
86533affcbSRobert Mustacchi 
87533affcbSRobert Mustacchi static void
wdc_e6_read(const nvme_process_arg_t * npa,nvme_wdc_e6_req_t * req,uint64_t off,void * buf,size_t len)88533affcbSRobert Mustacchi wdc_e6_read(const nvme_process_arg_t *npa, nvme_wdc_e6_req_t *req,
89533affcbSRobert Mustacchi     uint64_t off, void *buf, size_t len)
90533affcbSRobert Mustacchi {
91533affcbSRobert Mustacchi 	if (!nvme_wdc_e6_req_set_offset(req, off)) {
92533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to set e6 request offset to 0x%"
93533affcbSRobert Mustacchi 		    PRIx64, off);
94533affcbSRobert Mustacchi 	}
95533affcbSRobert Mustacchi 
96533affcbSRobert Mustacchi 	if (!nvme_wdc_e6_req_set_output(req, buf, len)) {
97533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to set e6 request output buffer");
98533affcbSRobert Mustacchi 	}
99533affcbSRobert Mustacchi 
100533affcbSRobert Mustacchi 	if (!nvme_wdc_e6_req_exec(req)) {
101533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to issue e6 request for %zu bytes "
102533affcbSRobert Mustacchi 		    "at offset 0x%" PRIx64, len, off);
103533affcbSRobert Mustacchi 	}
104533affcbSRobert Mustacchi }
105533affcbSRobert Mustacchi 
106533affcbSRobert Mustacchi /*
107533affcbSRobert Mustacchi  * Write out e6 data to a file. Because our read from the device has already
108533affcbSRobert Mustacchi  * been constrained by size, we don't bother further chunking up the write out
109533affcbSRobert Mustacchi  * to a file.
110533affcbSRobert Mustacchi  */
111533affcbSRobert Mustacchi static void
wdc_e6_write(int fd,const void * buf,size_t len)112533affcbSRobert Mustacchi wdc_e6_write(int fd, const void *buf, size_t len)
113533affcbSRobert Mustacchi {
114533affcbSRobert Mustacchi 	size_t off = 0;
115533affcbSRobert Mustacchi 
116533affcbSRobert Mustacchi 	while (len > 0) {
117533affcbSRobert Mustacchi 		void *boff = (void *)((uintptr_t)buf + off);
118533affcbSRobert Mustacchi 		ssize_t ret = write(fd, boff, len);
119533affcbSRobert Mustacchi 		if (ret < 0) {
120533affcbSRobert Mustacchi 			/*
121533affcbSRobert Mustacchi 			 * We explicitly allow a signal that interrupts us to
122533affcbSRobert Mustacchi 			 * lead to a failure assuming someone has more likely
123533affcbSRobert Mustacchi 			 * than not issued a SIGINT or similar.
124533affcbSRobert Mustacchi 			 */
125533affcbSRobert Mustacchi 			err(-1, "failed to write e6 data to output file");
126533affcbSRobert Mustacchi 		}
127533affcbSRobert Mustacchi 
128533affcbSRobert Mustacchi 		len -= (size_t)ret;
129533affcbSRobert Mustacchi 		off += (size_t)ret;
130533affcbSRobert Mustacchi 	}
131533affcbSRobert Mustacchi }
132533affcbSRobert Mustacchi 
133533affcbSRobert Mustacchi int
do_wdc_e6dump(const nvme_process_arg_t * npa)134533affcbSRobert Mustacchi do_wdc_e6dump(const nvme_process_arg_t *npa)
135533affcbSRobert Mustacchi {
136533affcbSRobert Mustacchi 	int ofd;
137533affcbSRobert Mustacchi 	nvmeadm_e6_dump_t *e6 = npa->npa_cmd_arg;
138533affcbSRobert Mustacchi 	nvme_vuc_disc_t *vuc;
139533affcbSRobert Mustacchi 	void *buf;
140533affcbSRobert Mustacchi 	nvme_wdc_e6_req_t *req;
141533affcbSRobert Mustacchi 	const wdc_e6_header_t *header;
142533affcbSRobert Mustacchi 	uint64_t len, off;
143533affcbSRobert Mustacchi 
144533affcbSRobert Mustacchi 	vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);
145533affcbSRobert Mustacchi 
146533affcbSRobert Mustacchi 	ofd = open(e6->e6_output, O_RDWR | O_CREAT | O_TRUNC, 0644);
147533affcbSRobert Mustacchi 	if (ofd < 0) {
148533affcbSRobert Mustacchi 		err(-1, "failed to open file %s", e6->e6_output);
149533affcbSRobert Mustacchi 	}
150533affcbSRobert Mustacchi 
151533affcbSRobert Mustacchi 	if ((buf = calloc(1, E6_BUFSIZE)) == NULL) {
152533affcbSRobert Mustacchi 		err(-1, "failed to allocate 0x%x bytes for E6 transfer buffer",
153533affcbSRobert Mustacchi 		    E6_BUFSIZE);
154533affcbSRobert Mustacchi 	}
155533affcbSRobert Mustacchi 
156533affcbSRobert Mustacchi 	if (!nvme_wdc_e6_req_init(npa->npa_ctrl, &req)) {
157533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to initialize e6 request");
158533affcbSRobert Mustacchi 	}
159533affcbSRobert Mustacchi 
160533affcbSRobert Mustacchi 	/*
161533affcbSRobert Mustacchi 	 * Begin by reading the header to determine the actual size. Note, as
162533affcbSRobert Mustacchi 	 * far as we can tell, the size of the header is included in the size we
163533affcbSRobert Mustacchi 	 * get.
164533affcbSRobert Mustacchi 	 */
165533affcbSRobert Mustacchi 	wdc_e6_read(npa, req, 0, buf, sizeof (wdc_e6_header_t));
166533affcbSRobert Mustacchi 	header = buf;
167533affcbSRobert Mustacchi 	len = be32toh(header->e6_size_be);
168533affcbSRobert Mustacchi 
169533affcbSRobert Mustacchi 	if (len == UINT32_MAX) {
170533affcbSRobert Mustacchi 		errx(-1, "e6 header size 0x%" PRIx64 " looks like an invalid "
171533affcbSRobert Mustacchi 		    "PCI read, aborting", len);
172533affcbSRobert Mustacchi 	}
173533affcbSRobert Mustacchi 
174533affcbSRobert Mustacchi 	if ((len % 4) != 0) {
175533affcbSRobert Mustacchi 		warnx("e6 header size 0x%zx is not 4 byte aligned, but "
176533affcbSRobert Mustacchi 		    "firmware claims it always will be, rounding up", len);
177533affcbSRobert Mustacchi 		len = P2ROUNDUP(len, 4);
178533affcbSRobert Mustacchi 	}
179533affcbSRobert Mustacchi 
180533affcbSRobert Mustacchi 	if (len < sizeof (wdc_e6_header_t)) {
181533affcbSRobert Mustacchi 		errx(-1, "e6 header size is too small, 0x%zx bytes does not "
182533affcbSRobert Mustacchi 		    "even cover the header", len);
183533affcbSRobert Mustacchi 	}
184533affcbSRobert Mustacchi 	wdc_e6_write(ofd, buf, sizeof (wdc_e6_header_t));
185533affcbSRobert Mustacchi 
186533affcbSRobert Mustacchi 	/*
187533affcbSRobert Mustacchi 	 * Account for the fact that we already read the header.
188533affcbSRobert Mustacchi 	 */
189533affcbSRobert Mustacchi 	off = sizeof (wdc_e6_header_t);
190533affcbSRobert Mustacchi 	len -= off;
191533affcbSRobert Mustacchi 	while (len > 0) {
192533affcbSRobert Mustacchi 		uint32_t toread = MIN(len, E6_BUFSIZE);
193533affcbSRobert Mustacchi 		wdc_e6_read(npa, req, off, buf, toread);
194533affcbSRobert Mustacchi 		wdc_e6_write(ofd, buf, toread);
195533affcbSRobert Mustacchi 
196533affcbSRobert Mustacchi 		off += toread;
197533affcbSRobert Mustacchi 		len -= toread;
198533affcbSRobert Mustacchi 	}
199533affcbSRobert Mustacchi 
200533affcbSRobert Mustacchi 	nvme_wdc_e6_req_fini(req);
201533affcbSRobert Mustacchi 	VERIFY0(close(ofd));
202533affcbSRobert Mustacchi 	nvmeadm_vuc_fini(npa, vuc);
203533affcbSRobert Mustacchi 
204533affcbSRobert Mustacchi 	return (0);
205533affcbSRobert Mustacchi }
206533affcbSRobert Mustacchi 
207533affcbSRobert Mustacchi void
usage_wdc_resize(const char * c_name)208533affcbSRobert Mustacchi usage_wdc_resize(const char *c_name)
209533affcbSRobert Mustacchi {
210533affcbSRobert Mustacchi 	(void) fprintf(stderr, "%s -s size | -g <ctl>\n\n"
211533affcbSRobert Mustacchi 	    "  Resize a device to a new overall capacity in GB (not GiB) or "
212533affcbSRobert Mustacchi 	    "get its\n  current size. Resizing will cause all data and "
213533affcbSRobert Mustacchi 	    "namespaces to be lost.\n",
214533affcbSRobert Mustacchi 	    c_name);
215533affcbSRobert Mustacchi }
216533affcbSRobert Mustacchi 
217533affcbSRobert Mustacchi void
optparse_wdc_resize(nvme_process_arg_t * npa)218533affcbSRobert Mustacchi optparse_wdc_resize(nvme_process_arg_t *npa)
219533affcbSRobert Mustacchi {
220533affcbSRobert Mustacchi 	int c;
221533affcbSRobert Mustacchi 	nvmeadm_wdc_resize_t *resize;
222533affcbSRobert Mustacchi 
223533affcbSRobert Mustacchi 	if ((resize = calloc(1, sizeof (nvmeadm_wdc_resize_t))) == NULL) {
224533affcbSRobert Mustacchi 		err(-1, "failed to allocate memory for resize options "
225533affcbSRobert Mustacchi 		    "structure");
226533affcbSRobert Mustacchi 	}
227533affcbSRobert Mustacchi 
228533affcbSRobert Mustacchi 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":gs:")) != -1) {
229533affcbSRobert Mustacchi 		const char *err;
230533affcbSRobert Mustacchi 
231533affcbSRobert Mustacchi 		switch (c) {
232533affcbSRobert Mustacchi 		case 'g':
233533affcbSRobert Mustacchi 			resize->wr_query = true;
234533affcbSRobert Mustacchi 			break;
235533affcbSRobert Mustacchi 		case 's':
236533affcbSRobert Mustacchi 			/*
237533affcbSRobert Mustacchi 			 * The size to set is in GB (not GiB). While WDC
238533affcbSRobert Mustacchi 			 * recommends specific size points depending on the
239533affcbSRobert Mustacchi 			 * drives initial capacity, we allow the user to set
240533affcbSRobert Mustacchi 			 * what they expect and will allow the command to
241533affcbSRobert Mustacchi 			 * succeed or fail as per the controller's whims. It
242533affcbSRobert Mustacchi 			 * would be better if we looked at the device and
243533affcbSRobert Mustacchi 			 * determined its underlying capacity and figured out
244533affcbSRobert Mustacchi 			 * what points made sense, but it's not clear on the
245533affcbSRobert Mustacchi 			 * best way to do that across a few different
246533affcbSRobert Mustacchi 			 * generations of WDC products.
247533affcbSRobert Mustacchi 			 */
248533affcbSRobert Mustacchi 			resize->wr_set = (uint32_t)strtonumx(optarg, 1,
249533affcbSRobert Mustacchi 			    UINT16_MAX, &err, 0);
250533affcbSRobert Mustacchi 			if (err != NULL) {
251533affcbSRobert Mustacchi 				errx(-1, "failed to parse resize size %s:"
252533affcbSRobert Mustacchi 				    "value is %s", optarg, err);
253533affcbSRobert Mustacchi 			}
254533affcbSRobert Mustacchi 			break;
255533affcbSRobert Mustacchi 		case '?':
256533affcbSRobert Mustacchi 			errx(-1, "unknown option: -%c", optopt);
257533affcbSRobert Mustacchi 		case ':':
258533affcbSRobert Mustacchi 			errx(-1, "option -%c requires an argument", optopt);
259533affcbSRobert Mustacchi 		}
260533affcbSRobert Mustacchi 	}
261533affcbSRobert Mustacchi 
262533affcbSRobert Mustacchi 	if (resize->wr_query && resize->wr_set != 0) {
263533affcbSRobert Mustacchi 		errx(-1, "only one of -g and -s may be specified");
264533affcbSRobert Mustacchi 	}
265533affcbSRobert Mustacchi 
266533affcbSRobert Mustacchi 	if (!resize->wr_query && resize->wr_set == 0) {
267533affcbSRobert Mustacchi 		errx(-1, "one of -g and -s must be specified");
268533affcbSRobert Mustacchi 	}
269533affcbSRobert Mustacchi 
270533affcbSRobert Mustacchi 	npa->npa_cmd_arg = resize;
271533affcbSRobert Mustacchi }
272533affcbSRobert Mustacchi 
273533affcbSRobert Mustacchi int
do_wdc_resize(const nvme_process_arg_t * npa)274533affcbSRobert Mustacchi do_wdc_resize(const nvme_process_arg_t *npa)
275533affcbSRobert Mustacchi {
276533affcbSRobert Mustacchi 	nvmeadm_wdc_resize_t *resize = npa->npa_cmd_arg;
277533affcbSRobert Mustacchi 	nvme_vuc_disc_t *vuc;
278533affcbSRobert Mustacchi 
279533affcbSRobert Mustacchi 	vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);
280533affcbSRobert Mustacchi 
281533affcbSRobert Mustacchi 	/*
282533affcbSRobert Mustacchi 	 * The VUC for this generally recommends exclusive access. If this
283533affcbSRobert Mustacchi 	 * becomes problematic for folks issuing this query, then we should
284533affcbSRobert Mustacchi 	 * break the query into a separate VUC entry that we should discover
285533affcbSRobert Mustacchi 	 * instead.
286533affcbSRobert Mustacchi 	 */
287533affcbSRobert Mustacchi 	if (resize->wr_query) {
288533affcbSRobert Mustacchi 		uint32_t val;
289533affcbSRobert Mustacchi 
290533affcbSRobert Mustacchi 		if (!nvme_wdc_resize_get(npa->npa_ctrl, &val)) {
291533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to query current WDC "
292533affcbSRobert Mustacchi 			    "device capacity");
293533affcbSRobert Mustacchi 		}
294533affcbSRobert Mustacchi 
295533affcbSRobert Mustacchi 		(void) printf("%u\n", val);
296533affcbSRobert Mustacchi 		nvmeadm_vuc_fini(npa, vuc);
297533affcbSRobert Mustacchi 		return (0);
298533affcbSRobert Mustacchi 	}
299533affcbSRobert Mustacchi 
300533affcbSRobert Mustacchi 	if (!nvme_wdc_resize_set(npa->npa_ctrl, resize->wr_set)) {
301533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to resize device to %u",
302533affcbSRobert Mustacchi 		    resize->wr_set);
303533affcbSRobert Mustacchi 	}
304533affcbSRobert Mustacchi 
305533affcbSRobert Mustacchi 	(void) printf("%s resized to %u GB\n", npa->npa_name, resize->wr_set);
306533affcbSRobert Mustacchi 	nvmeadm_vuc_fini(npa, vuc);
307533affcbSRobert Mustacchi 
308533affcbSRobert Mustacchi 	return (0);
309533affcbSRobert Mustacchi }
310*7a27a99aSRobert Mustacchi 
311*7a27a99aSRobert Mustacchi int
do_wdc_inject_assert(const nvme_process_arg_t * npa)312*7a27a99aSRobert Mustacchi do_wdc_inject_assert(const nvme_process_arg_t *npa)
313*7a27a99aSRobert Mustacchi {
314*7a27a99aSRobert Mustacchi 	nvme_vuc_disc_t *vuc;
315*7a27a99aSRobert Mustacchi 
316*7a27a99aSRobert Mustacchi 	if (npa->npa_argc > 0) {
317*7a27a99aSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
318*7a27a99aSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
319*7a27a99aSRobert Mustacchi 	}
320*7a27a99aSRobert Mustacchi 
321*7a27a99aSRobert Mustacchi 	vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);
322*7a27a99aSRobert Mustacchi 
323*7a27a99aSRobert Mustacchi 	if (!nvme_wdc_assert_inject(npa->npa_ctrl)) {
324*7a27a99aSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to inject assertion");
325*7a27a99aSRobert Mustacchi 	}
326*7a27a99aSRobert Mustacchi 
327*7a27a99aSRobert Mustacchi 	nvmeadm_vuc_fini(npa, vuc);
328*7a27a99aSRobert Mustacchi 	return (0);
329*7a27a99aSRobert Mustacchi }
330*7a27a99aSRobert Mustacchi 
331*7a27a99aSRobert Mustacchi int
do_wdc_clear_assert(const nvme_process_arg_t * npa)332*7a27a99aSRobert Mustacchi do_wdc_clear_assert(const nvme_process_arg_t *npa)
333*7a27a99aSRobert Mustacchi {
334*7a27a99aSRobert Mustacchi 	nvme_vuc_disc_t *vuc;
335*7a27a99aSRobert Mustacchi 
336*7a27a99aSRobert Mustacchi 	if (npa->npa_argc > 0) {
337*7a27a99aSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
338*7a27a99aSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
339*7a27a99aSRobert Mustacchi 	}
340*7a27a99aSRobert Mustacchi 
341*7a27a99aSRobert Mustacchi 	vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);
342*7a27a99aSRobert Mustacchi 
343*7a27a99aSRobert Mustacchi 	if (!nvme_wdc_assert_clear(npa->npa_ctrl)) {
344*7a27a99aSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to clear assertion");
345*7a27a99aSRobert Mustacchi 	}
346*7a27a99aSRobert Mustacchi 
347*7a27a99aSRobert Mustacchi 	nvmeadm_vuc_fini(npa, vuc);
348*7a27a99aSRobert Mustacchi 	return (0);
349*7a27a99aSRobert Mustacchi }
350*7a27a99aSRobert Mustacchi 
351*7a27a99aSRobert Mustacchi void
usage_wdc_clear_assert(const char * c_name)352*7a27a99aSRobert Mustacchi usage_wdc_clear_assert(const char *c_name)
353*7a27a99aSRobert Mustacchi {
354*7a27a99aSRobert Mustacchi 	(void) fprintf(stderr, "%s <ctl>\n\n"
355*7a27a99aSRobert Mustacchi 	    "  Clear an internal device assertion.\n", c_name);
356*7a27a99aSRobert Mustacchi }
357*7a27a99aSRobert Mustacchi 
358*7a27a99aSRobert Mustacchi void
usage_wdc_inject_assert(const char * c_name)359*7a27a99aSRobert Mustacchi usage_wdc_inject_assert(const char *c_name)
360*7a27a99aSRobert Mustacchi {
361*7a27a99aSRobert Mustacchi 	(void) fprintf(stderr, "%s <ctl>\n\n"
362*7a27a99aSRobert Mustacchi 	    "  Inject a device assertion. This will cause the device to "
363*7a27a99aSRobert Mustacchi 	    "pause\n  execution of commands and create an internal fault. This "
364*7a27a99aSRobert Mustacchi 	    "should\n  not be used unless directed as part of a "
365*7a27a99aSRobert Mustacchi 	    "troubleshooting exercise.\n  If in doubt, do not use this!\n",
366*7a27a99aSRobert Mustacchi 	    c_name);
367*7a27a99aSRobert Mustacchi }
368