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