xref: /titanic_52/usr/src/cmd/nvmeadm/nvmeadm.c (revision a856e1a641dd665e1fb6704573f2fb39e1b922b0)
1ecee5a1fSHans Rosenfeld /*
2ecee5a1fSHans Rosenfeld  * This file and its contents are supplied under the terms of the
3ecee5a1fSHans Rosenfeld  * Common Development and Distribution License ("CDDL"), version 1.0.
4ecee5a1fSHans Rosenfeld  * You may only use this file in accordance with the terms of version
5ecee5a1fSHans Rosenfeld  * 1.0 of the CDDL.
6ecee5a1fSHans Rosenfeld  *
7ecee5a1fSHans Rosenfeld  * A full copy of the text of the CDDL should have accompanied this
8ecee5a1fSHans Rosenfeld  * source.  A copy of the CDDL is also available via the Internet at
9ecee5a1fSHans Rosenfeld  * http://www.illumos.org/license/CDDL.
10ecee5a1fSHans Rosenfeld  */
11ecee5a1fSHans Rosenfeld 
12ecee5a1fSHans Rosenfeld /*
13ecee5a1fSHans Rosenfeld  * Copyright 2016 Nexenta Systems, Inc.
140b68f179SHans Rosenfeld  * Copyright 2017 Joyent, Inc.
15ecee5a1fSHans Rosenfeld  */
16ecee5a1fSHans Rosenfeld 
17ecee5a1fSHans Rosenfeld /*
18ecee5a1fSHans Rosenfeld  * nvmeadm -- NVMe administration utility
19ecee5a1fSHans Rosenfeld  *
20ecee5a1fSHans Rosenfeld  * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args]
21ecee5a1fSHans Rosenfeld  * commands:	list
22ecee5a1fSHans Rosenfeld  *		identify
23ecee5a1fSHans Rosenfeld  *		get-logpage <logpage name>
24ecee5a1fSHans Rosenfeld  *		get-features <feature>[,...]
25ecee5a1fSHans Rosenfeld  *		format ...
26ecee5a1fSHans Rosenfeld  *		secure-erase ...
27ecee5a1fSHans Rosenfeld  *		detach ...
28ecee5a1fSHans Rosenfeld  *		attach ...
29ecee5a1fSHans Rosenfeld  *		get-param ...
30ecee5a1fSHans Rosenfeld  *		set-param ...
31ecee5a1fSHans Rosenfeld  *		load-firmware ...
32ecee5a1fSHans Rosenfeld  *		activate-firmware ...
33ecee5a1fSHans Rosenfeld  *		write-uncorrectable ...
34ecee5a1fSHans Rosenfeld  *		compare ...
35ecee5a1fSHans Rosenfeld  *		compare-and-write ...
36ecee5a1fSHans Rosenfeld  */
37ecee5a1fSHans Rosenfeld 
38ecee5a1fSHans Rosenfeld #include <stdio.h>
39ecee5a1fSHans Rosenfeld #include <stdlib.h>
40ecee5a1fSHans Rosenfeld #include <strings.h>
41ecee5a1fSHans Rosenfeld #include <ctype.h>
42ecee5a1fSHans Rosenfeld #include <err.h>
43ecee5a1fSHans Rosenfeld #include <sys/sunddi.h>
44ecee5a1fSHans Rosenfeld #include <libdevinfo.h>
45ecee5a1fSHans Rosenfeld 
46ecee5a1fSHans Rosenfeld #include <sys/nvme.h>
47ecee5a1fSHans Rosenfeld 
48ecee5a1fSHans Rosenfeld #include "nvmeadm.h"
49ecee5a1fSHans Rosenfeld 
50ecee5a1fSHans Rosenfeld typedef struct nvme_process_arg nvme_process_arg_t;
51ecee5a1fSHans Rosenfeld typedef struct nvme_feature nvme_feature_t;
52ecee5a1fSHans Rosenfeld typedef struct nvmeadm_cmd nvmeadm_cmd_t;
53ecee5a1fSHans Rosenfeld 
54ecee5a1fSHans Rosenfeld struct nvme_process_arg {
55ecee5a1fSHans Rosenfeld 	int npa_argc;
56ecee5a1fSHans Rosenfeld 	char **npa_argv;
57ecee5a1fSHans Rosenfeld 	char *npa_name;
580b68f179SHans Rosenfeld 	char *npa_nsid;
590b68f179SHans Rosenfeld 	int npa_found;
60ecee5a1fSHans Rosenfeld 	boolean_t npa_isns;
61ecee5a1fSHans Rosenfeld 	const nvmeadm_cmd_t *npa_cmd;
62ecee5a1fSHans Rosenfeld 	di_node_t npa_node;
63ecee5a1fSHans Rosenfeld 	di_minor_t npa_minor;
64ecee5a1fSHans Rosenfeld 	char *npa_path;
65ecee5a1fSHans Rosenfeld 	char *npa_dsk;
66ecee5a1fSHans Rosenfeld 	nvme_identify_ctrl_t *npa_idctl;
67ecee5a1fSHans Rosenfeld 	nvme_identify_nsid_t *npa_idns;
68ecee5a1fSHans Rosenfeld 	nvme_version_t *npa_version;
69ecee5a1fSHans Rosenfeld };
70ecee5a1fSHans Rosenfeld 
71ecee5a1fSHans Rosenfeld struct nvme_feature {
72ecee5a1fSHans Rosenfeld 	char *f_name;
73ecee5a1fSHans Rosenfeld 	char *f_short;
74ecee5a1fSHans Rosenfeld 	uint8_t f_feature;
75ecee5a1fSHans Rosenfeld 	size_t f_bufsize;
76ecee5a1fSHans Rosenfeld 	uint_t f_getflags;
77ecee5a1fSHans Rosenfeld 	int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *);
78ecee5a1fSHans Rosenfeld 	void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *);
79ecee5a1fSHans Rosenfeld };
80ecee5a1fSHans Rosenfeld 
81ecee5a1fSHans Rosenfeld #define	NVMEADM_CTRL	1
82ecee5a1fSHans Rosenfeld #define	NVMEADM_NS	2
83ecee5a1fSHans Rosenfeld #define	NVMEADM_BOTH	(NVMEADM_CTRL | NVMEADM_NS)
84ecee5a1fSHans Rosenfeld 
85ecee5a1fSHans Rosenfeld struct nvmeadm_cmd {
86ecee5a1fSHans Rosenfeld 	char *c_name;
87ecee5a1fSHans Rosenfeld 	char *c_desc;
88ecee5a1fSHans Rosenfeld 	char *c_flagdesc;
89ecee5a1fSHans Rosenfeld 	int (*c_func)(int, const nvme_process_arg_t *);
90ecee5a1fSHans Rosenfeld 	void (*c_usage)(const char *);
91ecee5a1fSHans Rosenfeld 	boolean_t c_multi;
92ecee5a1fSHans Rosenfeld };
93ecee5a1fSHans Rosenfeld 
94ecee5a1fSHans Rosenfeld 
95ecee5a1fSHans Rosenfeld static void usage(const nvmeadm_cmd_t *);
96ecee5a1fSHans Rosenfeld static void nvme_walk(nvme_process_arg_t *, di_node_t);
97ecee5a1fSHans Rosenfeld static boolean_t nvme_match(nvme_process_arg_t *);
98ecee5a1fSHans Rosenfeld 
99ecee5a1fSHans Rosenfeld static int nvme_process(di_node_t, di_minor_t, void *);
100ecee5a1fSHans Rosenfeld 
101ecee5a1fSHans Rosenfeld static int do_list(int, const nvme_process_arg_t *);
102ecee5a1fSHans Rosenfeld static int do_identify(int, const nvme_process_arg_t *);
103ecee5a1fSHans Rosenfeld static int do_get_logpage_error(int, const nvme_process_arg_t *);
104ecee5a1fSHans Rosenfeld static int do_get_logpage_health(int, const nvme_process_arg_t *);
105ecee5a1fSHans Rosenfeld static int do_get_logpage_fwslot(int, const nvme_process_arg_t *);
106ecee5a1fSHans Rosenfeld static int do_get_logpage(int, const nvme_process_arg_t *);
107ecee5a1fSHans Rosenfeld static int do_get_feat_common(int, const nvme_feature_t *,
108ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *);
109ecee5a1fSHans Rosenfeld static int do_get_feat_intr_vect(int, const nvme_feature_t *,
110ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *);
111ecee5a1fSHans Rosenfeld static int do_get_features(int, const nvme_process_arg_t *);
112ecee5a1fSHans Rosenfeld static int do_format(int, const nvme_process_arg_t *);
113ecee5a1fSHans Rosenfeld static int do_secure_erase(int, const nvme_process_arg_t *);
114ecee5a1fSHans Rosenfeld static int do_attach_detach(int, const nvme_process_arg_t *);
115ecee5a1fSHans Rosenfeld 
116ecee5a1fSHans Rosenfeld static void usage_list(const char *);
117ecee5a1fSHans Rosenfeld static void usage_identify(const char *);
118ecee5a1fSHans Rosenfeld static void usage_get_logpage(const char *);
119ecee5a1fSHans Rosenfeld static void usage_get_features(const char *);
120ecee5a1fSHans Rosenfeld static void usage_format(const char *);
121ecee5a1fSHans Rosenfeld static void usage_secure_erase(const char *);
122ecee5a1fSHans Rosenfeld static void usage_attach_detach(const char *);
123ecee5a1fSHans Rosenfeld 
124ecee5a1fSHans Rosenfeld int verbose;
125ecee5a1fSHans Rosenfeld int debug;
126ecee5a1fSHans Rosenfeld static int exitcode;
127ecee5a1fSHans Rosenfeld 
128ecee5a1fSHans Rosenfeld static const nvmeadm_cmd_t nvmeadm_cmds[] = {
129ecee5a1fSHans Rosenfeld 	{
130ecee5a1fSHans Rosenfeld 		"list",
131ecee5a1fSHans Rosenfeld 		"list controllers and namespaces",
132ecee5a1fSHans Rosenfeld 		NULL,
133ecee5a1fSHans Rosenfeld 		do_list, usage_list, B_TRUE
134ecee5a1fSHans Rosenfeld 	},
135ecee5a1fSHans Rosenfeld 	{
136ecee5a1fSHans Rosenfeld 		"identify",
137ecee5a1fSHans Rosenfeld 		"identify controllers and/or namespaces",
138ecee5a1fSHans Rosenfeld 		NULL,
139ecee5a1fSHans Rosenfeld 		do_identify, usage_identify, B_TRUE
140ecee5a1fSHans Rosenfeld 	},
141ecee5a1fSHans Rosenfeld 	{
142ecee5a1fSHans Rosenfeld 		"get-logpage",
143ecee5a1fSHans Rosenfeld 		"get a log page from controllers and/or namespaces",
144ecee5a1fSHans Rosenfeld 		NULL,
145ecee5a1fSHans Rosenfeld 		do_get_logpage, usage_get_logpage, B_TRUE
146ecee5a1fSHans Rosenfeld 	},
147ecee5a1fSHans Rosenfeld 	{
148ecee5a1fSHans Rosenfeld 		"get-features",
149ecee5a1fSHans Rosenfeld 		"get features from controllers and/or namespaces",
150ecee5a1fSHans Rosenfeld 		NULL,
151ecee5a1fSHans Rosenfeld 		do_get_features, usage_get_features, B_TRUE
152ecee5a1fSHans Rosenfeld 	},
153ecee5a1fSHans Rosenfeld 	{
154ecee5a1fSHans Rosenfeld 		"format",
155ecee5a1fSHans Rosenfeld 		"format namespace(s) of a controller",
156ecee5a1fSHans Rosenfeld 		NULL,
157ecee5a1fSHans Rosenfeld 		do_format, usage_format, B_FALSE
158ecee5a1fSHans Rosenfeld 	},
159ecee5a1fSHans Rosenfeld 	{
160ecee5a1fSHans Rosenfeld 		"secure-erase",
161ecee5a1fSHans Rosenfeld 		"secure erase namespace(s) of a controller",
162ecee5a1fSHans Rosenfeld 		"  -c  Do a cryptographic erase.",
163ecee5a1fSHans Rosenfeld 		do_secure_erase, usage_secure_erase, B_FALSE
164ecee5a1fSHans Rosenfeld 	},
165ecee5a1fSHans Rosenfeld 	{
166ecee5a1fSHans Rosenfeld 		"detach",
167ecee5a1fSHans Rosenfeld 		"detach blkdev(7d) from namespace(s) of a controller",
168ecee5a1fSHans Rosenfeld 		NULL,
169ecee5a1fSHans Rosenfeld 		do_attach_detach, usage_attach_detach, B_FALSE
170ecee5a1fSHans Rosenfeld 	},
171ecee5a1fSHans Rosenfeld 	{
172ecee5a1fSHans Rosenfeld 		"attach",
173ecee5a1fSHans Rosenfeld 		"attach blkdev(7d) to namespace(s) of a controller",
174ecee5a1fSHans Rosenfeld 		NULL,
175ecee5a1fSHans Rosenfeld 		do_attach_detach, usage_attach_detach, B_FALSE
176ecee5a1fSHans Rosenfeld 	},
177ecee5a1fSHans Rosenfeld 	{
178ecee5a1fSHans Rosenfeld 		NULL, NULL, NULL,
179ecee5a1fSHans Rosenfeld 		NULL, NULL, B_FALSE
180ecee5a1fSHans Rosenfeld 	}
181ecee5a1fSHans Rosenfeld };
182ecee5a1fSHans Rosenfeld 
183ecee5a1fSHans Rosenfeld static const nvme_feature_t features[] = {
184ecee5a1fSHans Rosenfeld 	{ "Arbitration", "",
185ecee5a1fSHans Rosenfeld 	    NVME_FEAT_ARBITRATION, 0, NVMEADM_CTRL,
186ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_arbitration },
187ecee5a1fSHans Rosenfeld 	{ "Power Management", "",
188ecee5a1fSHans Rosenfeld 	    NVME_FEAT_POWER_MGMT, 0, NVMEADM_CTRL,
189ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_power_mgmt },
190ecee5a1fSHans Rosenfeld 	{ "LBA Range Type", "range",
191ecee5a1fSHans Rosenfeld 	    NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_NS,
192ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_lba_range },
193ecee5a1fSHans Rosenfeld 	{ "Temperature Threshold", "",
194ecee5a1fSHans Rosenfeld 	    NVME_FEAT_TEMPERATURE, 0, NVMEADM_CTRL,
195ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_temperature },
196ecee5a1fSHans Rosenfeld 	{ "Error Recovery", "",
197ecee5a1fSHans Rosenfeld 	    NVME_FEAT_ERROR, 0, NVMEADM_CTRL,
198ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_error },
199ecee5a1fSHans Rosenfeld 	{ "Volatile Write Cache", "cache",
200ecee5a1fSHans Rosenfeld 	    NVME_FEAT_WRITE_CACHE, 0, NVMEADM_CTRL,
201ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_write_cache },
202ecee5a1fSHans Rosenfeld 	{ "Number of Queues", "queues",
203ecee5a1fSHans Rosenfeld 	    NVME_FEAT_NQUEUES, 0, NVMEADM_CTRL,
204ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_nqueues },
205ecee5a1fSHans Rosenfeld 	{ "Interrupt Coalescing", "coalescing",
206ecee5a1fSHans Rosenfeld 	    NVME_FEAT_INTR_COAL, 0, NVMEADM_CTRL,
207ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_intr_coal },
208ecee5a1fSHans Rosenfeld 	{ "Interrupt Vector Configuration", "vector",
209ecee5a1fSHans Rosenfeld 	    NVME_FEAT_INTR_VECT, 0, NVMEADM_CTRL,
210ecee5a1fSHans Rosenfeld 	    do_get_feat_intr_vect, nvme_print_feat_intr_vect },
211ecee5a1fSHans Rosenfeld 	{ "Write Atomicity", "atomicity",
212ecee5a1fSHans Rosenfeld 	    NVME_FEAT_WRITE_ATOM, 0, NVMEADM_CTRL,
213ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_write_atom },
214ecee5a1fSHans Rosenfeld 	{ "Asynchronous Event Configuration", "event",
215ecee5a1fSHans Rosenfeld 	    NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_CTRL,
216ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_async_event },
217ecee5a1fSHans Rosenfeld 	{ "Autonomous Power State Transition", "",
218ecee5a1fSHans Rosenfeld 	    NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_CTRL,
219ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_auto_pst },
220ecee5a1fSHans Rosenfeld 	{ "Software Progress Marker", "progress",
221ecee5a1fSHans Rosenfeld 	    NVME_FEAT_PROGRESS, 0, NVMEADM_CTRL,
222ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_progress },
223ecee5a1fSHans Rosenfeld 	{ NULL, NULL, 0, 0, B_FALSE, NULL }
224ecee5a1fSHans Rosenfeld };
225ecee5a1fSHans Rosenfeld 
226ecee5a1fSHans Rosenfeld 
227ecee5a1fSHans Rosenfeld int
228ecee5a1fSHans Rosenfeld main(int argc, char **argv)
229ecee5a1fSHans Rosenfeld {
230ecee5a1fSHans Rosenfeld 	int c;
231ecee5a1fSHans Rosenfeld 	extern int optind;
232ecee5a1fSHans Rosenfeld 	const nvmeadm_cmd_t *cmd;
233ecee5a1fSHans Rosenfeld 	di_node_t node;
234ecee5a1fSHans Rosenfeld 	nvme_process_arg_t npa = { 0 };
235ecee5a1fSHans Rosenfeld 	int help = 0;
236ecee5a1fSHans Rosenfeld 	char *tmp, *lasts = NULL;
237ecee5a1fSHans Rosenfeld 
238ecee5a1fSHans Rosenfeld 	while ((c = getopt(argc, argv, "dhv")) != -1) {
239ecee5a1fSHans Rosenfeld 		switch (c) {
240ecee5a1fSHans Rosenfeld 		case 'd':
241ecee5a1fSHans Rosenfeld 			debug++;
242ecee5a1fSHans Rosenfeld 			break;
243ecee5a1fSHans Rosenfeld 		case 'v':
244ecee5a1fSHans Rosenfeld 			verbose++;
245ecee5a1fSHans Rosenfeld 			break;
246ecee5a1fSHans Rosenfeld 		case 'h':
247ecee5a1fSHans Rosenfeld 			help++;
248ecee5a1fSHans Rosenfeld 			break;
249ecee5a1fSHans Rosenfeld 		case '?':
250ecee5a1fSHans Rosenfeld 			usage(NULL);
251ecee5a1fSHans Rosenfeld 			exit(-1);
252ecee5a1fSHans Rosenfeld 		}
253ecee5a1fSHans Rosenfeld 	}
254ecee5a1fSHans Rosenfeld 
255ecee5a1fSHans Rosenfeld 	if (optind == argc) {
256ecee5a1fSHans Rosenfeld 		usage(NULL);
257ecee5a1fSHans Rosenfeld 		if (help)
258ecee5a1fSHans Rosenfeld 			exit(0);
259ecee5a1fSHans Rosenfeld 		else
260ecee5a1fSHans Rosenfeld 			exit(-1);
261ecee5a1fSHans Rosenfeld 	}
262ecee5a1fSHans Rosenfeld 
263ecee5a1fSHans Rosenfeld 	/* Look up the specified command in the command table. */
264ecee5a1fSHans Rosenfeld 	for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
265ecee5a1fSHans Rosenfeld 		if (strcmp(cmd->c_name, argv[optind]) == 0)
266ecee5a1fSHans Rosenfeld 			break;
267ecee5a1fSHans Rosenfeld 
268ecee5a1fSHans Rosenfeld 	if (cmd->c_name == NULL) {
269ecee5a1fSHans Rosenfeld 		usage(NULL);
270ecee5a1fSHans Rosenfeld 		exit(-1);
271ecee5a1fSHans Rosenfeld 	}
272ecee5a1fSHans Rosenfeld 
273ecee5a1fSHans Rosenfeld 	if (help) {
274ecee5a1fSHans Rosenfeld 		usage(cmd);
275ecee5a1fSHans Rosenfeld 		exit(0);
276ecee5a1fSHans Rosenfeld 	}
277ecee5a1fSHans Rosenfeld 
278ecee5a1fSHans Rosenfeld 	npa.npa_cmd = cmd;
279ecee5a1fSHans Rosenfeld 
280ecee5a1fSHans Rosenfeld 	optind++;
281ecee5a1fSHans Rosenfeld 
282ecee5a1fSHans Rosenfeld 	/*
283ecee5a1fSHans Rosenfeld 	 * All commands but "list" require a ctl/ns argument.
284ecee5a1fSHans Rosenfeld 	 */
285ecee5a1fSHans Rosenfeld 	if ((optind == argc || (strncmp(argv[optind], "nvme", 4) != 0)) &&
286ecee5a1fSHans Rosenfeld 	    cmd->c_func != do_list) {
287ecee5a1fSHans Rosenfeld 		warnx("missing controller/namespace name");
288ecee5a1fSHans Rosenfeld 		usage(cmd);
289ecee5a1fSHans Rosenfeld 		exit(-1);
290ecee5a1fSHans Rosenfeld 	}
291ecee5a1fSHans Rosenfeld 
292ecee5a1fSHans Rosenfeld 
293ecee5a1fSHans Rosenfeld 	/* Store the remaining arguments for use by the command. */
294ecee5a1fSHans Rosenfeld 	npa.npa_argc = argc - optind - 1;
295ecee5a1fSHans Rosenfeld 	npa.npa_argv = &argv[optind + 1];
296ecee5a1fSHans Rosenfeld 
297ecee5a1fSHans Rosenfeld 	/*
298ecee5a1fSHans Rosenfeld 	 * Make sure we're not running commands on multiple controllers that
299ecee5a1fSHans Rosenfeld 	 * aren't allowed to do that.
300ecee5a1fSHans Rosenfeld 	 */
301ecee5a1fSHans Rosenfeld 	if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL &&
302ecee5a1fSHans Rosenfeld 	    cmd->c_multi == B_FALSE) {
303ecee5a1fSHans Rosenfeld 		warnx("%s not allowed on multiple controllers",
304ecee5a1fSHans Rosenfeld 		    cmd->c_name);
305ecee5a1fSHans Rosenfeld 		usage(cmd);
306ecee5a1fSHans Rosenfeld 		exit(-1);
307ecee5a1fSHans Rosenfeld 	}
308ecee5a1fSHans Rosenfeld 
309ecee5a1fSHans Rosenfeld 	/*
310ecee5a1fSHans Rosenfeld 	 * Get controller/namespace arguments and run command.
311ecee5a1fSHans Rosenfeld 	 */
312ecee5a1fSHans Rosenfeld 	npa.npa_name = strtok_r(argv[optind], ",", &lasts);
313ecee5a1fSHans Rosenfeld 	do {
314ecee5a1fSHans Rosenfeld 		if (npa.npa_name != NULL) {
315ecee5a1fSHans Rosenfeld 			tmp = strchr(npa.npa_name, '/');
316ecee5a1fSHans Rosenfeld 			if (tmp != NULL) {
317ecee5a1fSHans Rosenfeld 				*tmp++ = '\0';
3180b68f179SHans Rosenfeld 				npa.npa_nsid = tmp;
319ecee5a1fSHans Rosenfeld 				npa.npa_isns = B_TRUE;
320ecee5a1fSHans Rosenfeld 			}
321ecee5a1fSHans Rosenfeld 		}
322ecee5a1fSHans Rosenfeld 
323ecee5a1fSHans Rosenfeld 		if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL)
324ecee5a1fSHans Rosenfeld 			err(-1, "failed to initialize libdevinfo");
325ecee5a1fSHans Rosenfeld 		nvme_walk(&npa, node);
326ecee5a1fSHans Rosenfeld 		di_fini(node);
327ecee5a1fSHans Rosenfeld 
3280b68f179SHans Rosenfeld 		if (npa.npa_found == 0) {
329ecee5a1fSHans Rosenfeld 			if (npa.npa_name != NULL) {
3300b68f179SHans Rosenfeld 				warnx("%s%.*s%.*s: no such controller or "
331ecee5a1fSHans Rosenfeld 				    "namespace", npa.npa_name,
3320b68f179SHans Rosenfeld 				    npa.npa_isns ? -1 : 0, "/",
3330b68f179SHans Rosenfeld 				    npa.npa_isns ? -1 : 0, npa.npa_nsid);
334ecee5a1fSHans Rosenfeld 			} else {
335ecee5a1fSHans Rosenfeld 				warnx("no controllers found");
336ecee5a1fSHans Rosenfeld 			}
337ecee5a1fSHans Rosenfeld 			exitcode--;
338ecee5a1fSHans Rosenfeld 		}
3390b68f179SHans Rosenfeld 		npa.npa_found = 0;
340ecee5a1fSHans Rosenfeld 		npa.npa_name = strtok_r(NULL, ",", &lasts);
341ecee5a1fSHans Rosenfeld 	} while (npa.npa_name != NULL);
342ecee5a1fSHans Rosenfeld 
343ecee5a1fSHans Rosenfeld 	exit(exitcode);
344ecee5a1fSHans Rosenfeld }
345ecee5a1fSHans Rosenfeld 
346ecee5a1fSHans Rosenfeld static void
347ecee5a1fSHans Rosenfeld usage(const nvmeadm_cmd_t *cmd)
348ecee5a1fSHans Rosenfeld {
349ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "usage:\n");
350ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "  %s -h %s\n", getprogname(),
351ecee5a1fSHans Rosenfeld 	    cmd != NULL ? cmd->c_name : "[<command>]");
352ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "  %s [-dv] ", getprogname());
353ecee5a1fSHans Rosenfeld 
354ecee5a1fSHans Rosenfeld 	if (cmd != NULL) {
355ecee5a1fSHans Rosenfeld 		cmd->c_usage(cmd->c_name);
356ecee5a1fSHans Rosenfeld 	} else {
357ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr,
358ecee5a1fSHans Rosenfeld 		    "<command> <ctl>[/<ns>][,...] [<args>]\n");
359ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr,
360ecee5a1fSHans Rosenfeld 		    "\n  Manage NVMe controllers and namespaces.\n");
361ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr, "\ncommands:\n");
362ecee5a1fSHans Rosenfeld 
363ecee5a1fSHans Rosenfeld 		for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
364ecee5a1fSHans Rosenfeld 			(void) fprintf(stderr, "  %-15s - %s\n",
365ecee5a1fSHans Rosenfeld 			    cmd->c_name, cmd->c_desc);
366ecee5a1fSHans Rosenfeld 	}
367ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "\nflags:\n"
368ecee5a1fSHans Rosenfeld 	    "  -h  print usage information\n"
369ecee5a1fSHans Rosenfeld 	    "  -d  print information useful for debugging %s\n"
370ecee5a1fSHans Rosenfeld 	    "  -v  print verbose information\n", getprogname());
371ecee5a1fSHans Rosenfeld 	if (cmd != NULL && cmd->c_flagdesc != NULL)
372ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
373ecee5a1fSHans Rosenfeld }
374ecee5a1fSHans Rosenfeld 
375ecee5a1fSHans Rosenfeld static boolean_t
376ecee5a1fSHans Rosenfeld nvme_match(nvme_process_arg_t *npa)
377ecee5a1fSHans Rosenfeld {
378ecee5a1fSHans Rosenfeld 	char *name;
3790b68f179SHans Rosenfeld 	char *nsid = NULL;
380ecee5a1fSHans Rosenfeld 
381ecee5a1fSHans Rosenfeld 	if (npa->npa_name == NULL)
382ecee5a1fSHans Rosenfeld 		return (B_TRUE);
383ecee5a1fSHans Rosenfeld 
384ecee5a1fSHans Rosenfeld 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
385ecee5a1fSHans Rosenfeld 	    di_instance(npa->npa_node)) < 0)
386ecee5a1fSHans Rosenfeld 		err(-1, "nvme_match()");
387ecee5a1fSHans Rosenfeld 
388ecee5a1fSHans Rosenfeld 	if (strcmp(name, npa->npa_name) != 0) {
389ecee5a1fSHans Rosenfeld 		free(name);
390ecee5a1fSHans Rosenfeld 		return (B_FALSE);
391ecee5a1fSHans Rosenfeld 	}
392ecee5a1fSHans Rosenfeld 
393ecee5a1fSHans Rosenfeld 	free(name);
394ecee5a1fSHans Rosenfeld 
395ecee5a1fSHans Rosenfeld 	if (npa->npa_isns) {
3960b68f179SHans Rosenfeld 		if (npa->npa_nsid == NULL)
397ecee5a1fSHans Rosenfeld 			return (B_TRUE);
398ecee5a1fSHans Rosenfeld 
3990b68f179SHans Rosenfeld 		nsid = di_minor_name(npa->npa_minor);
4000b68f179SHans Rosenfeld 
4010b68f179SHans Rosenfeld 		if (nsid == NULL || strcmp(npa->npa_nsid, nsid) != 0)
402ecee5a1fSHans Rosenfeld 			return (B_FALSE);
4030b68f179SHans Rosenfeld 	}
404ecee5a1fSHans Rosenfeld 
405ecee5a1fSHans Rosenfeld 	return (B_TRUE);
406ecee5a1fSHans Rosenfeld }
407ecee5a1fSHans Rosenfeld 
408ecee5a1fSHans Rosenfeld char *
409ecee5a1fSHans Rosenfeld nvme_dskname(const nvme_process_arg_t *npa)
410ecee5a1fSHans Rosenfeld {
411ecee5a1fSHans Rosenfeld 	char *path = NULL;
412ecee5a1fSHans Rosenfeld 	di_node_t child;
413ecee5a1fSHans Rosenfeld 	di_dim_t dim;
414ecee5a1fSHans Rosenfeld 	char *addr;
415ecee5a1fSHans Rosenfeld 
416ecee5a1fSHans Rosenfeld 	dim = di_dim_init();
417ecee5a1fSHans Rosenfeld 
418ecee5a1fSHans Rosenfeld 	for (child = di_child_node(npa->npa_node);
419ecee5a1fSHans Rosenfeld 	    child != DI_NODE_NIL;
420ecee5a1fSHans Rosenfeld 	    child = di_sibling_node(child)) {
421ecee5a1fSHans Rosenfeld 		addr = di_bus_addr(child);
422ecee5a1fSHans Rosenfeld 		if (addr == NULL)
423ecee5a1fSHans Rosenfeld 			continue;
424ecee5a1fSHans Rosenfeld 
425ecee5a1fSHans Rosenfeld 		if (addr[0] == 'w')
426ecee5a1fSHans Rosenfeld 			addr++;
427ecee5a1fSHans Rosenfeld 
428ecee5a1fSHans Rosenfeld 		if (strncasecmp(addr, di_minor_name(npa->npa_minor),
429ecee5a1fSHans Rosenfeld 		    strchrnul(addr, ',') - addr) != 0)
430ecee5a1fSHans Rosenfeld 			continue;
431ecee5a1fSHans Rosenfeld 
432ecee5a1fSHans Rosenfeld 		path = di_dim_path_dev(dim, di_driver_name(child),
433ecee5a1fSHans Rosenfeld 		    di_instance(child), "c");
434ecee5a1fSHans Rosenfeld 
435*a856e1a6SHans Rosenfeld 		/*
436*a856e1a6SHans Rosenfeld 		 * Error out if we didn't get a path, or if it's too short for
437*a856e1a6SHans Rosenfeld 		 * the following operations to be safe.
438*a856e1a6SHans Rosenfeld 		 */
439*a856e1a6SHans Rosenfeld 		if (path == NULL || strlen(path) < 2)
440*a856e1a6SHans Rosenfeld 			goto fail;
441*a856e1a6SHans Rosenfeld 
442*a856e1a6SHans Rosenfeld 		/* Chop off 's0' and get everything past the last '/' */
443ecee5a1fSHans Rosenfeld 		path[strlen(path) - 2] = '\0';
444*a856e1a6SHans Rosenfeld 		path = strrchr(path, '/');
445ecee5a1fSHans Rosenfeld 		if (path == NULL)
446*a856e1a6SHans Rosenfeld 			goto fail;
447*a856e1a6SHans Rosenfeld 		path++;
448ecee5a1fSHans Rosenfeld 
449ecee5a1fSHans Rosenfeld 		break;
450ecee5a1fSHans Rosenfeld 	}
451ecee5a1fSHans Rosenfeld 
452ecee5a1fSHans Rosenfeld 	di_dim_fini(dim);
453*a856e1a6SHans Rosenfeld 
454ecee5a1fSHans Rosenfeld 	return (path);
455*a856e1a6SHans Rosenfeld 
456*a856e1a6SHans Rosenfeld fail:
457*a856e1a6SHans Rosenfeld 	err(-1, "nvme_dskname");
458ecee5a1fSHans Rosenfeld }
459ecee5a1fSHans Rosenfeld 
460ecee5a1fSHans Rosenfeld static int
461ecee5a1fSHans Rosenfeld nvme_process(di_node_t node, di_minor_t minor, void *arg)
462ecee5a1fSHans Rosenfeld {
463ecee5a1fSHans Rosenfeld 	nvme_process_arg_t *npa = arg;
464ecee5a1fSHans Rosenfeld 	int fd;
465ecee5a1fSHans Rosenfeld 
466ecee5a1fSHans Rosenfeld 	npa->npa_node = node;
467ecee5a1fSHans Rosenfeld 	npa->npa_minor = minor;
468ecee5a1fSHans Rosenfeld 
469ecee5a1fSHans Rosenfeld 	if (!nvme_match(npa))
470ecee5a1fSHans Rosenfeld 		return (DI_WALK_CONTINUE);
471ecee5a1fSHans Rosenfeld 
472ecee5a1fSHans Rosenfeld 	if ((fd = nvme_open(minor)) < 0)
473ecee5a1fSHans Rosenfeld 		return (DI_WALK_CONTINUE);
474ecee5a1fSHans Rosenfeld 
4750b68f179SHans Rosenfeld 	npa->npa_found++;
476ecee5a1fSHans Rosenfeld 
477ecee5a1fSHans Rosenfeld 	npa->npa_path = di_devfs_path(node);
478ecee5a1fSHans Rosenfeld 	if (npa->npa_path == NULL)
479ecee5a1fSHans Rosenfeld 		goto out;
480ecee5a1fSHans Rosenfeld 
481ecee5a1fSHans Rosenfeld 	npa->npa_version = nvme_version(fd);
482ecee5a1fSHans Rosenfeld 	if (npa->npa_version == NULL)
483ecee5a1fSHans Rosenfeld 		goto out;
484ecee5a1fSHans Rosenfeld 
485ecee5a1fSHans Rosenfeld 	npa->npa_idctl = nvme_identify_ctrl(fd);
486ecee5a1fSHans Rosenfeld 	if (npa->npa_idctl == NULL)
487ecee5a1fSHans Rosenfeld 		goto out;
488ecee5a1fSHans Rosenfeld 
489ecee5a1fSHans Rosenfeld 	npa->npa_idns = nvme_identify_nsid(fd);
490ecee5a1fSHans Rosenfeld 	if (npa->npa_idns == NULL)
491ecee5a1fSHans Rosenfeld 		goto out;
492ecee5a1fSHans Rosenfeld 
493ecee5a1fSHans Rosenfeld 	if (npa->npa_isns)
494ecee5a1fSHans Rosenfeld 		npa->npa_dsk = nvme_dskname(npa);
495ecee5a1fSHans Rosenfeld 
496ecee5a1fSHans Rosenfeld 	exitcode += npa->npa_cmd->c_func(fd, npa);
497ecee5a1fSHans Rosenfeld 
498ecee5a1fSHans Rosenfeld out:
499ecee5a1fSHans Rosenfeld 	di_devfs_path_free(npa->npa_path);
500ecee5a1fSHans Rosenfeld 	free(npa->npa_dsk);
501ecee5a1fSHans Rosenfeld 	free(npa->npa_version);
502ecee5a1fSHans Rosenfeld 	free(npa->npa_idctl);
503ecee5a1fSHans Rosenfeld 	free(npa->npa_idns);
504ecee5a1fSHans Rosenfeld 
505ecee5a1fSHans Rosenfeld 	npa->npa_version = NULL;
506ecee5a1fSHans Rosenfeld 	npa->npa_idctl = NULL;
507ecee5a1fSHans Rosenfeld 	npa->npa_idns = NULL;
508ecee5a1fSHans Rosenfeld 
509ecee5a1fSHans Rosenfeld 	nvme_close(fd);
510ecee5a1fSHans Rosenfeld 
511ecee5a1fSHans Rosenfeld 	return (DI_WALK_CONTINUE);
512ecee5a1fSHans Rosenfeld }
513ecee5a1fSHans Rosenfeld 
514ecee5a1fSHans Rosenfeld static void
515ecee5a1fSHans Rosenfeld nvme_walk(nvme_process_arg_t *npa, di_node_t node)
516ecee5a1fSHans Rosenfeld {
517ecee5a1fSHans Rosenfeld 	char *minor_nodetype = DDI_NT_NVME_NEXUS;
518ecee5a1fSHans Rosenfeld 
519ecee5a1fSHans Rosenfeld 	if (npa->npa_isns)
520ecee5a1fSHans Rosenfeld 		minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT;
521ecee5a1fSHans Rosenfeld 
522ecee5a1fSHans Rosenfeld 	(void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process);
523ecee5a1fSHans Rosenfeld }
524ecee5a1fSHans Rosenfeld 
525ecee5a1fSHans Rosenfeld static void
526ecee5a1fSHans Rosenfeld usage_list(const char *c_name)
527ecee5a1fSHans Rosenfeld {
528ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s [<ctl>[/<ns>][,...]\n\n"
529ecee5a1fSHans Rosenfeld 	    "  List NVMe controllers and their namespaces. If no "
530ecee5a1fSHans Rosenfeld 	    "controllers and/or name-\n  spaces are specified, all "
531ecee5a1fSHans Rosenfeld 	    "controllers and namespaces in the system will be\n  "
532ecee5a1fSHans Rosenfeld 	    "listed.\n", c_name);
533ecee5a1fSHans Rosenfeld }
534ecee5a1fSHans Rosenfeld 
535ecee5a1fSHans Rosenfeld static int
536ecee5a1fSHans Rosenfeld do_list_nsid(int fd, const nvme_process_arg_t *npa)
537ecee5a1fSHans Rosenfeld {
538ecee5a1fSHans Rosenfeld 	_NOTE(ARGUNUSED(fd));
539111e6ba8SHans Rosenfeld 	const uint_t format = npa->npa_idns->id_flbas.lba_format;
540111e6ba8SHans Rosenfeld 	const uint_t bshift = npa->npa_idns->id_lbaf[format].lbaf_lbads;
541111e6ba8SHans Rosenfeld 
542111e6ba8SHans Rosenfeld 	/*
543111e6ba8SHans Rosenfeld 	 * Some devices have extra namespaces with illegal block sizes and
544111e6ba8SHans Rosenfeld 	 * zero blocks. Don't list them when verbose operation isn't requested.
545111e6ba8SHans Rosenfeld 	 */
546111e6ba8SHans Rosenfeld 	if ((bshift < 9 || npa->npa_idns->id_nsize == 0) && verbose == 0)
547111e6ba8SHans Rosenfeld 		return (0);
548ecee5a1fSHans Rosenfeld 
549ecee5a1fSHans Rosenfeld 	(void) printf("  %s/%s (%s): ", npa->npa_name,
550ecee5a1fSHans Rosenfeld 	    di_minor_name(npa->npa_minor),
551ecee5a1fSHans Rosenfeld 	    npa->npa_dsk != NULL ? npa->npa_dsk : "unattached");
552ecee5a1fSHans Rosenfeld 	nvme_print_nsid_summary(npa->npa_idns);
553ecee5a1fSHans Rosenfeld 
554ecee5a1fSHans Rosenfeld 	return (0);
555ecee5a1fSHans Rosenfeld }
556ecee5a1fSHans Rosenfeld 
557ecee5a1fSHans Rosenfeld static int
558ecee5a1fSHans Rosenfeld do_list(int fd, const nvme_process_arg_t *npa)
559ecee5a1fSHans Rosenfeld {
560ecee5a1fSHans Rosenfeld 	_NOTE(ARGUNUSED(fd));
561ecee5a1fSHans Rosenfeld 
562ecee5a1fSHans Rosenfeld 	nvme_process_arg_t ns_npa = { 0 };
563ecee5a1fSHans Rosenfeld 	nvmeadm_cmd_t cmd = { 0 };
564ecee5a1fSHans Rosenfeld 	char *name;
565ecee5a1fSHans Rosenfeld 
566ecee5a1fSHans Rosenfeld 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
567ecee5a1fSHans Rosenfeld 	    di_instance(npa->npa_node)) < 0)
568ecee5a1fSHans Rosenfeld 		err(-1, "do_list()");
569ecee5a1fSHans Rosenfeld 
570ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", name);
571ecee5a1fSHans Rosenfeld 	nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version);
572ecee5a1fSHans Rosenfeld 
573ecee5a1fSHans Rosenfeld 	ns_npa.npa_name = name;
574ecee5a1fSHans Rosenfeld 	ns_npa.npa_isns = B_TRUE;
575ecee5a1fSHans Rosenfeld 	ns_npa.npa_nsid = npa->npa_nsid;
576ecee5a1fSHans Rosenfeld 	cmd = *(npa->npa_cmd);
577ecee5a1fSHans Rosenfeld 	cmd.c_func = do_list_nsid;
578ecee5a1fSHans Rosenfeld 	ns_npa.npa_cmd = &cmd;
579ecee5a1fSHans Rosenfeld 
580ecee5a1fSHans Rosenfeld 	nvme_walk(&ns_npa, npa->npa_node);
581ecee5a1fSHans Rosenfeld 
582ecee5a1fSHans Rosenfeld 	free(name);
583ecee5a1fSHans Rosenfeld 
584ecee5a1fSHans Rosenfeld 	return (exitcode);
585ecee5a1fSHans Rosenfeld }
586ecee5a1fSHans Rosenfeld 
587ecee5a1fSHans Rosenfeld static void
588ecee5a1fSHans Rosenfeld usage_identify(const char *c_name)
589ecee5a1fSHans Rosenfeld {
590ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n"
591ecee5a1fSHans Rosenfeld 	    "  Print detailed information about the specified NVMe "
592ecee5a1fSHans Rosenfeld 	    "controllers and/or name-\n  spaces.\n", c_name);
593ecee5a1fSHans Rosenfeld }
594ecee5a1fSHans Rosenfeld 
595ecee5a1fSHans Rosenfeld static int
596ecee5a1fSHans Rosenfeld do_identify(int fd, const nvme_process_arg_t *npa)
597ecee5a1fSHans Rosenfeld {
5980b68f179SHans Rosenfeld 	if (!npa->npa_isns) {
599ecee5a1fSHans Rosenfeld 		nvme_capabilities_t *cap;
600ecee5a1fSHans Rosenfeld 
601ecee5a1fSHans Rosenfeld 		cap = nvme_capabilities(fd);
602ecee5a1fSHans Rosenfeld 		if (cap == NULL)
603ecee5a1fSHans Rosenfeld 			return (-1);
604ecee5a1fSHans Rosenfeld 
605ecee5a1fSHans Rosenfeld 		(void) printf("%s: ", npa->npa_name);
606ecee5a1fSHans Rosenfeld 		nvme_print_identify_ctrl(npa->npa_idctl, cap,
607ecee5a1fSHans Rosenfeld 		    npa->npa_version);
608ecee5a1fSHans Rosenfeld 
609ecee5a1fSHans Rosenfeld 		free(cap);
610ecee5a1fSHans Rosenfeld 	} else {
611ecee5a1fSHans Rosenfeld 		(void) printf("%s/%s: ", npa->npa_name,
612ecee5a1fSHans Rosenfeld 		    di_minor_name(npa->npa_minor));
613ecee5a1fSHans Rosenfeld 		nvme_print_identify_nsid(npa->npa_idns,
614ecee5a1fSHans Rosenfeld 		    npa->npa_version);
615ecee5a1fSHans Rosenfeld 	}
616ecee5a1fSHans Rosenfeld 
617ecee5a1fSHans Rosenfeld 	return (0);
618ecee5a1fSHans Rosenfeld }
619ecee5a1fSHans Rosenfeld 
620ecee5a1fSHans Rosenfeld static void
621ecee5a1fSHans Rosenfeld usage_get_logpage(const char *c_name)
622ecee5a1fSHans Rosenfeld {
623ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n"
624ecee5a1fSHans Rosenfeld 	    "  Print the specified log page of the specified NVMe "
625ecee5a1fSHans Rosenfeld 	    "controllers and/or name-\n  spaces. Supported log pages "
626ecee5a1fSHans Rosenfeld 	    "are error, health, and firmware.\n", c_name);
627ecee5a1fSHans Rosenfeld }
628ecee5a1fSHans Rosenfeld 
629ecee5a1fSHans Rosenfeld static int
630ecee5a1fSHans Rosenfeld do_get_logpage_error(int fd, const nvme_process_arg_t *npa)
631ecee5a1fSHans Rosenfeld {
632ecee5a1fSHans Rosenfeld 	int nlog = npa->npa_idctl->id_elpe + 1;
633ecee5a1fSHans Rosenfeld 	size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog;
634ecee5a1fSHans Rosenfeld 	nvme_error_log_entry_t *elog;
635ecee5a1fSHans Rosenfeld 
6360b68f179SHans Rosenfeld 	if (npa->npa_isns)
637ecee5a1fSHans Rosenfeld 		errx(-1, "Error Log not available on a per-namespace basis");
638ecee5a1fSHans Rosenfeld 
639ecee5a1fSHans Rosenfeld 	elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize);
640ecee5a1fSHans Rosenfeld 
641ecee5a1fSHans Rosenfeld 	if (elog == NULL)
642ecee5a1fSHans Rosenfeld 		return (-1);
643ecee5a1fSHans Rosenfeld 
644ecee5a1fSHans Rosenfeld 	nlog = bufsize / sizeof (nvme_error_log_entry_t);
645ecee5a1fSHans Rosenfeld 
646ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", npa->npa_name);
647ecee5a1fSHans Rosenfeld 	nvme_print_error_log(nlog, elog);
648ecee5a1fSHans Rosenfeld 
649ecee5a1fSHans Rosenfeld 	free(elog);
650ecee5a1fSHans Rosenfeld 
651ecee5a1fSHans Rosenfeld 	return (0);
652ecee5a1fSHans Rosenfeld }
653ecee5a1fSHans Rosenfeld 
654ecee5a1fSHans Rosenfeld static int
655ecee5a1fSHans Rosenfeld do_get_logpage_health(int fd, const nvme_process_arg_t *npa)
656ecee5a1fSHans Rosenfeld {
657ecee5a1fSHans Rosenfeld 	size_t bufsize = sizeof (nvme_health_log_t);
658ecee5a1fSHans Rosenfeld 	nvme_health_log_t *hlog;
659ecee5a1fSHans Rosenfeld 
6600b68f179SHans Rosenfeld 	if (npa->npa_isns) {
661ecee5a1fSHans Rosenfeld 		if (npa->npa_idctl->id_lpa.lp_smart == 0)
662ecee5a1fSHans Rosenfeld 			errx(-1, "SMART/Health information not available "
663ecee5a1fSHans Rosenfeld 			    "on a per-namespace basis on this controller");
664ecee5a1fSHans Rosenfeld 	}
665ecee5a1fSHans Rosenfeld 
666ecee5a1fSHans Rosenfeld 	hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
667ecee5a1fSHans Rosenfeld 
668ecee5a1fSHans Rosenfeld 	if (hlog == NULL)
669ecee5a1fSHans Rosenfeld 		return (-1);
670ecee5a1fSHans Rosenfeld 
671ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", npa->npa_name);
672ecee5a1fSHans Rosenfeld 	nvme_print_health_log(hlog, npa->npa_idctl);
673ecee5a1fSHans Rosenfeld 
674ecee5a1fSHans Rosenfeld 	free(hlog);
675ecee5a1fSHans Rosenfeld 
676ecee5a1fSHans Rosenfeld 	return (0);
677ecee5a1fSHans Rosenfeld }
678ecee5a1fSHans Rosenfeld 
679ecee5a1fSHans Rosenfeld static int
680ecee5a1fSHans Rosenfeld do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa)
681ecee5a1fSHans Rosenfeld {
682ecee5a1fSHans Rosenfeld 	size_t bufsize = sizeof (nvme_fwslot_log_t);
683ecee5a1fSHans Rosenfeld 	nvme_fwslot_log_t *fwlog;
684ecee5a1fSHans Rosenfeld 
6850b68f179SHans Rosenfeld 	if (npa->npa_isns)
686ecee5a1fSHans Rosenfeld 		errx(-1, "Firmware Slot information not available on a "
687ecee5a1fSHans Rosenfeld 		    "per-namespace basis");
688ecee5a1fSHans Rosenfeld 
689ecee5a1fSHans Rosenfeld 	fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize);
690ecee5a1fSHans Rosenfeld 
691ecee5a1fSHans Rosenfeld 	if (fwlog == NULL)
692ecee5a1fSHans Rosenfeld 		return (-1);
693ecee5a1fSHans Rosenfeld 
694ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", npa->npa_name);
695ecee5a1fSHans Rosenfeld 	nvme_print_fwslot_log(fwlog);
696ecee5a1fSHans Rosenfeld 
697ecee5a1fSHans Rosenfeld 	free(fwlog);
698ecee5a1fSHans Rosenfeld 
699ecee5a1fSHans Rosenfeld 	return (0);
700ecee5a1fSHans Rosenfeld }
701ecee5a1fSHans Rosenfeld 
702ecee5a1fSHans Rosenfeld static int
703ecee5a1fSHans Rosenfeld do_get_logpage(int fd, const nvme_process_arg_t *npa)
704ecee5a1fSHans Rosenfeld {
705ecee5a1fSHans Rosenfeld 	int ret = 0;
706ecee5a1fSHans Rosenfeld 	int (*func)(int, const nvme_process_arg_t *);
707ecee5a1fSHans Rosenfeld 
708ecee5a1fSHans Rosenfeld 	if (npa->npa_argc < 1) {
709ecee5a1fSHans Rosenfeld 		warnx("missing logpage name");
710ecee5a1fSHans Rosenfeld 		usage(npa->npa_cmd);
711ecee5a1fSHans Rosenfeld 		exit(-1);
712ecee5a1fSHans Rosenfeld 	}
713ecee5a1fSHans Rosenfeld 
714ecee5a1fSHans Rosenfeld 	if (strcmp(npa->npa_argv[0], "error") == 0)
715ecee5a1fSHans Rosenfeld 		func = do_get_logpage_error;
716ecee5a1fSHans Rosenfeld 	else if (strcmp(npa->npa_argv[0], "health") == 0)
717ecee5a1fSHans Rosenfeld 		func = do_get_logpage_health;
718ecee5a1fSHans Rosenfeld 	else if (strcmp(npa->npa_argv[0], "firmware") == 0)
719ecee5a1fSHans Rosenfeld 		func = do_get_logpage_fwslot;
720ecee5a1fSHans Rosenfeld 	else
721ecee5a1fSHans Rosenfeld 		errx(-1, "invalid log page: %s", npa->npa_argv[0]);
722ecee5a1fSHans Rosenfeld 
723ecee5a1fSHans Rosenfeld 	ret = func(fd, npa);
724ecee5a1fSHans Rosenfeld 	return (ret);
725ecee5a1fSHans Rosenfeld }
726ecee5a1fSHans Rosenfeld 
727ecee5a1fSHans Rosenfeld static void
728ecee5a1fSHans Rosenfeld usage_get_features(const char *c_name)
729ecee5a1fSHans Rosenfeld {
730ecee5a1fSHans Rosenfeld 	const nvme_feature_t *feat;
731ecee5a1fSHans Rosenfeld 
732ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
733ecee5a1fSHans Rosenfeld 	    "  Print the specified features of the specified NVMe controllers "
734ecee5a1fSHans Rosenfeld 	    "and/or\n  namespaces. Supported features are:\n\n", c_name);
735ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "    %-35s %-14s %s\n",
736ecee5a1fSHans Rosenfeld 	    "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE");
737ecee5a1fSHans Rosenfeld 	for (feat = &features[0]; feat->f_feature != 0; feat++) {
738ecee5a1fSHans Rosenfeld 		char *type;
739ecee5a1fSHans Rosenfeld 
740ecee5a1fSHans Rosenfeld 		if ((feat->f_getflags & NVMEADM_BOTH) == NVMEADM_BOTH)
741ecee5a1fSHans Rosenfeld 			type = "both";
742ecee5a1fSHans Rosenfeld 		else if ((feat->f_getflags & NVMEADM_CTRL) != 0)
743ecee5a1fSHans Rosenfeld 			type = "controller only";
744ecee5a1fSHans Rosenfeld 		else
745ecee5a1fSHans Rosenfeld 			type = "namespace only";
746ecee5a1fSHans Rosenfeld 
747ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr, "    %-35s %-14s %s\n",
748ecee5a1fSHans Rosenfeld 		    feat->f_name, feat->f_short, type);
749ecee5a1fSHans Rosenfeld 	}
750ecee5a1fSHans Rosenfeld 
751ecee5a1fSHans Rosenfeld }
752ecee5a1fSHans Rosenfeld 
753ecee5a1fSHans Rosenfeld static int
754ecee5a1fSHans Rosenfeld do_get_feat_common(int fd, const nvme_feature_t *feat,
755ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *idctl)
756ecee5a1fSHans Rosenfeld {
757ecee5a1fSHans Rosenfeld 	void *buf = NULL;
758ecee5a1fSHans Rosenfeld 	size_t bufsize = feat->f_bufsize;
759ecee5a1fSHans Rosenfeld 	uint64_t res;
760ecee5a1fSHans Rosenfeld 
761ecee5a1fSHans Rosenfeld 	if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf)
762ecee5a1fSHans Rosenfeld 	    == B_FALSE)
763ecee5a1fSHans Rosenfeld 		return (EINVAL);
764ecee5a1fSHans Rosenfeld 
765ecee5a1fSHans Rosenfeld 	nvme_print(2, feat->f_name, -1, NULL);
766ecee5a1fSHans Rosenfeld 	feat->f_print(res, buf, bufsize, idctl);
767ecee5a1fSHans Rosenfeld 	free(buf);
768ecee5a1fSHans Rosenfeld 
769ecee5a1fSHans Rosenfeld 	return (0);
770ecee5a1fSHans Rosenfeld }
771ecee5a1fSHans Rosenfeld 
772ecee5a1fSHans Rosenfeld static int
773ecee5a1fSHans Rosenfeld do_get_feat_intr_vect(int fd, const nvme_feature_t *feat,
774ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *idctl)
775ecee5a1fSHans Rosenfeld {
776ecee5a1fSHans Rosenfeld 	uint64_t res;
777ecee5a1fSHans Rosenfeld 	uint64_t arg;
778ecee5a1fSHans Rosenfeld 	int intr_cnt;
779ecee5a1fSHans Rosenfeld 
780ecee5a1fSHans Rosenfeld 	intr_cnt = nvme_intr_cnt(fd);
781ecee5a1fSHans Rosenfeld 
782ecee5a1fSHans Rosenfeld 	if (intr_cnt == -1)
783ecee5a1fSHans Rosenfeld 		return (EINVAL);
784ecee5a1fSHans Rosenfeld 
785ecee5a1fSHans Rosenfeld 	nvme_print(2, feat->f_name, -1, NULL);
786ecee5a1fSHans Rosenfeld 
787ecee5a1fSHans Rosenfeld 	for (arg = 0; arg < intr_cnt; arg++) {
788ecee5a1fSHans Rosenfeld 		if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL)
789ecee5a1fSHans Rosenfeld 		    == B_FALSE)
790ecee5a1fSHans Rosenfeld 			return (EINVAL);
791ecee5a1fSHans Rosenfeld 
792ecee5a1fSHans Rosenfeld 		feat->f_print(res, NULL, 0, idctl);
793ecee5a1fSHans Rosenfeld 	}
794ecee5a1fSHans Rosenfeld 
795ecee5a1fSHans Rosenfeld 	return (0);
796ecee5a1fSHans Rosenfeld }
797ecee5a1fSHans Rosenfeld 
798ecee5a1fSHans Rosenfeld static int
799ecee5a1fSHans Rosenfeld do_get_features(int fd, const nvme_process_arg_t *npa)
800ecee5a1fSHans Rosenfeld {
801ecee5a1fSHans Rosenfeld 	const nvme_feature_t *feat;
802ecee5a1fSHans Rosenfeld 	char *f, *flist, *lasts;
803ecee5a1fSHans Rosenfeld 	boolean_t header_printed = B_FALSE;
804ecee5a1fSHans Rosenfeld 
805ecee5a1fSHans Rosenfeld 	if (npa->npa_argc > 1)
806ecee5a1fSHans Rosenfeld 		errx(-1, "unexpected arguments");
807ecee5a1fSHans Rosenfeld 
808ecee5a1fSHans Rosenfeld 	/*
809ecee5a1fSHans Rosenfeld 	 * No feature list given, print all supported features.
810ecee5a1fSHans Rosenfeld 	 */
811ecee5a1fSHans Rosenfeld 	if (npa->npa_argc == 0) {
812ecee5a1fSHans Rosenfeld 		(void) printf("%s: Get Features\n", npa->npa_name);
813ecee5a1fSHans Rosenfeld 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
8140b68f179SHans Rosenfeld 			if ((npa->npa_isns &&
815ecee5a1fSHans Rosenfeld 			    (feat->f_getflags & NVMEADM_NS) == 0) ||
8160b68f179SHans Rosenfeld 			    (!npa->npa_isns &&
817ecee5a1fSHans Rosenfeld 			    (feat->f_getflags & NVMEADM_CTRL) == 0))
818ecee5a1fSHans Rosenfeld 				continue;
819ecee5a1fSHans Rosenfeld 
820ecee5a1fSHans Rosenfeld 			(void) feat->f_get(fd, feat, npa->npa_idctl);
821ecee5a1fSHans Rosenfeld 		}
822ecee5a1fSHans Rosenfeld 
823ecee5a1fSHans Rosenfeld 		return (0);
824ecee5a1fSHans Rosenfeld 	}
825ecee5a1fSHans Rosenfeld 
826ecee5a1fSHans Rosenfeld 	/*
827ecee5a1fSHans Rosenfeld 	 * Process feature list.
828ecee5a1fSHans Rosenfeld 	 */
829ecee5a1fSHans Rosenfeld 	flist = strdup(npa->npa_argv[0]);
830ecee5a1fSHans Rosenfeld 	if (flist == NULL)
831ecee5a1fSHans Rosenfeld 		err(-1, "do_get_features");
832ecee5a1fSHans Rosenfeld 
833ecee5a1fSHans Rosenfeld 	for (f = strtok_r(flist, ",", &lasts);
834ecee5a1fSHans Rosenfeld 	    f != NULL;
835ecee5a1fSHans Rosenfeld 	    f = strtok_r(NULL, ",", &lasts)) {
836ecee5a1fSHans Rosenfeld 		while (isspace(*f))
837ecee5a1fSHans Rosenfeld 			f++;
838ecee5a1fSHans Rosenfeld 
839ecee5a1fSHans Rosenfeld 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
840ecee5a1fSHans Rosenfeld 			if (strncasecmp(feat->f_name, f, strlen(f)) == 0 ||
841ecee5a1fSHans Rosenfeld 			    strncasecmp(feat->f_short, f, strlen(f)) == 0)
842ecee5a1fSHans Rosenfeld 				break;
843ecee5a1fSHans Rosenfeld 		}
844ecee5a1fSHans Rosenfeld 
845ecee5a1fSHans Rosenfeld 		if (feat->f_feature == 0) {
846ecee5a1fSHans Rosenfeld 			warnx("unknown feature %s", f);
847ecee5a1fSHans Rosenfeld 			continue;
848ecee5a1fSHans Rosenfeld 		}
849ecee5a1fSHans Rosenfeld 
8500b68f179SHans Rosenfeld 		if ((npa->npa_isns &&
851ecee5a1fSHans Rosenfeld 		    (feat->f_getflags & NVMEADM_NS) == 0) ||
8520b68f179SHans Rosenfeld 		    (!npa->npa_isns &&
853ecee5a1fSHans Rosenfeld 		    (feat->f_getflags & NVMEADM_CTRL) == 0)) {
854ecee5a1fSHans Rosenfeld 			warnx("feature %s %s supported for namespaces",
855ecee5a1fSHans Rosenfeld 			    feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ?
856ecee5a1fSHans Rosenfeld 			    "only" : "not");
857ecee5a1fSHans Rosenfeld 			continue;
858ecee5a1fSHans Rosenfeld 		}
859ecee5a1fSHans Rosenfeld 
860ecee5a1fSHans Rosenfeld 		if (!header_printed) {
861ecee5a1fSHans Rosenfeld 			(void) printf("%s: Get Features\n", npa->npa_name);
862ecee5a1fSHans Rosenfeld 			header_printed = B_TRUE;
863ecee5a1fSHans Rosenfeld 		}
864ecee5a1fSHans Rosenfeld 
865ecee5a1fSHans Rosenfeld 		if (feat->f_get(fd, feat, npa->npa_idctl) != 0) {
866ecee5a1fSHans Rosenfeld 			warnx("unsupported feature: %s", feat->f_name);
867ecee5a1fSHans Rosenfeld 			continue;
868ecee5a1fSHans Rosenfeld 		}
869ecee5a1fSHans Rosenfeld 	}
870ecee5a1fSHans Rosenfeld 
871ecee5a1fSHans Rosenfeld 	free(flist);
872ecee5a1fSHans Rosenfeld 	return (0);
873ecee5a1fSHans Rosenfeld }
874ecee5a1fSHans Rosenfeld 
875ecee5a1fSHans Rosenfeld static int
876ecee5a1fSHans Rosenfeld do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf,
877ecee5a1fSHans Rosenfeld     unsigned long ses)
878ecee5a1fSHans Rosenfeld {
879ecee5a1fSHans Rosenfeld 	nvme_process_arg_t ns_npa = { 0 };
880ecee5a1fSHans Rosenfeld 	nvmeadm_cmd_t cmd = { 0 };
881ecee5a1fSHans Rosenfeld 
882ecee5a1fSHans Rosenfeld 	cmd = *(npa->npa_cmd);
883ecee5a1fSHans Rosenfeld 	cmd.c_func = do_attach_detach;
884ecee5a1fSHans Rosenfeld 	cmd.c_name = "detach";
885ecee5a1fSHans Rosenfeld 	ns_npa = *npa;
886ecee5a1fSHans Rosenfeld 	ns_npa.npa_cmd = &cmd;
887ecee5a1fSHans Rosenfeld 
888ecee5a1fSHans Rosenfeld 	if (do_attach_detach(fd, &ns_npa) != 0)
889ecee5a1fSHans Rosenfeld 		return (exitcode);
890ecee5a1fSHans Rosenfeld 	if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) {
891ecee5a1fSHans Rosenfeld 		warn("%s failed", npa->npa_cmd->c_name);
892ecee5a1fSHans Rosenfeld 		exitcode += -1;
893ecee5a1fSHans Rosenfeld 	}
894ecee5a1fSHans Rosenfeld 	cmd.c_name = "attach";
895ecee5a1fSHans Rosenfeld 	exitcode += do_attach_detach(fd, &ns_npa);
896ecee5a1fSHans Rosenfeld 
897ecee5a1fSHans Rosenfeld 	return (exitcode);
898ecee5a1fSHans Rosenfeld }
899ecee5a1fSHans Rosenfeld 
900ecee5a1fSHans Rosenfeld static void
901ecee5a1fSHans Rosenfeld usage_format(const char *c_name)
902ecee5a1fSHans Rosenfeld {
903ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
904ecee5a1fSHans Rosenfeld 	    "  Format one or all namespaces of the specified NVMe "
905ecee5a1fSHans Rosenfeld 	    "controller. Supported LBA\n  formats can be queried with "
906ecee5a1fSHans Rosenfeld 	    "the \"%s identify\" command on the namespace\n  to be "
907ecee5a1fSHans Rosenfeld 	    "formatted.\n", c_name, getprogname());
908ecee5a1fSHans Rosenfeld }
909ecee5a1fSHans Rosenfeld 
910ecee5a1fSHans Rosenfeld static int
911ecee5a1fSHans Rosenfeld do_format(int fd, const nvme_process_arg_t *npa)
912ecee5a1fSHans Rosenfeld {
913ecee5a1fSHans Rosenfeld 	unsigned long lbaf;
914ecee5a1fSHans Rosenfeld 
915ecee5a1fSHans Rosenfeld 	if (npa->npa_idctl->id_oacs.oa_format == 0)
916ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
917ecee5a1fSHans Rosenfeld 
918ecee5a1fSHans Rosenfeld 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0)
919ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported on individual namespace",
920ecee5a1fSHans Rosenfeld 		    npa->npa_cmd->c_name);
921ecee5a1fSHans Rosenfeld 
922ecee5a1fSHans Rosenfeld 
923ecee5a1fSHans Rosenfeld 	if (npa->npa_argc > 0) {
924ecee5a1fSHans Rosenfeld 		errno = 0;
925ecee5a1fSHans Rosenfeld 		lbaf = strtoul(npa->npa_argv[0], NULL, 10);
926ecee5a1fSHans Rosenfeld 
927ecee5a1fSHans Rosenfeld 		if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF)
928ecee5a1fSHans Rosenfeld 			errx(-1, "invalid LBA format %d", lbaf + 1);
929ecee5a1fSHans Rosenfeld 
930ecee5a1fSHans Rosenfeld 		if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0)
931ecee5a1fSHans Rosenfeld 			errx(-1, "LBA formats with metadata not supported");
932ecee5a1fSHans Rosenfeld 	} else {
933ecee5a1fSHans Rosenfeld 		lbaf = npa->npa_idns->id_flbas.lba_format;
934ecee5a1fSHans Rosenfeld 	}
935ecee5a1fSHans Rosenfeld 
936ecee5a1fSHans Rosenfeld 	return (do_format_common(fd, npa, lbaf, 0));
937ecee5a1fSHans Rosenfeld }
938ecee5a1fSHans Rosenfeld 
939ecee5a1fSHans Rosenfeld static void
940ecee5a1fSHans Rosenfeld usage_secure_erase(const char *c_name)
941ecee5a1fSHans Rosenfeld {
942ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [-c]\n\n"
943ecee5a1fSHans Rosenfeld 	    "  Secure-Erase one or all namespaces of the specified "
944ecee5a1fSHans Rosenfeld 	    "NVMe controller.\n", c_name);
945ecee5a1fSHans Rosenfeld }
946ecee5a1fSHans Rosenfeld 
947ecee5a1fSHans Rosenfeld static int
948ecee5a1fSHans Rosenfeld do_secure_erase(int fd, const nvme_process_arg_t *npa)
949ecee5a1fSHans Rosenfeld {
950ecee5a1fSHans Rosenfeld 	unsigned long lbaf;
951ecee5a1fSHans Rosenfeld 	uint8_t ses = NVME_FRMT_SES_USER;
952ecee5a1fSHans Rosenfeld 
953ecee5a1fSHans Rosenfeld 	if (npa->npa_idctl->id_oacs.oa_format == 0)
954ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
955ecee5a1fSHans Rosenfeld 
956ecee5a1fSHans Rosenfeld 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0)
957ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported on individual namespace",
958ecee5a1fSHans Rosenfeld 		    npa->npa_cmd->c_name);
959ecee5a1fSHans Rosenfeld 
960ecee5a1fSHans Rosenfeld 	if (npa->npa_argc > 0) {
961ecee5a1fSHans Rosenfeld 		if (strcmp(npa->npa_argv[0], "-c") == 0)
962ecee5a1fSHans Rosenfeld 			ses = NVME_FRMT_SES_CRYPTO;
963ecee5a1fSHans Rosenfeld 		else
964ecee5a1fSHans Rosenfeld 			usage(npa->npa_cmd);
965ecee5a1fSHans Rosenfeld 	}
966ecee5a1fSHans Rosenfeld 
967ecee5a1fSHans Rosenfeld 	if (ses == NVME_FRMT_SES_CRYPTO &&
968ecee5a1fSHans Rosenfeld 	    npa->npa_idctl->id_fna.fn_crypt_erase == 0)
969ecee5a1fSHans Rosenfeld 		errx(-1, "cryptographic %s not supported",
970ecee5a1fSHans Rosenfeld 		    npa->npa_cmd->c_name);
971ecee5a1fSHans Rosenfeld 
972ecee5a1fSHans Rosenfeld 	lbaf = npa->npa_idns->id_flbas.lba_format;
973ecee5a1fSHans Rosenfeld 
974ecee5a1fSHans Rosenfeld 	return (do_format_common(fd, npa, lbaf, ses));
975ecee5a1fSHans Rosenfeld }
976ecee5a1fSHans Rosenfeld 
977ecee5a1fSHans Rosenfeld static void
978ecee5a1fSHans Rosenfeld usage_attach_detach(const char *c_name)
979ecee5a1fSHans Rosenfeld {
980ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
981ecee5a1fSHans Rosenfeld 	    "  %c%s blkdev(7d) %s one or all namespaces of the "
982ecee5a1fSHans Rosenfeld 	    "specified NVMe controller.\n",
983ecee5a1fSHans Rosenfeld 	    c_name, toupper(c_name[0]), &c_name[1],
984ecee5a1fSHans Rosenfeld 	    c_name[0] == 'd' ? "from" : "to");
985ecee5a1fSHans Rosenfeld }
986ecee5a1fSHans Rosenfeld 
987ecee5a1fSHans Rosenfeld static int
988ecee5a1fSHans Rosenfeld do_attach_detach(int fd, const nvme_process_arg_t *npa)
989ecee5a1fSHans Rosenfeld {
990ecee5a1fSHans Rosenfeld 	char *c_name = npa->npa_cmd->c_name;
991ecee5a1fSHans Rosenfeld 
992ecee5a1fSHans Rosenfeld 	if (!npa->npa_isns) {
993ecee5a1fSHans Rosenfeld 		nvme_process_arg_t ns_npa = { 0 };
994ecee5a1fSHans Rosenfeld 
995ecee5a1fSHans Rosenfeld 		ns_npa.npa_name = npa->npa_name;
996ecee5a1fSHans Rosenfeld 		ns_npa.npa_isns = B_TRUE;
997ecee5a1fSHans Rosenfeld 		ns_npa.npa_cmd = npa->npa_cmd;
998ecee5a1fSHans Rosenfeld 
999ecee5a1fSHans Rosenfeld 		nvme_walk(&ns_npa, npa->npa_node);
1000ecee5a1fSHans Rosenfeld 
1001ecee5a1fSHans Rosenfeld 		return (exitcode);
1002ecee5a1fSHans Rosenfeld 	} else {
1003ecee5a1fSHans Rosenfeld 		if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd)
1004ecee5a1fSHans Rosenfeld 		    == B_FALSE) {
1005ecee5a1fSHans Rosenfeld 			warn("%s failed", c_name);
1006ecee5a1fSHans Rosenfeld 			return (-1);
1007ecee5a1fSHans Rosenfeld 		}
1008ecee5a1fSHans Rosenfeld 	}
1009ecee5a1fSHans Rosenfeld 
1010ecee5a1fSHans Rosenfeld 	return (0);
1011ecee5a1fSHans Rosenfeld }
1012