xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_inject.c (revision 564eeb7d65973f1d64a5390ff477b689707e9daf)
1*564eeb7dSRobert Mustacchi /*
2*564eeb7dSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*564eeb7dSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*564eeb7dSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*564eeb7dSRobert Mustacchi  * 1.0 of the CDDL.
6*564eeb7dSRobert Mustacchi  *
7*564eeb7dSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*564eeb7dSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*564eeb7dSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*564eeb7dSRobert Mustacchi  */
11*564eeb7dSRobert Mustacchi 
12*564eeb7dSRobert Mustacchi /*
13*564eeb7dSRobert Mustacchi  * Copyright 2026 Oxide Computer Company
14*564eeb7dSRobert Mustacchi  */
15*564eeb7dSRobert Mustacchi 
16*564eeb7dSRobert Mustacchi /*
17*564eeb7dSRobert Mustacchi  * Commands and logic to support error injection.
18*564eeb7dSRobert Mustacchi  */
19*564eeb7dSRobert Mustacchi 
20*564eeb7dSRobert Mustacchi #include <err.h>
21*564eeb7dSRobert Mustacchi #include <stdio.h>
22*564eeb7dSRobert Mustacchi #include <stdlib.h>
23*564eeb7dSRobert Mustacchi #include <strings.h>
24*564eeb7dSRobert Mustacchi #include <sys/sysmacros.h>
25*564eeb7dSRobert Mustacchi #include <sys/nvme/ocp.h>
26*564eeb7dSRobert Mustacchi 
27*564eeb7dSRobert Mustacchi #include "nvmeadm.h"
28*564eeb7dSRobert Mustacchi 
29*564eeb7dSRobert Mustacchi typedef struct {
30*564eeb7dSRobert Mustacchi 	uint32_t ni_lat;
31*564eeb7dSRobert Mustacchi 	uint32_t ni_nread;
32*564eeb7dSRobert Mustacchi } nvmeadm_inject_t;
33*564eeb7dSRobert Mustacchi 
34*564eeb7dSRobert Mustacchi typedef struct {
35*564eeb7dSRobert Mustacchi 	const char *map_name;
36*564eeb7dSRobert Mustacchi 	ocp_errinj_type_t map_type;
37*564eeb7dSRobert Mustacchi } ocp_err_map_t;
38*564eeb7dSRobert Mustacchi 
39*564eeb7dSRobert Mustacchi static const ocp_err_map_t ocp_err_map[] = {
40*564eeb7dSRobert Mustacchi 	{ "cpu-hang", OCP_ERRINJ_T_CPU_HANG },
41*564eeb7dSRobert Mustacchi 	{ "nand-hang", OCP_ERRINJ_T_NAND_HANG },
42*564eeb7dSRobert Mustacchi 	{ "plp-defect", OCP_ERRINJ_T_PLP_DEFECT },
43*564eeb7dSRobert Mustacchi 	{ "fw-error", OCP_ERRINJ_T_FW_ERROR },
44*564eeb7dSRobert Mustacchi 	{ "dram-corrupt-crit", OCP_ERRINJ_T_DRAM_CORRUPT_CRIT },
45*564eeb7dSRobert Mustacchi 	{ "dram-corrupt-noncrit", OCP_ERRINJ_T_DRAM_CORRUPT_NONCRIT },
46*564eeb7dSRobert Mustacchi 	{ "nand-corrupt", OCP_ERRINJ_T_NAND_CORRUPT },
47*564eeb7dSRobert Mustacchi 	{ "sram-corrupt", OCP_ERRINJ_T_SRAM_CORRUPT },
48*564eeb7dSRobert Mustacchi 	{ "hw-malfunction", OCP_ERRINJ_T_HW_MALFUNC },
49*564eeb7dSRobert Mustacchi 	{ "no-spare-nand", OCP_ERRINJ_T_NO_SPARE_NAND },
50*564eeb7dSRobert Mustacchi 	{ "incomplete-shutdown", OCP_ERRINJ_T_INCOMPLETE_SHUTDOWN },
51*564eeb7dSRobert Mustacchi 	{ "metadata-corrupt", OCP_ERRINJ_T_METADATA_CORRUPT },
52*564eeb7dSRobert Mustacchi 	{ "critical-gc", OCP_ERRINJ_T_CRIT_GC },
53*564eeb7dSRobert Mustacchi 	{ "latency-spike", OCP_ERRINJ_T_LATENCY_SPIKE },
54*564eeb7dSRobert Mustacchi 	{ "io-failure", OCP_ERRINJ_T_IO_FAIL },
55*564eeb7dSRobert Mustacchi 	{ "io-timeout", OCP_ERRINJ_T_IO_TIMEOUT },
56*564eeb7dSRobert Mustacchi 	{ "admin-failure", OCP_ERRINJ_T_ADMIN_FAIL },
57*564eeb7dSRobert Mustacchi 	{ "admin-timeout", OCP_ERRINJ_T_ADMIN_TIMEOUT },
58*564eeb7dSRobert Mustacchi 	{ "thermal-throttle-start", OCP_ERRINJ_T_THERM_THROT_EN },
59*564eeb7dSRobert Mustacchi 	{ "thermal-throttle-end", OCP_ERRINJ_T_THERM_THROT_DIS },
60*564eeb7dSRobert Mustacchi 	{ "crit-temp", OCP_ERRINJ_T_THERM_CRIT_TEMP },
61*564eeb7dSRobert Mustacchi 	{ "nand-offline", OCP_ERRINJ_T_DIE_OFFLINE },
62*564eeb7dSRobert Mustacchi 	{ "sanitize-cmd-failure", OCP_ERRINJ_T_SANITIZE_CMD_FAIL },
63*564eeb7dSRobert Mustacchi 	{ "require-user-data-erase", OCP_ERRINJ_T_USER_DATA_ERASE },
64*564eeb7dSRobert Mustacchi 	{ "pcie-correctable", OCP_ERRINJ_T_PCIE_CE },
65*564eeb7dSRobert Mustacchi 	{ "pcie-uncorrectable", OCP_ERRINJ_T_PCIE_UE },
66*564eeb7dSRobert Mustacchi 	{ "clear-pel", OCP_ERRINJ_T_PEL_CLEAR },
67*564eeb7dSRobert Mustacchi 	{ "sanitize-op-failure", OCP_ERRINJ_T_SANITIZE_OP_FAIL },
68*564eeb7dSRobert Mustacchi 	{ "random-io-spikes", OCP_ERRINJ_T_RANDOM_LATENCY },
69*564eeb7dSRobert Mustacchi 	{ "factory-reset", OCP_ERRINJ_T_FACTORY_RESET },
70*564eeb7dSRobert Mustacchi 	{ "voltage-drop", OCP_ERRINJ_T_VOLTAGE_DROP },
71*564eeb7dSRobert Mustacchi 	{ "available-spare", OCP_ERRINJ_T_AVAIL_SPARE },
72*564eeb7dSRobert Mustacchi 	{ "volatile-memory-backup", OCP_ERRINJ_T_VOL_MEM_BACKUP },
73*564eeb7dSRobert Mustacchi };
74*564eeb7dSRobert Mustacchi 
75*564eeb7dSRobert Mustacchi const char *
nvmeadm_ocp_errinj_type_to_str(uint32_t val)76*564eeb7dSRobert Mustacchi nvmeadm_ocp_errinj_type_to_str(uint32_t val)
77*564eeb7dSRobert Mustacchi {
78*564eeb7dSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(ocp_err_map); i++) {
79*564eeb7dSRobert Mustacchi 		if (ocp_err_map[i].map_type == val) {
80*564eeb7dSRobert Mustacchi 			return (ocp_err_map[i].map_name);
81*564eeb7dSRobert Mustacchi 		}
82*564eeb7dSRobert Mustacchi 	}
83*564eeb7dSRobert Mustacchi 
84*564eeb7dSRobert Mustacchi 	return ("unknown");
85*564eeb7dSRobert Mustacchi }
86*564eeb7dSRobert Mustacchi 
87*564eeb7dSRobert Mustacchi void
usage_ocp_errinj(const char * c_name)88*564eeb7dSRobert Mustacchi usage_ocp_errinj(const char *c_name)
89*564eeb7dSRobert Mustacchi {
90*564eeb7dSRobert Mustacchi 
91*564eeb7dSRobert Mustacchi 	(void) fprintf(stderr, "%s [-l lat_us] [-n nreads] <ctl> <error>\n\n",
92*564eeb7dSRobert Mustacchi 	    c_name);
93*564eeb7dSRobert Mustacchi 	(void) fprintf(stderr, "  inject a specific error or event on a "
94*564eeb7dSRobert Mustacchi 	    "controller. This may disrupt or degrade\ndevice operation.\n");
95*564eeb7dSRobert Mustacchi }
96*564eeb7dSRobert Mustacchi 
97*564eeb7dSRobert Mustacchi void
optparse_ocp_errinj(nvme_process_arg_t * npa)98*564eeb7dSRobert Mustacchi optparse_ocp_errinj(nvme_process_arg_t *npa)
99*564eeb7dSRobert Mustacchi {
100*564eeb7dSRobert Mustacchi 	int c;
101*564eeb7dSRobert Mustacchi 	nvmeadm_inject_t *inject;
102*564eeb7dSRobert Mustacchi 
103*564eeb7dSRobert Mustacchi 	if ((inject = calloc(1, sizeof (nvmeadm_inject_t))) == NULL) {
104*564eeb7dSRobert Mustacchi 		err(-1, "failed to allocate memory for option tracking");
105*564eeb7dSRobert Mustacchi 	}
106*564eeb7dSRobert Mustacchi 
107*564eeb7dSRobert Mustacchi 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":l:n:")) != -1) {
108*564eeb7dSRobert Mustacchi 		switch (c) {
109*564eeb7dSRobert Mustacchi 		case 'l':
110*564eeb7dSRobert Mustacchi 			inject->ni_lat = (uint32_t)optparse_ui_range(optarg,
111*564eeb7dSRobert Mustacchi 			    "lat_us", 0, UINT32_MAX);
112*564eeb7dSRobert Mustacchi 			break;
113*564eeb7dSRobert Mustacchi 		case 'n':
114*564eeb7dSRobert Mustacchi 			inject->ni_nread = (uint32_t)optparse_ui_range(optarg,
115*564eeb7dSRobert Mustacchi 			    "nread", 0, UINT32_MAX);
116*564eeb7dSRobert Mustacchi 			break;
117*564eeb7dSRobert Mustacchi 		case '?':
118*564eeb7dSRobert Mustacchi 			errx(-1, "unknown option: -%c", optopt);
119*564eeb7dSRobert Mustacchi 		case ':':
120*564eeb7dSRobert Mustacchi 			errx(-1, "option -%c requires an argument", optopt);
121*564eeb7dSRobert Mustacchi 		}
122*564eeb7dSRobert Mustacchi 	}
123*564eeb7dSRobert Mustacchi 
124*564eeb7dSRobert Mustacchi 	npa->npa_cmd_arg = inject;
125*564eeb7dSRobert Mustacchi }
126*564eeb7dSRobert Mustacchi 
127*564eeb7dSRobert Mustacchi int
do_ocp_errinj(const nvme_process_arg_t * npa)128*564eeb7dSRobert Mustacchi do_ocp_errinj(const nvme_process_arg_t *npa)
129*564eeb7dSRobert Mustacchi {
130*564eeb7dSRobert Mustacchi 	const char *event;
131*564eeb7dSRobert Mustacchi 	const nvmeadm_inject_t *inject = npa->npa_cmd_arg;
132*564eeb7dSRobert Mustacchi 	nvme_ocp_errinj_req_t *req;
133*564eeb7dSRobert Mustacchi 
134*564eeb7dSRobert Mustacchi 	if (npa->npa_argc < 1) {
135*564eeb7dSRobert Mustacchi 		errx(-1, "missing required error to inject");
136*564eeb7dSRobert Mustacchi 	} else if (npa->npa_argc > 1) {
137*564eeb7dSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
138*564eeb7dSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[1]);
139*564eeb7dSRobert Mustacchi 	}
140*564eeb7dSRobert Mustacchi 	event = npa->npa_argv[0];
141*564eeb7dSRobert Mustacchi 
142*564eeb7dSRobert Mustacchi 	if (npa->npa_ns != NULL) {
143*564eeb7dSRobert Mustacchi 		errx(-1, "error injection may only be performed on a "
144*564eeb7dSRobert Mustacchi 		    "controller, not a namespace");
145*564eeb7dSRobert Mustacchi 	}
146*564eeb7dSRobert Mustacchi 
147*564eeb7dSRobert Mustacchi 	/*
148*564eeb7dSRobert Mustacchi 	 * First try to parse by name. Otherwise try to parse this as an
149*564eeb7dSRobert Mustacchi 	 * integer.
150*564eeb7dSRobert Mustacchi 	 */
151*564eeb7dSRobert Mustacchi 	uint32_t type = UINT32_MAX;
152*564eeb7dSRobert Mustacchi 	if (strcasecmp(event, "clear-errors") == 0) {
153*564eeb7dSRobert Mustacchi 		if (inject->ni_lat != 0) {
154*564eeb7dSRobert Mustacchi 			errx(-1, "-l may not be specified when clearing "
155*564eeb7dSRobert Mustacchi 			    "errors");
156*564eeb7dSRobert Mustacchi 		}
157*564eeb7dSRobert Mustacchi 
158*564eeb7dSRobert Mustacchi 		if (inject->ni_nread != 0) {
159*564eeb7dSRobert Mustacchi 			errx(-1, "-n may not be specified when clearing "
160*564eeb7dSRobert Mustacchi 			    "errors");
161*564eeb7dSRobert Mustacchi 		}
162*564eeb7dSRobert Mustacchi 
163*564eeb7dSRobert Mustacchi 		if (!nvme_ocp_errinj_clear(npa->npa_ctrl)) {
164*564eeb7dSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to clear pending errors");
165*564eeb7dSRobert Mustacchi 		}
166*564eeb7dSRobert Mustacchi 		return (EXIT_SUCCESS);
167*564eeb7dSRobert Mustacchi 	}
168*564eeb7dSRobert Mustacchi 
169*564eeb7dSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(ocp_err_map); i++) {
170*564eeb7dSRobert Mustacchi 		if (strcasecmp(ocp_err_map[i].map_name, event) == 0) {
171*564eeb7dSRobert Mustacchi 			type = ocp_err_map[i].map_type;
172*564eeb7dSRobert Mustacchi 			break;
173*564eeb7dSRobert Mustacchi 		}
174*564eeb7dSRobert Mustacchi 	}
175*564eeb7dSRobert Mustacchi 
176*564eeb7dSRobert Mustacchi 	if (type == UINT32_MAX) {
177*564eeb7dSRobert Mustacchi 		const char *errstr;
178*564eeb7dSRobert Mustacchi 
179*564eeb7dSRobert Mustacchi 		type = (uint32_t)strtonumx(event, 0, UINT16_MAX, &errstr, 0);
180*564eeb7dSRobert Mustacchi 		if (errstr != NULL) {
181*564eeb7dSRobert Mustacchi 			errx(-1, "failed to parse %s as an error injection "
182*564eeb7dSRobert Mustacchi 			    "type", event);
183*564eeb7dSRobert Mustacchi 		}
184*564eeb7dSRobert Mustacchi 	}
185*564eeb7dSRobert Mustacchi 
186*564eeb7dSRobert Mustacchi 	/*
187*564eeb7dSRobert Mustacchi 	 * While we could check whether -l is allowed to be set based on the
188*564eeb7dSRobert Mustacchi 	 * event type, we instead opt to leave that to the library code for now.
189*564eeb7dSRobert Mustacchi 	 */
190*564eeb7dSRobert Mustacchi 
191*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_req_init(npa->npa_ctrl, &req) ||
192*564eeb7dSRobert Mustacchi 	    !nvme_ocp_errinj_req_set_type(req, type) ||
193*564eeb7dSRobert Mustacchi 	    (inject->ni_nread != 0 && !nvme_ocp_errinj_req_set_nrtde(req,
194*564eeb7dSRobert Mustacchi 	    inject->ni_nread)) ||
195*564eeb7dSRobert Mustacchi 	    (inject->ni_lat != 0 && !nvme_ocp_errinj_req_set_ld(req,
196*564eeb7dSRobert Mustacchi 	    inject->ni_lat))) {
197*564eeb7dSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to initialize error %s injection "
198*564eeb7dSRobert Mustacchi 		    "request", event);
199*564eeb7dSRobert Mustacchi 	}
200*564eeb7dSRobert Mustacchi 
201*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_req_exec(req)) {
202*564eeb7dSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to inject error %s", event);
203*564eeb7dSRobert Mustacchi 	}
204*564eeb7dSRobert Mustacchi 
205*564eeb7dSRobert Mustacchi 	nvme_ocp_errinj_req_fini(req);
206*564eeb7dSRobert Mustacchi 	return (EXIT_SUCCESS);
207*564eeb7dSRobert Mustacchi }
208