xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm.c (revision 046911eb)
13d9b1a2aSHans Rosenfeld /*
23d9b1a2aSHans Rosenfeld  * This file and its contents are supplied under the terms of the
33d9b1a2aSHans Rosenfeld  * Common Development and Distribution License ("CDDL"), version 1.0.
43d9b1a2aSHans Rosenfeld  * You may only use this file in accordance with the terms of version
53d9b1a2aSHans Rosenfeld  * 1.0 of the CDDL.
63d9b1a2aSHans Rosenfeld  *
73d9b1a2aSHans Rosenfeld  * A full copy of the text of the CDDL should have accompanied this
83d9b1a2aSHans Rosenfeld  * source.  A copy of the CDDL is also available via the Internet at
93d9b1a2aSHans Rosenfeld  * http://www.illumos.org/license/CDDL.
103d9b1a2aSHans Rosenfeld  */
113d9b1a2aSHans Rosenfeld 
123d9b1a2aSHans Rosenfeld /*
13c6e58d8cSHans Rosenfeld  * Copyright 2017 Joyent, Inc.
14533affcbSRobert Mustacchi  * Copyright 2024 Oxide Computer Company
158466ab88SHans Rosenfeld  * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
163d9b1a2aSHans Rosenfeld  */
173d9b1a2aSHans Rosenfeld 
183d9b1a2aSHans Rosenfeld /*
193d9b1a2aSHans Rosenfeld  * nvmeadm -- NVMe administration utility
203d9b1a2aSHans Rosenfeld  *
213d9b1a2aSHans Rosenfeld  * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args]
223d9b1a2aSHans Rosenfeld  * commands:	list
233d9b1a2aSHans Rosenfeld  *		identify
24533affcbSRobert Mustacchi  *		list-logpages [logpage name],...
253d9b1a2aSHans Rosenfeld  *		get-logpage <logpage name>
263d9b1a2aSHans Rosenfeld  *		get-features <feature>[,...]
273d9b1a2aSHans Rosenfeld  *		format ...
283d9b1a2aSHans Rosenfeld  *		secure-erase ...
293d9b1a2aSHans Rosenfeld  *		detach ...
303d9b1a2aSHans Rosenfeld  *		attach ...
3168df0c4fSGuy Morrogh  *		list-firmware ...
323d9b1a2aSHans Rosenfeld  *		load-firmware ...
33cf840871SPaul Winder  *		commit-firmware ...
343d9b1a2aSHans Rosenfeld  *		activate-firmware ...
353d9b1a2aSHans Rosenfeld  */
363d9b1a2aSHans Rosenfeld 
373d9b1a2aSHans Rosenfeld #include <stdio.h>
383d9b1a2aSHans Rosenfeld #include <stdlib.h>
39153f3212SHans Rosenfeld #include <stddef.h>
40cf840871SPaul Winder #include <unistd.h>
41cf840871SPaul Winder #include <fcntl.h>
423d9b1a2aSHans Rosenfeld #include <strings.h>
433d9b1a2aSHans Rosenfeld #include <ctype.h>
443d9b1a2aSHans Rosenfeld #include <err.h>
453d9b1a2aSHans Rosenfeld #include <sys/sunddi.h>
463d9b1a2aSHans Rosenfeld #include <libdevinfo.h>
47533affcbSRobert Mustacchi #include <sys/sysmacros.h>
483d9b1a2aSHans Rosenfeld 
493d9b1a2aSHans Rosenfeld #include <sys/nvme.h>
503d9b1a2aSHans Rosenfeld 
513d9b1a2aSHans Rosenfeld #include "nvmeadm.h"
523d9b1a2aSHans Rosenfeld 
53153f3212SHans Rosenfeld /*
54153f3212SHans Rosenfeld  * Assertions to make sure that we've properly captured various aspects of the
55153f3212SHans Rosenfeld  * packed structures and haven't broken them during updates.
56153f3212SHans Rosenfeld  */
57153f3212SHans Rosenfeld CTASSERT(sizeof (nvme_identify_ctrl_t) == NVME_IDENTIFY_BUFSIZE);
58153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_ctrl_t, id_oacs) == 256);
59153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_ctrl_t, id_sqes) == 512);
60153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_ctrl_t, id_oncs) == 520);
61153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_ctrl_t, id_subnqn) == 768);
62153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_ctrl_t, id_nvmof) == 1792);
63153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_ctrl_t, id_psd) == 2048);
64153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_ctrl_t, id_vs) == 3072);
65153f3212SHans Rosenfeld 
66153f3212SHans Rosenfeld CTASSERT(sizeof (nvme_identify_nsid_t) == NVME_IDENTIFY_BUFSIZE);
67153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_nsid_t, id_fpi) == 32);
68153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_nsid_t, id_anagrpid) == 92);
69153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_nsid_t, id_nguid) == 104);
70153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_nsid_t, id_lbaf) == 128);
71153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_nsid_t, id_vs) == 384);
72153f3212SHans Rosenfeld 
73153f3212SHans Rosenfeld CTASSERT(sizeof (nvme_identify_nsid_list_t) == NVME_IDENTIFY_BUFSIZE);
74153f3212SHans Rosenfeld CTASSERT(sizeof (nvme_identify_ctrl_list_t) == NVME_IDENTIFY_BUFSIZE);
75153f3212SHans Rosenfeld 
76153f3212SHans Rosenfeld CTASSERT(sizeof (nvme_identify_primary_caps_t) == NVME_IDENTIFY_BUFSIZE);
77153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vqfrt) == 32);
78153f3212SHans Rosenfeld CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vifrt) == 64);
79153f3212SHans Rosenfeld 
80153f3212SHans Rosenfeld CTASSERT(sizeof (nvme_nschange_list_t) == 4096);
81153f3212SHans Rosenfeld 
82353d89b0SHans Rosenfeld #define	NVMEADM_F_CTRL	1
83353d89b0SHans Rosenfeld #define	NVMEADM_F_NS	2
84353d89b0SHans Rosenfeld #define	NVMEADM_F_BOTH	(NVMEADM_F_CTRL | NVMEADM_F_NS)
85353d89b0SHans Rosenfeld 
863d9b1a2aSHans Rosenfeld static void usage(const nvmeadm_cmd_t *);
87533affcbSRobert Mustacchi static bool nvmeadm_ctrl_disc_cb(nvme_t *, const nvme_ctrl_disc_t *, void *);
88533affcbSRobert Mustacchi 
89533affcbSRobert Mustacchi static int do_list(const nvme_process_arg_t *);
90533affcbSRobert Mustacchi static int do_identify(const nvme_process_arg_t *);
91533affcbSRobert Mustacchi static int do_identify_ctrl(const nvme_process_arg_t *);
92533affcbSRobert Mustacchi static int do_identify_ns(const nvme_process_arg_t *);
93533affcbSRobert Mustacchi static int do_list_logs(const nvme_process_arg_t *);
94533affcbSRobert Mustacchi static int do_get_logpage_fwslot(const nvme_process_arg_t *);
95533affcbSRobert Mustacchi static int do_get_logpage(const nvme_process_arg_t *);
96533affcbSRobert Mustacchi static int do_list_features(const nvme_process_arg_t *);
97533affcbSRobert Mustacchi static boolean_t do_get_feat_intr_vect(const nvme_process_arg_t *,
98533affcbSRobert Mustacchi     const nvme_feat_disc_t *, const nvmeadm_feature_t *);
99533affcbSRobert Mustacchi static boolean_t do_get_feat_temp_thresh(const nvme_process_arg_t *,
100533affcbSRobert Mustacchi     const nvme_feat_disc_t *, const nvmeadm_feature_t *);
101533affcbSRobert Mustacchi static int do_get_features(const nvme_process_arg_t *);
102533affcbSRobert Mustacchi static int do_format(const nvme_process_arg_t *);
103533affcbSRobert Mustacchi static int do_secure_erase(const nvme_process_arg_t *);
104533affcbSRobert Mustacchi static int do_attach(const nvme_process_arg_t *);
105533affcbSRobert Mustacchi static int do_detach(const nvme_process_arg_t *);
106533affcbSRobert Mustacchi static int do_firmware_load(const nvme_process_arg_t *);
107533affcbSRobert Mustacchi static int do_firmware_commit(const nvme_process_arg_t *);
108533affcbSRobert Mustacchi static int do_firmware_activate(const nvme_process_arg_t *);
1093d9b1a2aSHans Rosenfeld 
110e4cc4004SRobert Mustacchi static void optparse_list(nvme_process_arg_t *);
111153f3212SHans Rosenfeld static void optparse_identify(nvme_process_arg_t *);
112153f3212SHans Rosenfeld static void optparse_identify_ctrl(nvme_process_arg_t *);
113153f3212SHans Rosenfeld static void optparse_identify_ns(nvme_process_arg_t *);
114533affcbSRobert Mustacchi static void optparse_list_logs(nvme_process_arg_t *);
115f9fceaa5SRobert Mustacchi static void optparse_get_logpage(nvme_process_arg_t *);
116533affcbSRobert Mustacchi static void optparse_list_features(nvme_process_arg_t *);
117a3bac573SHans Rosenfeld static void optparse_secure_erase(nvme_process_arg_t *);
118e4cc4004SRobert Mustacchi 
1193d9b1a2aSHans Rosenfeld static void usage_list(const char *);
1203d9b1a2aSHans Rosenfeld static void usage_identify(const char *);
121153f3212SHans Rosenfeld static void usage_identify_ctrl(const char *);
122153f3212SHans Rosenfeld static void usage_identify_ns(const char *);
123533affcbSRobert Mustacchi static void usage_list_logs(const char *);
1243d9b1a2aSHans Rosenfeld static void usage_get_logpage(const char *);
125533affcbSRobert Mustacchi static void usage_list_features(const char *);
1263d9b1a2aSHans Rosenfeld static void usage_get_features(const char *);
1273d9b1a2aSHans Rosenfeld static void usage_format(const char *);
1283d9b1a2aSHans Rosenfeld static void usage_secure_erase(const char *);
1293d9b1a2aSHans Rosenfeld static void usage_attach_detach(const char *);
13068df0c4fSGuy Morrogh static void usage_firmware_list(const char *);
131cf840871SPaul Winder static void usage_firmware_load(const char *);
132cf840871SPaul Winder static void usage_firmware_commit(const char *);
133cf840871SPaul Winder static void usage_firmware_activate(const char *);
1343d9b1a2aSHans Rosenfeld 
1353d9b1a2aSHans Rosenfeld int verbose;
1363d9b1a2aSHans Rosenfeld int debug;
137a3bac573SHans Rosenfeld 
138533affcbSRobert Mustacchi /*
139533affcbSRobert Mustacchi  * nvmeadm Secure-erase specific options
140533affcbSRobert Mustacchi  */
141a3bac573SHans Rosenfeld #define	NVMEADM_O_SE_CRYPTO	0x00000004
142a3bac573SHans Rosenfeld 
143533affcbSRobert Mustacchi /*
144533affcbSRobert Mustacchi  * nvmeadm identify specific options
145533affcbSRobert Mustacchi  */
146153f3212SHans Rosenfeld #define	NVMEADM_O_ID_NSID_LIST	0x00000008
147153f3212SHans Rosenfeld #define	NVMEADM_O_ID_COMMON_NS	0x00000010
148153f3212SHans Rosenfeld #define	NVMEADM_O_ID_CTRL_LIST	0x00000020
149153f3212SHans Rosenfeld #define	NVMEADM_O_ID_DESC_LIST	0x00000040
150153f3212SHans Rosenfeld #define	NVMEADM_O_ID_ALLOC_NS	0x00000080
151153f3212SHans Rosenfeld 
152533affcbSRobert Mustacchi /*
153533affcbSRobert Mustacchi  * nvmeadm List specific options
154533affcbSRobert Mustacchi  */
155b3266bebSHans Rosenfeld #define	NVMEADM_O_LS_CTRL	0x00000100
156b3266bebSHans Rosenfeld 
1573d9b1a2aSHans Rosenfeld static int exitcode;
1583d9b1a2aSHans Rosenfeld 
159153f3212SHans Rosenfeld /*
160153f3212SHans Rosenfeld  * Nvmeadm subcommand definitons.
161153f3212SHans Rosenfeld  *
162153f3212SHans Rosenfeld  * When adding a new subcommand, please check that the commands still
163153f3212SHans Rosenfeld  * line up in the usage() message, and adjust the format string in
164153f3212SHans Rosenfeld  * usage() below if necessary.
165153f3212SHans Rosenfeld  */
1663d9b1a2aSHans Rosenfeld static const nvmeadm_cmd_t nvmeadm_cmds[] = {
1673d9b1a2aSHans Rosenfeld 	{
1683d9b1a2aSHans Rosenfeld 		"list",
1693d9b1a2aSHans Rosenfeld 		"list controllers and namespaces",
170b3266bebSHans Rosenfeld 		"  -c\t\tlist only controllers\n"
171e4cc4004SRobert Mustacchi 		"  -p\t\tprint parsable output\n"
172533affcbSRobert Mustacchi 		"  -o field\tselect a field for parsable output\n",
173533affcbSRobert Mustacchi 		"  model\t\tthe model name of the device\n"
174533affcbSRobert Mustacchi 		"  serial\tthe serial number of the device\n"
175533affcbSRobert Mustacchi 		"  fwrev\t\tthe device's current firmware revision\n"
176533affcbSRobert Mustacchi 		"  version\tthe device's NVMe specification version\n"
177533affcbSRobert Mustacchi 		"  capacity\tthe capacity of the device in bytes\n"
178533affcbSRobert Mustacchi 		"  instance\tthe device driver instance (e.g. nvme3)\n"
179533affcbSRobert Mustacchi 		"  unallocated\tthe amount of unallocated NVM in bytes",
180353d89b0SHans Rosenfeld 		do_list, usage_list, optparse_list,
181353d89b0SHans Rosenfeld 		NVMEADM_C_MULTI
1823d9b1a2aSHans Rosenfeld 	},
1833d9b1a2aSHans Rosenfeld 	{
1843d9b1a2aSHans Rosenfeld 		"identify",
1853d9b1a2aSHans Rosenfeld 		"identify controllers and/or namespaces",
186153f3212SHans Rosenfeld 		"  -C\t\tget Common Namespace Identification\n"
187153f3212SHans Rosenfeld 		"  -a\t\tget only allocated namespace information\n"
188153f3212SHans Rosenfeld 		"  -c\t\tget controller identifier list\n"
189153f3212SHans Rosenfeld 		"  -d\t\tget namespace identification descriptors list\n"
190533affcbSRobert Mustacchi 		"  -n\t\tget namespaces identifier list",
191533affcbSRobert Mustacchi 		NULL,
192153f3212SHans Rosenfeld 		do_identify, usage_identify, optparse_identify,
193153f3212SHans Rosenfeld 		NVMEADM_C_MULTI
194153f3212SHans Rosenfeld 	},
195153f3212SHans Rosenfeld 	{
196153f3212SHans Rosenfeld 		"identify-controller",
197153f3212SHans Rosenfeld 		"identify controllers",
198153f3212SHans Rosenfeld 		"  -C\t\tget Common Namespace Identification\n"
199153f3212SHans Rosenfeld 		"  -a\t\tget only allocated namespace information\n"
200153f3212SHans Rosenfeld 		"  -c\t\tget controller identifier list\n"
201533affcbSRobert Mustacchi 		"  -n\t\tget namespaces identifier list",
202533affcbSRobert Mustacchi 		NULL,
203153f3212SHans Rosenfeld 		do_identify_ctrl, usage_identify_ctrl, optparse_identify_ctrl,
204153f3212SHans Rosenfeld 		NVMEADM_C_MULTI
205153f3212SHans Rosenfeld 	},
206153f3212SHans Rosenfeld 	{
207153f3212SHans Rosenfeld 		"identify-namespace",
208153f3212SHans Rosenfeld 		"identify namespaces",
209153f3212SHans Rosenfeld 		"  -c\t\tget attached controller identifier list\n"
210533affcbSRobert Mustacchi 		"  -d\t\tget namespace identification descriptors list",
211533affcbSRobert Mustacchi 		NULL,
212153f3212SHans Rosenfeld 		do_identify_ns, usage_identify_ns, optparse_identify_ns,
213353d89b0SHans Rosenfeld 		NVMEADM_C_MULTI
2143d9b1a2aSHans Rosenfeld 	},
215533affcbSRobert Mustacchi 	{
216533affcbSRobert Mustacchi 		"list-logpages",
217533affcbSRobert Mustacchi 		"list a device's supported log pages",
218533affcbSRobert Mustacchi 		"  -a\t\tprint all log pages, including unimplemented ones\n"
219533affcbSRobert Mustacchi 		"  -H\t\tomit column headers\n"
220533affcbSRobert Mustacchi 		"  -o field\tselect a field for parsable output\n"
221533affcbSRobert Mustacchi 		"  -p\t\tprint parsable output\n"
222533affcbSRobert Mustacchi 		"  -s scope\tprint logs that match the specified scopes "
223533affcbSRobert Mustacchi 		"(default is based on\n\t\tdevice)\n",
224533affcbSRobert Mustacchi 		"  device\tthe name of the controller or namespace\n"
225533affcbSRobert Mustacchi 		"  name\t\tthe name of the log page\n"
226533affcbSRobert Mustacchi 		"  desc\t\ta description of the loage page\n"
227533affcbSRobert Mustacchi 		"  scope\t\tthe valid device scopes for the log page\n"
228533affcbSRobert Mustacchi 		"  fields\tthe list of fields in the get log request that may "
229533affcbSRobert Mustacchi 		"be set or required\n\t\t(e.g. lsi, lsp, rae, etc.)\n"
230533affcbSRobert Mustacchi 		"  csi\t\tthe command set interface the log page belongs to\n"
231533affcbSRobert Mustacchi 		"  lid\t\tthe log page's numeric ID\n"
232533affcbSRobert Mustacchi 		"  impl\t\tindicates whether the device implements the log "
233533affcbSRobert Mustacchi 		"page\n"
234533affcbSRobert Mustacchi 		"  size\t\tthe size of the log page for fixed size logs\n"
235533affcbSRobert Mustacchi 		"  minsize\tthe minimum size required to determine the full "
236533affcbSRobert Mustacchi 		"log page size\n\t\tfor variable-length pages\n"
237533affcbSRobert Mustacchi 		"  sources\twhere information for this log page came from\n"
238533affcbSRobert Mustacchi 		"  kind\t\tindicates the kind of log page e.g. standard, "
239533affcbSRobert Mustacchi 		"vendor-specific,\n\t\tetc.",
240533affcbSRobert Mustacchi 		do_list_logs, usage_list_logs, optparse_list_logs,
241533affcbSRobert Mustacchi 		NVMEADM_C_MULTI
242533affcbSRobert Mustacchi 	},
2433d9b1a2aSHans Rosenfeld 	{
2443d9b1a2aSHans Rosenfeld 		"get-logpage",
2453d9b1a2aSHans Rosenfeld 		"get a log page from controllers and/or namespaces",
246f9fceaa5SRobert Mustacchi 		"  -O file\toutput log raw binary data to a file\n",
2473d9b1a2aSHans Rosenfeld 		NULL,
248f9fceaa5SRobert Mustacchi 		do_get_logpage, usage_get_logpage, optparse_get_logpage,
249353d89b0SHans Rosenfeld 		NVMEADM_C_MULTI
2503d9b1a2aSHans Rosenfeld 	},
251533affcbSRobert Mustacchi 	{
252533affcbSRobert Mustacchi 		"list-features",
253533affcbSRobert Mustacchi 		"list a device's supported features",
254533affcbSRobert Mustacchi 		"  -a\t\tprint all features, including unsupported\n"
255533affcbSRobert Mustacchi 		"  -H\t\tomit column headers\n"
256533affcbSRobert Mustacchi 		"  -o field\tselect a field for parsable output\n"
257533affcbSRobert Mustacchi 		"  -p\t\tprint parsable output",
258533affcbSRobert Mustacchi 		"  device\tthe name of the controller or namespace\n"
259533affcbSRobert Mustacchi 		"  short\t\tthe short name of the feature\n"
260533affcbSRobert Mustacchi 		"  spec\t\tthe longer feature description from the NVMe spec\n"
261533affcbSRobert Mustacchi 		"  fid\t\tthe numeric feature ID\n"
262533affcbSRobert Mustacchi 		"  scope\t\tthe valid device scopes for the feature\n"
263533affcbSRobert Mustacchi 		"  kind\t\tindicates the kind of feature e.g. standard, "
264533affcbSRobert Mustacchi 		"vendor-specific,\n\t\tetc.\n"
265533affcbSRobert Mustacchi 		"  csi\t\tindicates the features command set interface\n"
266533affcbSRobert Mustacchi 		"  flags\t\tindicates additional properties of the feature\n"
267533affcbSRobert Mustacchi 		"  get-in\tindicates the fields that are required to get the "
268533affcbSRobert Mustacchi 		"feature\n"
269533affcbSRobert Mustacchi 		"  set-in\tindicates the fields that are required to set the "
270533affcbSRobert Mustacchi 		"feature\n"
271533affcbSRobert Mustacchi 		"  get-out\tindicates the fields the feature outputs\n"
272533affcbSRobert Mustacchi 		"  set-out\tindicates the fields the feature outputs when "
273533affcbSRobert Mustacchi 		"setting the feature\n"
274533affcbSRobert Mustacchi 		"  datalen\tindicates the length of the feature's data "
275533affcbSRobert Mustacchi 		"payload\n"
276533affcbSRobert Mustacchi 		"  impl\t\tindicates whether the device implements the "
277533affcbSRobert Mustacchi 		"feature",
278533affcbSRobert Mustacchi 		do_list_features, usage_list_features, optparse_list_features,
279533affcbSRobert Mustacchi 		NVMEADM_C_MULTI
280533affcbSRobert Mustacchi 	},
2813d9b1a2aSHans Rosenfeld 	{
2823d9b1a2aSHans Rosenfeld 		"get-features",
2833d9b1a2aSHans Rosenfeld 		"get features from controllers and/or namespaces",
2843d9b1a2aSHans Rosenfeld 		NULL,
285533affcbSRobert Mustacchi 		NULL,
286353d89b0SHans Rosenfeld 		do_get_features, usage_get_features, NULL,
287353d89b0SHans Rosenfeld 		NVMEADM_C_MULTI
2883d9b1a2aSHans Rosenfeld 	},
2893d9b1a2aSHans Rosenfeld 	{
2903d9b1a2aSHans Rosenfeld 		"format",
2913d9b1a2aSHans Rosenfeld 		"format namespace(s) of a controller",
2923d9b1a2aSHans Rosenfeld 		NULL,
293533affcbSRobert Mustacchi 		NULL,
294353d89b0SHans Rosenfeld 		do_format, usage_format, NULL,
295353d89b0SHans Rosenfeld 		NVMEADM_C_EXCL
2963d9b1a2aSHans Rosenfeld 	},
2973d9b1a2aSHans Rosenfeld 	{
2983d9b1a2aSHans Rosenfeld 		"secure-erase",
2993d9b1a2aSHans Rosenfeld 		"secure erase namespace(s) of a controller",
3003d9b1a2aSHans Rosenfeld 		"  -c  Do a cryptographic erase.",
301533affcbSRobert Mustacchi 		NULL,
302a3bac573SHans Rosenfeld 		do_secure_erase, usage_secure_erase, optparse_secure_erase,
303353d89b0SHans Rosenfeld 		NVMEADM_C_EXCL
3043d9b1a2aSHans Rosenfeld 	},
3053d9b1a2aSHans Rosenfeld 	{
3063d9b1a2aSHans Rosenfeld 		"detach",
307bbf21555SRichard Lowe 		"detach blkdev(4D) from namespace(s) of a controller",
3083d9b1a2aSHans Rosenfeld 		NULL,
309533affcbSRobert Mustacchi 		NULL,
310533affcbSRobert Mustacchi 		do_detach, usage_attach_detach, NULL,
311353d89b0SHans Rosenfeld 		NVMEADM_C_EXCL
3123d9b1a2aSHans Rosenfeld 	},
3133d9b1a2aSHans Rosenfeld 	{
3143d9b1a2aSHans Rosenfeld 		"attach",
315bbf21555SRichard Lowe 		"attach blkdev(4D) to namespace(s) of a controller",
3163d9b1a2aSHans Rosenfeld 		NULL,
317533affcbSRobert Mustacchi 		NULL,
318533affcbSRobert Mustacchi 		do_attach, usage_attach_detach, NULL,
319353d89b0SHans Rosenfeld 		NVMEADM_C_EXCL
3203d9b1a2aSHans Rosenfeld 	},
32168df0c4fSGuy Morrogh 	{
32268df0c4fSGuy Morrogh 		"list-firmware",
32368df0c4fSGuy Morrogh 		"list firmware on a controller",
32468df0c4fSGuy Morrogh 		NULL,
325533affcbSRobert Mustacchi 		NULL,
326353d89b0SHans Rosenfeld 		do_get_logpage_fwslot, usage_firmware_list, NULL,
327353d89b0SHans Rosenfeld 		0
32868df0c4fSGuy Morrogh 	},
329cf840871SPaul Winder 	{
330cf840871SPaul Winder 		"load-firmware",
331cf840871SPaul Winder 		"load firmware to a controller",
332cf840871SPaul Winder 		NULL,
333533affcbSRobert Mustacchi 		NULL,
334353d89b0SHans Rosenfeld 		do_firmware_load, usage_firmware_load, NULL,
335533affcbSRobert Mustacchi 		NVMEADM_C_EXCL
336cf840871SPaul Winder 	},
337cf840871SPaul Winder 	{
338cf840871SPaul Winder 		"commit-firmware",
339cf840871SPaul Winder 		"commit downloaded firmware to a slot of a controller",
340cf840871SPaul Winder 		NULL,
341533affcbSRobert Mustacchi 		NULL,
342353d89b0SHans Rosenfeld 		do_firmware_commit, usage_firmware_commit, NULL,
343533affcbSRobert Mustacchi 		NVMEADM_C_EXCL
344cf840871SPaul Winder 	},
345cf840871SPaul Winder 	{
346cf840871SPaul Winder 		"activate-firmware",
347cf840871SPaul Winder 		"activate a firmware slot of a controller",
348cf840871SPaul Winder 		NULL,
349533affcbSRobert Mustacchi 		NULL,
350353d89b0SHans Rosenfeld 		do_firmware_activate, usage_firmware_activate, NULL,
351533affcbSRobert Mustacchi 		NVMEADM_C_EXCL
352533affcbSRobert Mustacchi 	},
353533affcbSRobert Mustacchi 	{
354533affcbSRobert Mustacchi 		"wdc/e6dump",
355533affcbSRobert Mustacchi 		"dump WDC e6 diagnostic log",
356533affcbSRobert Mustacchi 		"  -o output\tspecify output file destination\n",
357533affcbSRobert Mustacchi 		NULL,
358533affcbSRobert Mustacchi 		do_wdc_e6dump, usage_wdc_e6dump, optparse_wdc_e6dump,
359533affcbSRobert Mustacchi 		0
360533affcbSRobert Mustacchi 	},
361533affcbSRobert Mustacchi 	{
362533affcbSRobert Mustacchi 		"wdc/resize",
363533affcbSRobert Mustacchi 		"change a WDC device's capacity",
364533affcbSRobert Mustacchi 		"  -g\t\tquery the device's current resized capacity\n"
365533affcbSRobert Mustacchi 		"  -s size\tset the size of a device to the specified in gb",
366533affcbSRobert Mustacchi 		NULL,
367533affcbSRobert Mustacchi 		do_wdc_resize, usage_wdc_resize, optparse_wdc_resize,
368533affcbSRobert Mustacchi 		/*
369533affcbSRobert Mustacchi 		 * We do not set NVMEADM_C_EXCL here as that is handled by the
370533affcbSRobert Mustacchi 		 * vendor unique command logic and operates based on the
371533affcbSRobert Mustacchi 		 * information we get from vuc discovery.
372533affcbSRobert Mustacchi 		 */
373353d89b0SHans Rosenfeld 		0
374cf840871SPaul Winder 	},
3757a27a99aSRobert Mustacchi 	{
3767a27a99aSRobert Mustacchi 		"wdc/clear-assert",
3777a27a99aSRobert Mustacchi 		"clear internal device assertion",
3787a27a99aSRobert Mustacchi 		NULL,
3797a27a99aSRobert Mustacchi 		NULL,
3807a27a99aSRobert Mustacchi 		do_wdc_clear_assert, usage_wdc_clear_assert, NULL
3817a27a99aSRobert Mustacchi 	},
3827a27a99aSRobert Mustacchi 	{
3837a27a99aSRobert Mustacchi 		"wdc/inject-assert",
3847a27a99aSRobert Mustacchi 		"inject internal device assertion",
3857a27a99aSRobert Mustacchi 		NULL,
3867a27a99aSRobert Mustacchi 		NULL,
3877a27a99aSRobert Mustacchi 		do_wdc_inject_assert, usage_wdc_inject_assert, NULL
3887a27a99aSRobert Mustacchi 	},
3893d9b1a2aSHans Rosenfeld 	{
3903d9b1a2aSHans Rosenfeld 		NULL, NULL, NULL,
391353d89b0SHans Rosenfeld 		NULL, NULL, NULL, 0
3923d9b1a2aSHans Rosenfeld 	}
3933d9b1a2aSHans Rosenfeld };
3943d9b1a2aSHans Rosenfeld 
395533affcbSRobert Mustacchi static const nvmeadm_feature_t features[] = {
396533affcbSRobert Mustacchi 	{
397533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_ARBITRATION,
398533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_arbitration
399533affcbSRobert Mustacchi 	}, {
400533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_POWER_MGMT,
401533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_power_mgmt
402533affcbSRobert Mustacchi 	}, {
403533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_LBA_RANGE,
404533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_lba_range
405533affcbSRobert Mustacchi 	}, {
406533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_TEMPERATURE,
407533affcbSRobert Mustacchi 		.f_get = do_get_feat_temp_thresh,
408533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_temperature
409533affcbSRobert Mustacchi 	}, {
410533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_ERROR,
411533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_error
412533affcbSRobert Mustacchi 	}, {
413533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_WRITE_CACHE,
414533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_write_cache
415533affcbSRobert Mustacchi 	}, {
416533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_NQUEUES,
417533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_nqueues
418533affcbSRobert Mustacchi 	}, {
419533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_INTR_COAL,
420533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_intr_coal
421533affcbSRobert Mustacchi 	}, {
422533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_INTR_VECT,
423533affcbSRobert Mustacchi 		.f_get = do_get_feat_intr_vect,
424533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_intr_vect
425533affcbSRobert Mustacchi 	}, {
426533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_WRITE_ATOM,
427533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_write_atom
428533affcbSRobert Mustacchi 	}, {
429533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_ASYNC_EVENT,
430533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_async_event
431533affcbSRobert Mustacchi 	}, {
432533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_AUTO_PST,
433533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_auto_pst
434533affcbSRobert Mustacchi 	}, {
435533affcbSRobert Mustacchi 		.f_feature = NVME_FEAT_PROGRESS,
436533affcbSRobert Mustacchi 		.f_print = nvme_print_feat_progress
437533affcbSRobert Mustacchi 	}
4383d9b1a2aSHans Rosenfeld };
4393d9b1a2aSHans Rosenfeld 
440533affcbSRobert Mustacchi static void
nvmeadm_ctrl_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)441533affcbSRobert Mustacchi nvmeadm_ctrl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap)
442533affcbSRobert Mustacchi {
443533affcbSRobert Mustacchi 	nvme_ctrl_t *ctrl = npa->npa_ctrl;
444533affcbSRobert Mustacchi 
445533affcbSRobert Mustacchi 	(void) fprintf(stderr, "nvmeadm: ");
446533affcbSRobert Mustacchi 	(void) vfprintf(stderr, fmt, ap);
447533affcbSRobert Mustacchi 	(void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n",
448533affcbSRobert Mustacchi 	    nvme_ctrl_errmsg(ctrl), nvme_ctrl_errtostr(npa->npa_ctrl,
449533affcbSRobert Mustacchi 	    nvme_ctrl_err(ctrl)), nvme_ctrl_err(ctrl), nvme_ctrl_syserr(ctrl));
450533affcbSRobert Mustacchi }
451533affcbSRobert Mustacchi 
452533affcbSRobert Mustacchi static void
nvmeadm_hdl_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)453533affcbSRobert Mustacchi nvmeadm_hdl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap)
454533affcbSRobert Mustacchi {
455533affcbSRobert Mustacchi 	nvme_t *nvme = npa->npa_nvme;
456533affcbSRobert Mustacchi 
457533affcbSRobert Mustacchi 	(void) fprintf(stderr, "nvmeadm: ");
458533affcbSRobert Mustacchi 	(void) vfprintf(stderr, fmt, ap);
459533affcbSRobert Mustacchi 	(void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n",
460533affcbSRobert Mustacchi 	    nvme_errmsg(nvme), nvme_errtostr(nvme, nvme_err(nvme)),
461533affcbSRobert Mustacchi 	    nvme_err(nvme), nvme_syserr(nvme));
462533affcbSRobert Mustacchi }
463533affcbSRobert Mustacchi 
464533affcbSRobert Mustacchi static void
nvmeadm_ctrl_info_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)465533affcbSRobert Mustacchi nvmeadm_ctrl_info_vwarn(const nvme_process_arg_t *npa, const char *fmt,
466533affcbSRobert Mustacchi     va_list ap)
467533affcbSRobert Mustacchi {
468533affcbSRobert Mustacchi 	nvme_ctrl_info_t *info = npa->npa_ctrl_info;
469533affcbSRobert Mustacchi 
470533affcbSRobert Mustacchi 	(void) fprintf(stderr, "nvmeadm: ");
471533affcbSRobert Mustacchi 	(void) vfprintf(stderr, fmt, ap);
472533affcbSRobert Mustacchi 	(void) fprintf(stderr, ": %s: %s (libnvme info: 0x%x, sys: %d)\n",
473533affcbSRobert Mustacchi 	    nvme_ctrl_info_errmsg(info), nvme_ctrl_info_errtostr(info,
474533affcbSRobert Mustacchi 	    nvme_ctrl_info_err(info)), nvme_ctrl_info_err(info),
475533affcbSRobert Mustacchi 	    nvme_ctrl_info_syserr(info));
476533affcbSRobert Mustacchi }
477533affcbSRobert Mustacchi 
478533affcbSRobert Mustacchi void
nvmeadm_warn(const nvme_process_arg_t * npa,const char * fmt,...)479533affcbSRobert Mustacchi nvmeadm_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
480533affcbSRobert Mustacchi {
481533affcbSRobert Mustacchi 	va_list ap;
482533affcbSRobert Mustacchi 
483533affcbSRobert Mustacchi 	va_start(ap, fmt);
484533affcbSRobert Mustacchi 	nvmeadm_ctrl_vwarn(npa, fmt, ap);
485533affcbSRobert Mustacchi 	va_end(ap);
486533affcbSRobert Mustacchi }
487533affcbSRobert Mustacchi 
488533affcbSRobert Mustacchi void __NORETURN
nvmeadm_fatal(const nvme_process_arg_t * npa,const char * fmt,...)489533affcbSRobert Mustacchi nvmeadm_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
490533affcbSRobert Mustacchi {
491533affcbSRobert Mustacchi 	va_list ap;
492533affcbSRobert Mustacchi 
493533affcbSRobert Mustacchi 	va_start(ap, fmt);
494533affcbSRobert Mustacchi 	nvmeadm_ctrl_vwarn(npa, fmt, ap);
495533affcbSRobert Mustacchi 	va_end(ap);
496533affcbSRobert Mustacchi 
497533affcbSRobert Mustacchi 	exit(-1);
498533affcbSRobert Mustacchi }
499533affcbSRobert Mustacchi 
500533affcbSRobert Mustacchi void
nvmeadm_hdl_warn(const nvme_process_arg_t * npa,const char * fmt,...)501533affcbSRobert Mustacchi nvmeadm_hdl_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
502533affcbSRobert Mustacchi {
503533affcbSRobert Mustacchi 	va_list ap;
504533affcbSRobert Mustacchi 
505533affcbSRobert Mustacchi 	va_start(ap, fmt);
506533affcbSRobert Mustacchi 	nvmeadm_hdl_vwarn(npa, fmt, ap);
507533affcbSRobert Mustacchi 	va_end(ap);
508533affcbSRobert Mustacchi }
509533affcbSRobert Mustacchi 
510533affcbSRobert Mustacchi void __NORETURN
nvmeadm_hdl_fatal(const nvme_process_arg_t * npa,const char * fmt,...)511533affcbSRobert Mustacchi nvmeadm_hdl_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
512533affcbSRobert Mustacchi {
513533affcbSRobert Mustacchi 	va_list ap;
514533affcbSRobert Mustacchi 
515533affcbSRobert Mustacchi 	va_start(ap, fmt);
516533affcbSRobert Mustacchi 	nvmeadm_hdl_vwarn(npa, fmt, ap);
517533affcbSRobert Mustacchi 	va_end(ap);
518533affcbSRobert Mustacchi 
519533affcbSRobert Mustacchi 	exit(-1);
520533affcbSRobert Mustacchi }
521533affcbSRobert Mustacchi 
522533affcbSRobert Mustacchi static void
nvmeadm_ctrl_info_warn(const nvme_process_arg_t * npa,const char * fmt,...)523533affcbSRobert Mustacchi nvmeadm_ctrl_info_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
524533affcbSRobert Mustacchi {
525533affcbSRobert Mustacchi 	va_list ap;
526533affcbSRobert Mustacchi 
527533affcbSRobert Mustacchi 	va_start(ap, fmt);
528533affcbSRobert Mustacchi 	nvmeadm_ctrl_info_vwarn(npa, fmt, ap);
529533affcbSRobert Mustacchi 	va_end(ap);
530533affcbSRobert Mustacchi }
531533affcbSRobert Mustacchi 
532533affcbSRobert Mustacchi static void
nvmeadm_ctrl_info_fatal(const nvme_process_arg_t * npa,const char * fmt,...)533533affcbSRobert Mustacchi nvmeadm_ctrl_info_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
534533affcbSRobert Mustacchi {
535533affcbSRobert Mustacchi 	va_list ap;
536533affcbSRobert Mustacchi 
537533affcbSRobert Mustacchi 	va_start(ap, fmt);
538533affcbSRobert Mustacchi 	nvmeadm_ctrl_info_vwarn(npa, fmt, ap);
539533affcbSRobert Mustacchi 	va_end(ap);
540533affcbSRobert Mustacchi 
541533affcbSRobert Mustacchi 	exit(-1);
542533affcbSRobert Mustacchi }
543533affcbSRobert Mustacchi 
544533affcbSRobert Mustacchi boolean_t
nvme_version_check(const nvme_process_arg_t * npa,const nvme_version_t * vers)545533affcbSRobert Mustacchi nvme_version_check(const nvme_process_arg_t *npa, const nvme_version_t *vers)
546533affcbSRobert Mustacchi {
547533affcbSRobert Mustacchi 	return (nvme_vers_atleast(npa->npa_version, vers) ? B_TRUE : B_FALSE);
548533affcbSRobert Mustacchi }
549533affcbSRobert Mustacchi 
550533affcbSRobert Mustacchi /*
551533affcbSRobert Mustacchi  * Because nvmeadm operates on a series of NVMe devices for several commands,
552533affcbSRobert Mustacchi  * here we need to clean up everything that we allocated for this device so we
553533affcbSRobert Mustacchi  * can prepare for the next.
554533affcbSRobert Mustacchi  */
555533affcbSRobert Mustacchi static void
nvmeadm_cleanup_npa(nvme_process_arg_t * npa)556533affcbSRobert Mustacchi nvmeadm_cleanup_npa(nvme_process_arg_t *npa)
557533affcbSRobert Mustacchi {
558533affcbSRobert Mustacchi 	npa->npa_idctl = NULL;
559533affcbSRobert Mustacchi 	npa->npa_version = NULL;
560533affcbSRobert Mustacchi 
561533affcbSRobert Mustacchi 	if (npa->npa_excl) {
562533affcbSRobert Mustacchi 		if (npa->npa_ns != NULL) {
563533affcbSRobert Mustacchi 			nvme_ns_unlock(npa->npa_ns);
564533affcbSRobert Mustacchi 		} else if (npa->npa_ctrl != NULL) {
565533affcbSRobert Mustacchi 			nvme_ctrl_unlock(npa->npa_ctrl);
566533affcbSRobert Mustacchi 		}
567533affcbSRobert Mustacchi 	}
568533affcbSRobert Mustacchi 
569533affcbSRobert Mustacchi 	if (npa->npa_ns_info != NULL) {
570533affcbSRobert Mustacchi 		nvme_ns_info_free(npa->npa_ns_info);
571533affcbSRobert Mustacchi 		npa->npa_ns_info = NULL;
572533affcbSRobert Mustacchi 	}
573533affcbSRobert Mustacchi 
574533affcbSRobert Mustacchi 	if (npa->npa_ctrl_info != NULL) {
575533affcbSRobert Mustacchi 		nvme_ctrl_info_free(npa->npa_ctrl_info);
576533affcbSRobert Mustacchi 		npa->npa_ctrl_info = NULL;
577533affcbSRobert Mustacchi 	}
578533affcbSRobert Mustacchi 
579533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
580533affcbSRobert Mustacchi 		nvme_ns_fini(npa->npa_ns);
581533affcbSRobert Mustacchi 		npa->npa_ns = NULL;
582533affcbSRobert Mustacchi 	}
583533affcbSRobert Mustacchi 
584533affcbSRobert Mustacchi 	if (npa->npa_ctrl != NULL) {
585533affcbSRobert Mustacchi 		nvme_ctrl_fini(npa->npa_ctrl);
586533affcbSRobert Mustacchi 		npa->npa_ctrl = NULL;
587533affcbSRobert Mustacchi 	}
588533affcbSRobert Mustacchi }
589533affcbSRobert Mustacchi 
590533affcbSRobert Mustacchi /*
591533affcbSRobert Mustacchi  * Determine if a command requires a controller or namespace write lock. If so
592533affcbSRobert Mustacchi  * we first attempt to grab it non-blocking and then if that fails, we'll warn
593533affcbSRobert Mustacchi  * that we may be blocking for the lock so that way the user has a chance to do
594533affcbSRobert Mustacchi  * something and can cancel it.
595533affcbSRobert Mustacchi  */
596533affcbSRobert Mustacchi static void
nvmeadm_excl(const nvme_process_arg_t * npa,nvme_lock_level_t level)597533affcbSRobert Mustacchi nvmeadm_excl(const nvme_process_arg_t *npa, nvme_lock_level_t level)
598533affcbSRobert Mustacchi {
599533affcbSRobert Mustacchi 	bool ret;
600533affcbSRobert Mustacchi 	nvme_lock_flags_t flags = NVME_LOCK_F_DONT_BLOCK;
601533affcbSRobert Mustacchi 
602533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
603533affcbSRobert Mustacchi 		ret = nvme_ns_lock(npa->npa_ns, level, flags);
604533affcbSRobert Mustacchi 	} else {
605533affcbSRobert Mustacchi 		ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags);
606533affcbSRobert Mustacchi 	}
607533affcbSRobert Mustacchi 
608533affcbSRobert Mustacchi 	if (ret) {
609533affcbSRobert Mustacchi 		return;
610533affcbSRobert Mustacchi 	}
611533affcbSRobert Mustacchi 
612533affcbSRobert Mustacchi 	if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_LOCK_WOULD_BLOCK) {
613533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to acquire lock on %s",
614533affcbSRobert Mustacchi 		    npa->npa_name);
615533affcbSRobert Mustacchi 	}
616533affcbSRobert Mustacchi 
617533affcbSRobert Mustacchi 	(void) fprintf(stderr, "Waiting on contended %s lock on %s...",
618533affcbSRobert Mustacchi 	    npa->npa_ns != NULL ? "namespace": "controller", npa->npa_name);
619533affcbSRobert Mustacchi 	(void) fflush(stderr);
620533affcbSRobert Mustacchi 
621533affcbSRobert Mustacchi 	flags &= ~NVME_LOCK_F_DONT_BLOCK;
622533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
623533affcbSRobert Mustacchi 		ret = nvme_ns_lock(npa->npa_ns, level, flags);
624533affcbSRobert Mustacchi 	} else {
625533affcbSRobert Mustacchi 		ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags);
626533affcbSRobert Mustacchi 	}
627533affcbSRobert Mustacchi 
628533affcbSRobert Mustacchi 	if (!ret) {
629533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to acquire lock on %s",
630533affcbSRobert Mustacchi 		    npa->npa_name);
631533affcbSRobert Mustacchi 	}
632533affcbSRobert Mustacchi 
633533affcbSRobert Mustacchi 	(void) fprintf(stderr, " acquired\n");
634533affcbSRobert Mustacchi }
635533affcbSRobert Mustacchi 
636533affcbSRobert Mustacchi /*
637533affcbSRobert Mustacchi  * Most of nvmeadm was written before the existence of libnvme and always had
638533affcbSRobert Mustacchi  * things like the identify controller or namespace information sitting around.
639533affcbSRobert Mustacchi  * As such we try to grab all this in one place for it. Note, regardless if this
640533affcbSRobert Mustacchi  * succeeds or fails, our callers will still call nvmeadm_cleanup_npa() so we
641533affcbSRobert Mustacchi  * don't need to clean up the various libnvme objects.
642533affcbSRobert Mustacchi  */
643533affcbSRobert Mustacchi static boolean_t
nvmeadm_open_dev(nvme_process_arg_t * npa)644533affcbSRobert Mustacchi nvmeadm_open_dev(nvme_process_arg_t *npa)
645533affcbSRobert Mustacchi {
646533affcbSRobert Mustacchi 	if (!nvme_ctrl_ns_init(npa->npa_nvme, npa->npa_name, &npa->npa_ctrl,
647533affcbSRobert Mustacchi 	    &npa->npa_ns)) {
648533affcbSRobert Mustacchi 		nvmeadm_hdl_warn(npa, "failed to open '%s'", npa->npa_name);
649533affcbSRobert Mustacchi 		exitcode = -1;
650533affcbSRobert Mustacchi 		return (B_FALSE);
651533affcbSRobert Mustacchi 	}
652533affcbSRobert Mustacchi 
653533affcbSRobert Mustacchi 	/*
654533affcbSRobert Mustacchi 	 * Several commands expect to be able to access the controller's
655533affcbSRobert Mustacchi 	 * information snapshot. Grab that now for it and the namespace if it
656533affcbSRobert Mustacchi 	 * exists.
657533affcbSRobert Mustacchi 	 */
658533affcbSRobert Mustacchi 	if (!nvme_ctrl_info_snap(npa->npa_ctrl, &npa->npa_ctrl_info)) {
659533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to get controller info for %s",
660533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
661533affcbSRobert Mustacchi 		exitcode = -1;
662533affcbSRobert Mustacchi 		return (B_FALSE);
663533affcbSRobert Mustacchi 	}
664533affcbSRobert Mustacchi 
665533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL && !nvme_ns_info_snap(npa->npa_ns,
666533affcbSRobert Mustacchi 	    &npa->npa_ns_info)) {
667533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to get namespace info for %s",
668533affcbSRobert Mustacchi 		    npa->npa_name);
669533affcbSRobert Mustacchi 		exitcode = -1;
670533affcbSRobert Mustacchi 		return (B_FALSE);
671533affcbSRobert Mustacchi 	}
672533affcbSRobert Mustacchi 
673533affcbSRobert Mustacchi 	/*
674533affcbSRobert Mustacchi 	 * Snapshot data the rest of the command has fairly ingrained.
675533affcbSRobert Mustacchi 	 */
676533affcbSRobert Mustacchi 	npa->npa_version = nvme_ctrl_info_version(npa->npa_ctrl_info);
677533affcbSRobert Mustacchi 	npa->npa_idctl = nvme_ctrl_info_identify(npa->npa_ctrl_info);
678533affcbSRobert Mustacchi 
679533affcbSRobert Mustacchi 	/*
680533affcbSRobert Mustacchi 	 * If this command has requested exclusive access, proceed to grab that
681533affcbSRobert Mustacchi 	 * before we continue.
682533affcbSRobert Mustacchi 	 */
683533affcbSRobert Mustacchi 	if (npa->npa_excl) {
684533affcbSRobert Mustacchi 		nvmeadm_excl(npa, NVME_LOCK_L_WRITE);
685533affcbSRobert Mustacchi 	}
686533affcbSRobert Mustacchi 
687533affcbSRobert Mustacchi 	return (B_TRUE);
688533affcbSRobert Mustacchi }
689533affcbSRobert Mustacchi 
690533affcbSRobert Mustacchi static bool
nvmeadm_ctrl_disc_cb(nvme_t * nvme,const nvme_ctrl_disc_t * disc,void * arg)691533affcbSRobert Mustacchi nvmeadm_ctrl_disc_cb(nvme_t *nvme, const nvme_ctrl_disc_t *disc, void *arg)
692533affcbSRobert Mustacchi {
693533affcbSRobert Mustacchi 	nvme_process_arg_t *npa = arg;
694533affcbSRobert Mustacchi 	di_node_t di = nvme_ctrl_disc_devi(disc);
695533affcbSRobert Mustacchi 	char name[128];
696533affcbSRobert Mustacchi 
697533affcbSRobert Mustacchi 	(void) snprintf(name, sizeof (name), "%s%d", di_driver_name(di),
698533affcbSRobert Mustacchi 	    di_instance(di));
699533affcbSRobert Mustacchi 	npa->npa_name = name;
700533affcbSRobert Mustacchi 	npa->npa_ctrl_name = name;
701533affcbSRobert Mustacchi 
702533affcbSRobert Mustacchi 	if (nvmeadm_open_dev(npa)) {
703533affcbSRobert Mustacchi 		if (npa->npa_cmd->c_func(npa) != 0) {
704533affcbSRobert Mustacchi 			exitcode = -1;
705533affcbSRobert Mustacchi 		}
706533affcbSRobert Mustacchi 	}
707533affcbSRobert Mustacchi 
708533affcbSRobert Mustacchi 	nvmeadm_cleanup_npa(npa);
709533affcbSRobert Mustacchi 	return (true);
710533affcbSRobert Mustacchi }
7113d9b1a2aSHans Rosenfeld 
7123d9b1a2aSHans Rosenfeld int
main(int argc,char ** argv)7133d9b1a2aSHans Rosenfeld main(int argc, char **argv)
7143d9b1a2aSHans Rosenfeld {
7153d9b1a2aSHans Rosenfeld 	int c;
7163d9b1a2aSHans Rosenfeld 	const nvmeadm_cmd_t *cmd;
7173d9b1a2aSHans Rosenfeld 	nvme_process_arg_t npa = { 0 };
7183d9b1a2aSHans Rosenfeld 	int help = 0;
719cf988e4aSRobert Mustacchi 	char *ctrl = NULL;
7203d9b1a2aSHans Rosenfeld 
7213d9b1a2aSHans Rosenfeld 	while ((c = getopt(argc, argv, "dhv")) != -1) {
7223d9b1a2aSHans Rosenfeld 		switch (c) {
7233d9b1a2aSHans Rosenfeld 		case 'd':
7243d9b1a2aSHans Rosenfeld 			debug++;
7253d9b1a2aSHans Rosenfeld 			break;
726153f3212SHans Rosenfeld 
7273d9b1a2aSHans Rosenfeld 		case 'v':
7283d9b1a2aSHans Rosenfeld 			verbose++;
7293d9b1a2aSHans Rosenfeld 			break;
730153f3212SHans Rosenfeld 
7313d9b1a2aSHans Rosenfeld 		case 'h':
7323d9b1a2aSHans Rosenfeld 			help++;
7333d9b1a2aSHans Rosenfeld 			break;
734153f3212SHans Rosenfeld 
7353d9b1a2aSHans Rosenfeld 		case '?':
7363d9b1a2aSHans Rosenfeld 			usage(NULL);
7373d9b1a2aSHans Rosenfeld 			exit(-1);
7383d9b1a2aSHans Rosenfeld 		}
7393d9b1a2aSHans Rosenfeld 	}
7403d9b1a2aSHans Rosenfeld 
7413d9b1a2aSHans Rosenfeld 	if (optind == argc) {
7423d9b1a2aSHans Rosenfeld 		usage(NULL);
7433d9b1a2aSHans Rosenfeld 		if (help)
7443d9b1a2aSHans Rosenfeld 			exit(0);
7453d9b1a2aSHans Rosenfeld 		else
7463d9b1a2aSHans Rosenfeld 			exit(-1);
7473d9b1a2aSHans Rosenfeld 	}
7483d9b1a2aSHans Rosenfeld 
7493d9b1a2aSHans Rosenfeld 	/* Look up the specified command in the command table. */
7503d9b1a2aSHans Rosenfeld 	for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
7513d9b1a2aSHans Rosenfeld 		if (strcmp(cmd->c_name, argv[optind]) == 0)
7523d9b1a2aSHans Rosenfeld 			break;
7533d9b1a2aSHans Rosenfeld 
7543d9b1a2aSHans Rosenfeld 	if (cmd->c_name == NULL) {
7553d9b1a2aSHans Rosenfeld 		usage(NULL);
7563d9b1a2aSHans Rosenfeld 		exit(-1);
7573d9b1a2aSHans Rosenfeld 	}
7583d9b1a2aSHans Rosenfeld 
7593d9b1a2aSHans Rosenfeld 	if (help) {
7603d9b1a2aSHans Rosenfeld 		usage(cmd);
7613d9b1a2aSHans Rosenfeld 		exit(0);
7623d9b1a2aSHans Rosenfeld 	}
7633d9b1a2aSHans Rosenfeld 
764533affcbSRobert Mustacchi 	npa.npa_nvme = nvme_init();
765533affcbSRobert Mustacchi 	if (npa.npa_nvme == NULL) {
766533affcbSRobert Mustacchi 		err(-1, "failed to initialize libnvme");
767533affcbSRobert Mustacchi 	}
7683d9b1a2aSHans Rosenfeld 	npa.npa_cmd = cmd;
769353d89b0SHans Rosenfeld 	npa.npa_excl = ((cmd->c_flags & NVMEADM_C_EXCL) != 0);
7703d9b1a2aSHans Rosenfeld 
7713d9b1a2aSHans Rosenfeld 	optind++;
7723d9b1a2aSHans Rosenfeld 
773e4cc4004SRobert Mustacchi 	/*
774e4cc4004SRobert Mustacchi 	 * Store the remaining arguments for use by the command. Give the
775e4cc4004SRobert Mustacchi 	 * command a chance to process the options across the board before going
776e4cc4004SRobert Mustacchi 	 * into each controller.
777e4cc4004SRobert Mustacchi 	 */
778e4cc4004SRobert Mustacchi 	npa.npa_argc = argc - optind;
779e4cc4004SRobert Mustacchi 	npa.npa_argv = &argv[optind];
780e4cc4004SRobert Mustacchi 
781e4cc4004SRobert Mustacchi 	if (cmd->c_optparse != NULL) {
782533affcbSRobert Mustacchi 		optind = 0;
783e4cc4004SRobert Mustacchi 		cmd->c_optparse(&npa);
784533affcbSRobert Mustacchi 		npa.npa_argc -= optind;
785533affcbSRobert Mustacchi 		npa.npa_argv += optind;
786e4cc4004SRobert Mustacchi 	}
787e4cc4004SRobert Mustacchi 
7883d9b1a2aSHans Rosenfeld 	/*
789cf988e4aSRobert Mustacchi 	 * All commands but "list" require a ctl/ns argument. However, this
790cf988e4aSRobert Mustacchi 	 * should not be passed through to the command in its subsequent
791cf988e4aSRobert Mustacchi 	 * arguments.
7923d9b1a2aSHans Rosenfeld 	 */
793533affcbSRobert Mustacchi 	if (npa.npa_argc == 0 && cmd->c_func != do_list) {
7943d9b1a2aSHans Rosenfeld 		warnx("missing controller/namespace name");
7953d9b1a2aSHans Rosenfeld 		usage(cmd);
7963d9b1a2aSHans Rosenfeld 		exit(-1);
7973d9b1a2aSHans Rosenfeld 	}
7983d9b1a2aSHans Rosenfeld 
799cf988e4aSRobert Mustacchi 	if (npa.npa_argc > 0) {
800cf988e4aSRobert Mustacchi 		ctrl = npa.npa_argv[0];
801cf988e4aSRobert Mustacchi 		npa.npa_argv++;
802cf988e4aSRobert Mustacchi 		npa.npa_argc--;
803cf988e4aSRobert Mustacchi 	} else {
804533affcbSRobert Mustacchi 		if (!nvme_ctrl_discover(npa.npa_nvme, nvmeadm_ctrl_disc_cb,
805533affcbSRobert Mustacchi 		    &npa)) {
806533affcbSRobert Mustacchi 			nvmeadm_hdl_fatal(&npa, "failed to walk controllers");
807533affcbSRobert Mustacchi 		}
808533affcbSRobert Mustacchi 		exit(exitcode);
809cf988e4aSRobert Mustacchi 	}
810cf988e4aSRobert Mustacchi 
8113d9b1a2aSHans Rosenfeld 	/*
8123d9b1a2aSHans Rosenfeld 	 * Make sure we're not running commands on multiple controllers that
8133d9b1a2aSHans Rosenfeld 	 * aren't allowed to do that.
8143d9b1a2aSHans Rosenfeld 	 */
815cf988e4aSRobert Mustacchi 	if (ctrl != NULL && strchr(ctrl, ',') != NULL &&
816353d89b0SHans Rosenfeld 	    (cmd->c_flags & NVMEADM_C_MULTI) == 0) {
8173d9b1a2aSHans Rosenfeld 		warnx("%s not allowed on multiple controllers",
8183d9b1a2aSHans Rosenfeld 		    cmd->c_name);
8193d9b1a2aSHans Rosenfeld 		usage(cmd);
8203d9b1a2aSHans Rosenfeld 		exit(-1);
8213d9b1a2aSHans Rosenfeld 	}
8223d9b1a2aSHans Rosenfeld 
8233d9b1a2aSHans Rosenfeld 	/*
8243d9b1a2aSHans Rosenfeld 	 * Get controller/namespace arguments and run command.
8253d9b1a2aSHans Rosenfeld 	 */
826533affcbSRobert Mustacchi 	while ((npa.npa_name = strsep(&ctrl, ",")) != NULL) {
827533affcbSRobert Mustacchi 		char *ctrl_name, *slash;
8283d9b1a2aSHans Rosenfeld 
829533affcbSRobert Mustacchi 		/*
830533affcbSRobert Mustacchi 		 * We may be given just a controller as an argument or a
831533affcbSRobert Mustacchi 		 * controller and a namespace as an argument. Parts of the
832533affcbSRobert Mustacchi 		 * commands want to know what controller they're referring to
833533affcbSRobert Mustacchi 		 * even if the overall argument was for a namespace. So we
834533affcbSRobert Mustacchi 		 * always dup the argument and try to make the controller out of
835533affcbSRobert Mustacchi 		 * it.
836533affcbSRobert Mustacchi 		 */
837533affcbSRobert Mustacchi 		ctrl_name = strdup(npa.npa_name);
838533affcbSRobert Mustacchi 		if (ctrl_name == NULL) {
839533affcbSRobert Mustacchi 			err(-1, "failed to duplicate NVMe controller/namespace "
840533affcbSRobert Mustacchi 			    "name");
841533affcbSRobert Mustacchi 		}
842533affcbSRobert Mustacchi 		if ((slash = strchr(ctrl_name, '/')) != NULL)
843533affcbSRobert Mustacchi 			*slash = '\0';
844533affcbSRobert Mustacchi 		npa.npa_ctrl_name = ctrl_name;
8453d9b1a2aSHans Rosenfeld 
846533affcbSRobert Mustacchi 		if (nvmeadm_open_dev(&npa)) {
847533affcbSRobert Mustacchi 			if (npa.npa_cmd->c_func(&npa) != 0) {
848533affcbSRobert Mustacchi 				exitcode = -1;
8493d9b1a2aSHans Rosenfeld 			}
8503d9b1a2aSHans Rosenfeld 		}
851533affcbSRobert Mustacchi 
852533affcbSRobert Mustacchi 		nvmeadm_cleanup_npa(&npa);
853533affcbSRobert Mustacchi 		free(ctrl_name);
854533affcbSRobert Mustacchi 	}
8553d9b1a2aSHans Rosenfeld 
8563d9b1a2aSHans Rosenfeld 	exit(exitcode);
8573d9b1a2aSHans Rosenfeld }
8583d9b1a2aSHans Rosenfeld 
859e4cc4004SRobert Mustacchi static void
nvme_oferr(const char * fmt,...)860e4cc4004SRobert Mustacchi nvme_oferr(const char *fmt, ...)
861e4cc4004SRobert Mustacchi {
862e4cc4004SRobert Mustacchi 	va_list ap;
863e4cc4004SRobert Mustacchi 
864e4cc4004SRobert Mustacchi 	va_start(ap, fmt);
865e4cc4004SRobert Mustacchi 	verrx(-1, fmt, ap);
866e4cc4004SRobert Mustacchi }
867e4cc4004SRobert Mustacchi 
8683d9b1a2aSHans Rosenfeld static void
usage(const nvmeadm_cmd_t * cmd)8693d9b1a2aSHans Rosenfeld usage(const nvmeadm_cmd_t *cmd)
8703d9b1a2aSHans Rosenfeld {
871a3bac573SHans Rosenfeld 	const char *progname = getprogname();
872a3bac573SHans Rosenfeld 
8733d9b1a2aSHans Rosenfeld 	(void) fprintf(stderr, "usage:\n");
874a3bac573SHans Rosenfeld 	(void) fprintf(stderr, "  %s -h %s\n", progname,
8753d9b1a2aSHans Rosenfeld 	    cmd != NULL ? cmd->c_name : "[<command>]");
876a3bac573SHans Rosenfeld 	(void) fprintf(stderr, "  %s [-dv] ", progname);
8773d9b1a2aSHans Rosenfeld 
8783d9b1a2aSHans Rosenfeld 	if (cmd != NULL) {
8793d9b1a2aSHans Rosenfeld 		cmd->c_usage(cmd->c_name);
8803d9b1a2aSHans Rosenfeld 	} else {
8813d9b1a2aSHans Rosenfeld 		(void) fprintf(stderr,
8823d9b1a2aSHans Rosenfeld 		    "<command> <ctl>[/<ns>][,...] [<args>]\n");
8833d9b1a2aSHans Rosenfeld 		(void) fprintf(stderr,
8843d9b1a2aSHans Rosenfeld 		    "\n  Manage NVMe controllers and namespaces.\n");
8853d9b1a2aSHans Rosenfeld 		(void) fprintf(stderr, "\ncommands:\n");
8863d9b1a2aSHans Rosenfeld 
887153f3212SHans Rosenfeld 		for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) {
888153f3212SHans Rosenfeld 			/*
889153f3212SHans Rosenfeld 			 * The longest nvmeadm subcommand is 19 characters long.
890153f3212SHans Rosenfeld 			 * The format string needs to be updated every time a
891153f3212SHans Rosenfeld 			 * longer subcommand is added.
892153f3212SHans Rosenfeld 			 */
893153f3212SHans Rosenfeld 			(void) fprintf(stderr, "  %-19s - %s\n",
8943d9b1a2aSHans Rosenfeld 			    cmd->c_name, cmd->c_desc);
895153f3212SHans Rosenfeld 		}
8963d9b1a2aSHans Rosenfeld 	}
897a3bac573SHans Rosenfeld 	(void) fprintf(stderr, "\n%s flags:\n"
898e4cc4004SRobert Mustacchi 	    "  -h\t\tprint usage information\n"
899e4cc4004SRobert Mustacchi 	    "  -d\t\tprint information useful for debugging %s\n"
900a3bac573SHans Rosenfeld 	    "  -v\t\tprint verbose information\n",
901a3bac573SHans Rosenfeld 	    progname, progname);
902a3bac573SHans Rosenfeld 
903a3bac573SHans Rosenfeld 	if (cmd != NULL && cmd->c_flagdesc != NULL) {
904a3bac573SHans Rosenfeld 		(void) fprintf(stderr, "\n%s %s flags:\n",
905a3bac573SHans Rosenfeld 		    progname, cmd->c_name);
9063d9b1a2aSHans Rosenfeld 		(void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
907a3bac573SHans Rosenfeld 	}
908670f080bSHans Rosenfeld 
909533affcbSRobert Mustacchi 	if (cmd != NULL && cmd->c_fielddesc != NULL) {
910533affcbSRobert Mustacchi 		(void) fprintf(stderr, "\n%s %s valid fields:\n",
911533affcbSRobert Mustacchi 		    progname, cmd->c_name);
912533affcbSRobert Mustacchi 		(void) fprintf(stderr, "%s\n", cmd->c_fielddesc);
913670f080bSHans Rosenfeld 	}
9143d9b1a2aSHans Rosenfeld }
9153d9b1a2aSHans Rosenfeld 
9163d9b1a2aSHans Rosenfeld char *
nvme_dskname(di_node_t ctrl,const char * bd_addr)917533affcbSRobert Mustacchi nvme_dskname(di_node_t ctrl, const char *bd_addr)
9183d9b1a2aSHans Rosenfeld {
9193d9b1a2aSHans Rosenfeld 	di_dim_t dim;
920b602cbcdSManjith Gambhir 	char *diskname = NULL;
9213d9b1a2aSHans Rosenfeld 
9223d9b1a2aSHans Rosenfeld 	dim = di_dim_init();
923533affcbSRobert Mustacchi 	if (dim == NULL) {
924533affcbSRobert Mustacchi 		err(-1, "failed to initialize devinfo minor translation");
925533affcbSRobert Mustacchi 	}
9263d9b1a2aSHans Rosenfeld 
927533affcbSRobert Mustacchi 	for (di_node_t child = di_child_node(ctrl); child != DI_NODE_NIL;
9283d9b1a2aSHans Rosenfeld 	    child = di_sibling_node(child)) {
929533affcbSRobert Mustacchi 		char *disk_ctd, *path = NULL;
930533affcbSRobert Mustacchi 		const char *addr = di_bus_addr(child);
9313d9b1a2aSHans Rosenfeld 		if (addr == NULL)
9323d9b1a2aSHans Rosenfeld 			continue;
9333d9b1a2aSHans Rosenfeld 
934533affcbSRobert Mustacchi 		if (strcmp(addr, bd_addr) != 0)
9353d9b1a2aSHans Rosenfeld 			continue;
9363d9b1a2aSHans Rosenfeld 
9373d9b1a2aSHans Rosenfeld 		path = di_dim_path_dev(dim, di_driver_name(child),
9383d9b1a2aSHans Rosenfeld 		    di_instance(child), "c");
9393d9b1a2aSHans Rosenfeld 
940e7e9ed12SHans Rosenfeld 		/*
941e7e9ed12SHans Rosenfeld 		 * Error out if we didn't get a path, or if it's too short for
942e7e9ed12SHans Rosenfeld 		 * the following operations to be safe.
943e7e9ed12SHans Rosenfeld 		 */
944533affcbSRobert Mustacchi 		if (path == NULL || strlen(path) < 2) {
945533affcbSRobert Mustacchi 			errx(-1, "failed to get a valid minor path");
946533affcbSRobert Mustacchi 		}
947e7e9ed12SHans Rosenfeld 
948e7e9ed12SHans Rosenfeld 		/* Chop off 's0' and get everything past the last '/' */
949e7e9ed12SHans Rosenfeld 		path[strlen(path) - 2] = '\0';
950b602cbcdSManjith Gambhir 		disk_ctd = strrchr(path, '/');
951533affcbSRobert Mustacchi 		if (disk_ctd == NULL) {
952533affcbSRobert Mustacchi 			errx(-1, "encountered malformed minor path: %s", path);
953533affcbSRobert Mustacchi 		}
954533affcbSRobert Mustacchi 
955b602cbcdSManjith Gambhir 		diskname = strdup(++disk_ctd);
956533affcbSRobert Mustacchi 		if (diskname == NULL) {
957533affcbSRobert Mustacchi 			err(-1, "failed to duplicate disk path");
958533affcbSRobert Mustacchi 		}
9593d9b1a2aSHans Rosenfeld 
960b602cbcdSManjith Gambhir 		free(path);
9613d9b1a2aSHans Rosenfeld 		break;
9623d9b1a2aSHans Rosenfeld 	}
9633d9b1a2aSHans Rosenfeld 
9643d9b1a2aSHans Rosenfeld 	di_dim_fini(dim);
965b602cbcdSManjith Gambhir 	return (diskname);
9663d9b1a2aSHans Rosenfeld }
9673d9b1a2aSHans Rosenfeld 
968533affcbSRobert Mustacchi static void
usage_list(const char * c_name)969533affcbSRobert Mustacchi usage_list(const char *c_name)
9703d9b1a2aSHans Rosenfeld {
971e4cc4004SRobert Mustacchi 	(void) fprintf(stderr, "%s "
972b3266bebSHans Rosenfeld 	    "[-c] [-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n"
9733d9b1a2aSHans Rosenfeld 	    "  List NVMe controllers and their namespaces. If no "
9743d9b1a2aSHans Rosenfeld 	    "controllers and/or name-\n  spaces are specified, all "
9753d9b1a2aSHans Rosenfeld 	    "controllers and namespaces in the system will be\n  "
9763d9b1a2aSHans Rosenfeld 	    "listed.\n", c_name);
9773d9b1a2aSHans Rosenfeld }
9783d9b1a2aSHans Rosenfeld 
979e4cc4004SRobert Mustacchi static void
optparse_list(nvme_process_arg_t * npa)980e4cc4004SRobert Mustacchi optparse_list(nvme_process_arg_t *npa)
981e4cc4004SRobert Mustacchi {
982e4cc4004SRobert Mustacchi 	int c;
983e4cc4004SRobert Mustacchi 	uint_t oflags = 0;
984e4cc4004SRobert Mustacchi 	boolean_t parse = B_FALSE;
985e4cc4004SRobert Mustacchi 	const char *fields = NULL;
986533affcbSRobert Mustacchi 	const ofmt_field_t *ofmt = nvmeadm_list_nsid_ofmt;
987e4cc4004SRobert Mustacchi 
988b3266bebSHans Rosenfeld 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":co:p")) != -1) {
989e4cc4004SRobert Mustacchi 		switch (c) {
990b3266bebSHans Rosenfeld 		case 'c':
991b3266bebSHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_LS_CTRL;
992533affcbSRobert Mustacchi 			ofmt = nvmeadm_list_ctrl_ofmt;
993b3266bebSHans Rosenfeld 			break;
994e4cc4004SRobert Mustacchi 		case 'o':
995e4cc4004SRobert Mustacchi 			fields = optarg;
996e4cc4004SRobert Mustacchi 			break;
997153f3212SHans Rosenfeld 
998e4cc4004SRobert Mustacchi 		case 'p':
999e4cc4004SRobert Mustacchi 			parse = B_TRUE;
1000e4cc4004SRobert Mustacchi 			oflags |= OFMT_PARSABLE;
1001e4cc4004SRobert Mustacchi 			break;
1002153f3212SHans Rosenfeld 
1003e4cc4004SRobert Mustacchi 		case '?':
1004153f3212SHans Rosenfeld 			errx(-1, "unknown option: -%c", optopt);
1005153f3212SHans Rosenfeld 
1006e4cc4004SRobert Mustacchi 		case ':':
1007e4cc4004SRobert Mustacchi 			errx(-1, "option -%c requires an argument", optopt);
1008e4cc4004SRobert Mustacchi 		}
1009e4cc4004SRobert Mustacchi 	}
1010e4cc4004SRobert Mustacchi 
1011e4cc4004SRobert Mustacchi 	if (fields != NULL && !parse) {
1012e4cc4004SRobert Mustacchi 		errx(-1, "-o can only be used when in parsable mode (-p)");
1013e4cc4004SRobert Mustacchi 	}
1014e4cc4004SRobert Mustacchi 
1015e4cc4004SRobert Mustacchi 	if (parse && fields == NULL) {
1016e4cc4004SRobert Mustacchi 		errx(-1, "parsable mode (-p) requires one to specify output "
1017e4cc4004SRobert Mustacchi 		    "fields with -o");
1018e4cc4004SRobert Mustacchi 	}
1019e4cc4004SRobert Mustacchi 
1020e4cc4004SRobert Mustacchi 	if (parse) {
1021e4cc4004SRobert Mustacchi 		ofmt_status_t oferr;
1022e4cc4004SRobert Mustacchi 
1023b3266bebSHans Rosenfeld 		oferr = ofmt_open(fields, ofmt, oflags, 0,
1024e4cc4004SRobert Mustacchi 		    &npa->npa_ofmt);
1025e4cc4004SRobert Mustacchi 		ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
1026e4cc4004SRobert Mustacchi 	}
1027e4cc4004SRobert Mustacchi }
1028e4cc4004SRobert Mustacchi 
1029533affcbSRobert Mustacchi static void
do_list_nsid(const nvme_process_arg_t * npa,nvme_ctrl_info_t * ctrl,nvme_ns_info_t * ns)1030533affcbSRobert Mustacchi do_list_nsid(const nvme_process_arg_t *npa, nvme_ctrl_info_t *ctrl,
1031533affcbSRobert Mustacchi     nvme_ns_info_t *ns)
10323d9b1a2aSHans Rosenfeld {
1033533affcbSRobert Mustacchi 	const char *bd_addr, *disk = NULL;
1034533affcbSRobert Mustacchi 	char *disk_path = NULL;
1035533affcbSRobert Mustacchi 	di_node_t ctrl_devi;
10361e167260SHans Rosenfeld 
1037533affcbSRobert Mustacchi 	switch (nvme_ns_info_level(ns)) {
1038533affcbSRobert Mustacchi 	case NVME_NS_DISC_F_ALL:
1039533affcbSRobert Mustacchi 		disk = "unallocated";
1040533affcbSRobert Mustacchi 		break;
1041533affcbSRobert Mustacchi 	case NVME_NS_DISC_F_ALLOCATED:
1042533affcbSRobert Mustacchi 		disk = "inactive";
1043533affcbSRobert Mustacchi 		break;
1044533affcbSRobert Mustacchi 	case NVME_NS_DISC_F_ACTIVE:
1045533affcbSRobert Mustacchi 		disk = "ignored";
1046533affcbSRobert Mustacchi 		break;
1047533affcbSRobert Mustacchi 	case NVME_NS_DISC_F_NOT_IGNORED:
1048533affcbSRobert Mustacchi 		disk = "unattached";
1049533affcbSRobert Mustacchi 		break;
1050533affcbSRobert Mustacchi 	case NVME_NS_DISC_F_BLKDEV:
1051533affcbSRobert Mustacchi 		disk = "unknown";
1052533affcbSRobert Mustacchi 		if (nvme_ns_info_bd_addr(ns, &bd_addr) &&
1053533affcbSRobert Mustacchi 		    nvme_ctrl_devi(npa->npa_ctrl, &ctrl_devi)) {
1054533affcbSRobert Mustacchi 			disk_path = nvme_dskname(ctrl_devi, bd_addr);
1055533affcbSRobert Mustacchi 			disk = disk_path;
1056533affcbSRobert Mustacchi 		}
1057533affcbSRobert Mustacchi 		break;
1058533affcbSRobert Mustacchi 	}
10593d9b1a2aSHans Rosenfeld 
1060baf9a850SHans Rosenfeld 	if (npa->npa_ofmt != NULL) {
1061533affcbSRobert Mustacchi 		nvmeadm_list_ofmt_arg_t oarg = { 0 };
1062533affcbSRobert Mustacchi 
1063533affcbSRobert Mustacchi 		oarg.nloa_name = npa->npa_ctrl_name;
1064533affcbSRobert Mustacchi 		oarg.nloa_ctrl = ctrl;
1065533affcbSRobert Mustacchi 		oarg.nloa_ns = ns;
1066533affcbSRobert Mustacchi 		oarg.nloa_disk = disk_path;
1067baf9a850SHans Rosenfeld 
1068533affcbSRobert Mustacchi 		ofmt_print(npa->npa_ofmt, &oarg);
1069baf9a850SHans Rosenfeld 	} else {
1070533affcbSRobert Mustacchi 		(void) printf("  %s/%u (%s)", npa->npa_ctrl_name,
1071533affcbSRobert Mustacchi 		    nvme_ns_info_nsid(ns), disk);
1072533affcbSRobert Mustacchi 		if (nvme_ns_info_level(ns) >= NVME_NS_DISC_F_ACTIVE) {
1073533affcbSRobert Mustacchi 			(void) printf(": ");
1074533affcbSRobert Mustacchi 			nvme_print_nsid_summary(ns);
1075baf9a850SHans Rosenfeld 		} else {
1076533affcbSRobert Mustacchi 			(void) printf("\n");
1077baf9a850SHans Rosenfeld 		}
1078e4cc4004SRobert Mustacchi 	}
10793d9b1a2aSHans Rosenfeld 
1080533affcbSRobert Mustacchi 	free(disk_path);
10813d9b1a2aSHans Rosenfeld }
10823d9b1a2aSHans Rosenfeld 
10833d9b1a2aSHans Rosenfeld static int
do_list(const nvme_process_arg_t * npa)1084533affcbSRobert Mustacchi do_list(const nvme_process_arg_t *npa)
10853d9b1a2aSHans Rosenfeld {
1086533affcbSRobert Mustacchi 	nvme_ctrl_info_t *info = NULL;
1087533affcbSRobert Mustacchi 	nvme_ns_iter_t *iter = NULL;
1088533affcbSRobert Mustacchi 	nvme_iter_t ret;
1089533affcbSRobert Mustacchi 	const nvme_ns_disc_t *disc;
1090533affcbSRobert Mustacchi 	nvme_ns_disc_level_t level;
1091533affcbSRobert Mustacchi 	int rv = -1;
1092533affcbSRobert Mustacchi 
1093533affcbSRobert Mustacchi 	if (npa->npa_argc > 0) {
1094533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
1095533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1096533affcbSRobert Mustacchi 	}
10973d9b1a2aSHans Rosenfeld 
1098533affcbSRobert Mustacchi 	if (!nvme_ctrl_info_snap(npa->npa_ctrl, &info)) {
1099533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to get controller information for %s",
1100533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
1101533affcbSRobert Mustacchi 		return (-1);
1102533affcbSRobert Mustacchi 	}
11033d9b1a2aSHans Rosenfeld 
1104e4cc4004SRobert Mustacchi 	if (npa->npa_ofmt == NULL) {
1105533affcbSRobert Mustacchi 		(void) printf("%s: ", npa->npa_ctrl_name);
1106533affcbSRobert Mustacchi 		nvme_print_ctrl_summary(info);
1107b3266bebSHans Rosenfeld 	} else if ((npa->npa_cmdflags & NVMEADM_O_LS_CTRL) != 0) {
1108533affcbSRobert Mustacchi 		nvmeadm_list_ofmt_arg_t oarg = { 0 };
1109533affcbSRobert Mustacchi 		oarg.nloa_name = npa->npa_ctrl_name;
1110533affcbSRobert Mustacchi 		oarg.nloa_ctrl = info;
1111533affcbSRobert Mustacchi 
1112533affcbSRobert Mustacchi 		ofmt_print(npa->npa_ofmt, &oarg);
1113e4cc4004SRobert Mustacchi 	}
11143d9b1a2aSHans Rosenfeld 
1115533affcbSRobert Mustacchi 	if ((npa->npa_cmdflags & NVMEADM_O_LS_CTRL) != 0) {
1116533affcbSRobert Mustacchi 		rv = 0;
1117533affcbSRobert Mustacchi 		goto out;
1118533affcbSRobert Mustacchi 	}
1119533affcbSRobert Mustacchi 
1120533affcbSRobert Mustacchi 	/*
1121533affcbSRobert Mustacchi 	 * Check if we were given an explicit namespace as an argument. If so,
1122533affcbSRobert Mustacchi 	 * we always list it and don't need to do discovery.
1123533affcbSRobert Mustacchi 	 */
1124533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
1125533affcbSRobert Mustacchi 		nvme_ns_info_t *ns_info;
1126533affcbSRobert Mustacchi 
1127533affcbSRobert Mustacchi 		if (!nvme_ns_info_snap(npa->npa_ns, &ns_info)) {
1128533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to get namespace "
1129533affcbSRobert Mustacchi 			    "information for %s", npa->npa_name);
1130533affcbSRobert Mustacchi 			goto out;
1131533affcbSRobert Mustacchi 		}
1132b3266bebSHans Rosenfeld 
1133533affcbSRobert Mustacchi 		do_list_nsid(npa, info, ns_info);
1134533affcbSRobert Mustacchi 		nvme_ns_info_free(ns_info);
1135533affcbSRobert Mustacchi 		rv = 0;
1136533affcbSRobert Mustacchi 		goto out;
1137b3266bebSHans Rosenfeld 	}
11383d9b1a2aSHans Rosenfeld 
1139533affcbSRobert Mustacchi 	if (verbose) {
1140533affcbSRobert Mustacchi 		level = NVME_NS_DISC_F_ALL;
1141533affcbSRobert Mustacchi 	} else {
1142533affcbSRobert Mustacchi 		level = NVME_NS_DISC_F_NOT_IGNORED;
1143533affcbSRobert Mustacchi 	}
1144533affcbSRobert Mustacchi 
1145533affcbSRobert Mustacchi 	if (!nvme_ns_discover_init(npa->npa_ctrl, level, &iter)) {
1146533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate namespaces on %s",
1147533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
1148533affcbSRobert Mustacchi 		goto out;
1149533affcbSRobert Mustacchi 	}
1150533affcbSRobert Mustacchi 
1151533affcbSRobert Mustacchi 	while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
1152533affcbSRobert Mustacchi 		nvme_ns_info_t *ns_info;
1153533affcbSRobert Mustacchi 		uint32_t nsid = nvme_ns_disc_nsid(disc);
1154533affcbSRobert Mustacchi 
1155533affcbSRobert Mustacchi 		if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, nsid, &ns_info)) {
1156533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to get namespace "
1157533affcbSRobert Mustacchi 			    "information for %s/%u", npa->npa_ctrl_name, nsid);
1158533affcbSRobert Mustacchi 			exitcode = -1;
1159533affcbSRobert Mustacchi 			continue;
1160533affcbSRobert Mustacchi 		}
1161533affcbSRobert Mustacchi 
1162533affcbSRobert Mustacchi 		do_list_nsid(npa, info, ns_info);
1163533affcbSRobert Mustacchi 		nvme_ns_info_free(ns_info);
1164533affcbSRobert Mustacchi 	}
1165533affcbSRobert Mustacchi 
1166533affcbSRobert Mustacchi 	nvme_ns_discover_fini(iter);
1167533affcbSRobert Mustacchi 	if (ret == NVME_ITER_ERROR) {
1168533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate all namespaces on %s",
1169533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
1170533affcbSRobert Mustacchi 	} else {
1171533affcbSRobert Mustacchi 		rv = 0;
1172533affcbSRobert Mustacchi 	}
1173533affcbSRobert Mustacchi 
1174533affcbSRobert Mustacchi out:
1175533affcbSRobert Mustacchi 	nvme_ctrl_info_free(info);
1176533affcbSRobert Mustacchi 	return (rv);
11773d9b1a2aSHans Rosenfeld }
11783d9b1a2aSHans Rosenfeld 
11793d9b1a2aSHans Rosenfeld static void
optparse_identify_ctrl(nvme_process_arg_t * npa)1180153f3212SHans Rosenfeld optparse_identify_ctrl(nvme_process_arg_t *npa)
11813d9b1a2aSHans Rosenfeld {
1182153f3212SHans Rosenfeld 	int c;
1183153f3212SHans Rosenfeld 
1184153f3212SHans Rosenfeld 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacn")) != -1) {
1185153f3212SHans Rosenfeld 		switch (c) {
1186153f3212SHans Rosenfeld 		case 'C':
1187153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
1188153f3212SHans Rosenfeld 			break;
1189153f3212SHans Rosenfeld 
1190153f3212SHans Rosenfeld 		case 'a':
1191153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
1192153f3212SHans Rosenfeld 			break;
1193153f3212SHans Rosenfeld 
1194153f3212SHans Rosenfeld 		case 'c':
1195153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1196153f3212SHans Rosenfeld 			break;
1197153f3212SHans Rosenfeld 
1198153f3212SHans Rosenfeld 		case 'n':
1199153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
1200153f3212SHans Rosenfeld 			break;
1201153f3212SHans Rosenfeld 
1202153f3212SHans Rosenfeld 		case '?':
1203153f3212SHans Rosenfeld 			errx(-1, "unknown option: -%c", optopt);
1204153f3212SHans Rosenfeld 
1205153f3212SHans Rosenfeld 		case ':':
1206153f3212SHans Rosenfeld 			errx(-1, "option -%c requires an argument", optopt);
1207153f3212SHans Rosenfeld 		}
1208153f3212SHans Rosenfeld 	}
1209153f3212SHans Rosenfeld }
1210153f3212SHans Rosenfeld 
1211153f3212SHans Rosenfeld static void
usage_identify_ctrl(const char * c_name)1212153f3212SHans Rosenfeld usage_identify_ctrl(const char *c_name)
1213153f3212SHans Rosenfeld {
1214153f3212SHans Rosenfeld 	(void) fprintf(stderr, "%s [-C | -c | [-a] -n] <ctl>[,...]\n\n"
12153d9b1a2aSHans Rosenfeld 	    "  Print detailed information about the specified NVMe "
1216153f3212SHans Rosenfeld 	    "controllers.\n", c_name);
12173d9b1a2aSHans Rosenfeld }
12183d9b1a2aSHans Rosenfeld 
12193d9b1a2aSHans Rosenfeld static int
do_identify_ctrl(const nvme_process_arg_t * npa)1220533affcbSRobert Mustacchi do_identify_ctrl(const nvme_process_arg_t *npa)
12213d9b1a2aSHans Rosenfeld {
1222153f3212SHans Rosenfeld 	boolean_t alloc = B_FALSE;
1223153f3212SHans Rosenfeld 
1224533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL)
1225153f3212SHans Rosenfeld 		errx(-1, "identify-controller cannot be used on namespaces");
1226153f3212SHans Rosenfeld 
1227533affcbSRobert Mustacchi 	if (npa->npa_argc > 0) {
1228533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
1229533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1230533affcbSRobert Mustacchi 	}
1231533affcbSRobert Mustacchi 
1232153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
1233153f3212SHans Rosenfeld 	    npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
1234153f3212SHans Rosenfeld 		errx(-1, "-C cannot be combined with other flags");
1235153f3212SHans Rosenfeld 	}
1236153f3212SHans Rosenfeld 
1237153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1238153f3212SHans Rosenfeld 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1239153f3212SHans Rosenfeld 		errx(-1, "-c cannot be combined with other flags");
1240153f3212SHans Rosenfeld 	}
1241153f3212SHans Rosenfeld 
1242153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
1243153f3212SHans Rosenfeld 	    npa->npa_cmdflags !=
1244153f3212SHans Rosenfeld 	    (NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) {
1245153f3212SHans Rosenfeld 		errx(-1, "-a can only be used together with -n");
1246153f3212SHans Rosenfeld 	}
1247153f3212SHans Rosenfeld 
1248153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
1249153f3212SHans Rosenfeld 		alloc = B_TRUE;
1250153f3212SHans Rosenfeld 	}
1251153f3212SHans Rosenfeld 
1252153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) {
1253533affcbSRobert Mustacchi 		const nvme_identify_nsid_t *idns;
1254153f3212SHans Rosenfeld 
1255533affcbSRobert Mustacchi 		if (!nvme_ctrl_info_common_ns(npa->npa_ctrl_info, &idns)) {
1256533affcbSRobert Mustacchi 			nvmeadm_ctrl_info_warn(npa, "failed to get common "
1257533affcbSRobert Mustacchi 			    "namespace information for %s", npa->npa_name);
1258153f3212SHans Rosenfeld 			return (-1);
1259153f3212SHans Rosenfeld 		}
1260153f3212SHans Rosenfeld 
1261153f3212SHans Rosenfeld 		(void) printf("%s: ", npa->npa_name);
1262533affcbSRobert Mustacchi 		nvme_print_identify_nsid(idns, npa->npa_version);
1263153f3212SHans Rosenfeld 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) {
1264533affcbSRobert Mustacchi 		const char *caption;
1265533affcbSRobert Mustacchi 		uint32_t cns;
1266153f3212SHans Rosenfeld 		nvme_identify_nsid_list_t *idnslist;
1267533affcbSRobert Mustacchi 		nvme_id_req_t *req;
1268153f3212SHans Rosenfeld 
1269533affcbSRobert Mustacchi 		if (alloc) {
1270533affcbSRobert Mustacchi 			caption = "Identify Allocated Namespace List";
1271533affcbSRobert Mustacchi 			cns = NVME_IDENTIFY_NSID_ALLOC_LIST;
1272533affcbSRobert Mustacchi 		} else {
1273533affcbSRobert Mustacchi 			caption = "Identify Active Namespace List";
1274533affcbSRobert Mustacchi 			cns = NVME_IDENTIFY_NSID_LIST;
1275153f3212SHans Rosenfeld 		}
1276153f3212SHans Rosenfeld 
1277533affcbSRobert Mustacchi 		if ((idnslist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1278533affcbSRobert Mustacchi 			err(-1, "failed to allocate identify buffer size");
1279533affcbSRobert Mustacchi 		}
1280153f3212SHans Rosenfeld 
1281533affcbSRobert Mustacchi 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM, cns,
1282533affcbSRobert Mustacchi 		    &req)) {
1283533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to initialize %s request",
1284533affcbSRobert Mustacchi 			    caption);
1285533affcbSRobert Mustacchi 		}
1286153f3212SHans Rosenfeld 
1287533affcbSRobert Mustacchi 		/*
1288533affcbSRobert Mustacchi 		 * Always set the NSID for these requests to NSID 0 so that way
1289533affcbSRobert Mustacchi 		 * we can start the list at the beginning. When we encounter
1290533affcbSRobert Mustacchi 		 * devices with more than 1024 NSIDs then we'll need to issue
1291533affcbSRobert Mustacchi 		 * additional requests.
1292533affcbSRobert Mustacchi 		 */
1293533affcbSRobert Mustacchi 		if (!nvme_id_req_set_nsid(req, 0) ||
1294533affcbSRobert Mustacchi 		    !nvme_id_req_set_output(req, idnslist,
1295533affcbSRobert Mustacchi 		    NVME_IDENTIFY_BUFSIZE)) {
1296533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to set required fields for "
1297533affcbSRobert Mustacchi 			    "identify request");
1298533affcbSRobert Mustacchi 		}
1299533affcbSRobert Mustacchi 
1300533affcbSRobert Mustacchi 		if (!nvme_id_req_exec(req)) {
1301533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to execute identify "
1302533affcbSRobert Mustacchi 			    "request");
1303533affcbSRobert Mustacchi 		}
1304533affcbSRobert Mustacchi 		nvme_id_req_fini(req);
1305153f3212SHans Rosenfeld 
1306153f3212SHans Rosenfeld 		(void) printf("%s: ", npa->npa_name);
1307153f3212SHans Rosenfeld 
1308153f3212SHans Rosenfeld 		nvme_print_identify_nsid_list(caption, idnslist);
1309153f3212SHans Rosenfeld 		free(idnslist);
1310153f3212SHans Rosenfeld 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1311153f3212SHans Rosenfeld 		nvme_identify_ctrl_list_t *ctlist;
1312533affcbSRobert Mustacchi 		nvme_id_req_t *req;
1313153f3212SHans Rosenfeld 
1314533affcbSRobert Mustacchi 		if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1315533affcbSRobert Mustacchi 			err(-1, "failed to allocate identify buffer size");
1316153f3212SHans Rosenfeld 		}
1317153f3212SHans Rosenfeld 
1318533affcbSRobert Mustacchi 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1319533affcbSRobert Mustacchi 		    NVME_IDENTIFY_CTRL_LIST, &req)) {
1320533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to initialize identify "
1321533affcbSRobert Mustacchi 			    "request");
1322153f3212SHans Rosenfeld 		}
1323153f3212SHans Rosenfeld 
1324533affcbSRobert Mustacchi 		if (!nvme_id_req_set_ctrlid(req, 0) ||
1325533affcbSRobert Mustacchi 		    !nvme_id_req_set_output(req, ctlist,
1326533affcbSRobert Mustacchi 		    NVME_IDENTIFY_BUFSIZE)) {
1327533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to set required fields for "
1328533affcbSRobert Mustacchi 			    "identify request");
1329533affcbSRobert Mustacchi 		}
1330533affcbSRobert Mustacchi 		if (!nvme_id_req_exec(req)) {
1331533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to execute identify "
1332533affcbSRobert Mustacchi 			    "request");
1333533affcbSRobert Mustacchi 		}
1334533affcbSRobert Mustacchi 		nvme_id_req_fini(req);
1335153f3212SHans Rosenfeld 
1336153f3212SHans Rosenfeld 		(void) printf("%s: ", npa->npa_name);
1337153f3212SHans Rosenfeld 		nvme_print_identify_ctrl_list("Identify Controller List",
1338153f3212SHans Rosenfeld 		    ctlist);
1339153f3212SHans Rosenfeld 		free(ctlist);
1340153f3212SHans Rosenfeld 	} else {
1341533affcbSRobert Mustacchi 		uint32_t mpsmin;
13423d9b1a2aSHans Rosenfeld 
1343533affcbSRobert Mustacchi 		if (!nvme_ctrl_info_pci_mps_min(npa->npa_ctrl_info,
1344533affcbSRobert Mustacchi 		    &mpsmin)) {
1345533affcbSRobert Mustacchi 			nvmeadm_ctrl_info_fatal(npa, "failed to get minimum "
1346533affcbSRobert Mustacchi 			    "memory page size");
1347533affcbSRobert Mustacchi 		}
13483d9b1a2aSHans Rosenfeld 
13493d9b1a2aSHans Rosenfeld 		(void) printf("%s: ", npa->npa_name);
1350533affcbSRobert Mustacchi 		nvme_print_identify_ctrl(npa->npa_idctl, mpsmin,
1351533affcbSRobert Mustacchi 		    npa->npa_version);
1352153f3212SHans Rosenfeld 	}
1353153f3212SHans Rosenfeld 
1354153f3212SHans Rosenfeld 	return (0);
1355153f3212SHans Rosenfeld }
1356153f3212SHans Rosenfeld 
1357153f3212SHans Rosenfeld static void
optparse_identify_ns(nvme_process_arg_t * npa)1358153f3212SHans Rosenfeld optparse_identify_ns(nvme_process_arg_t *npa)
1359153f3212SHans Rosenfeld {
1360153f3212SHans Rosenfeld 	int c;
1361153f3212SHans Rosenfeld 
1362153f3212SHans Rosenfeld 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":cd")) != -1) {
1363153f3212SHans Rosenfeld 		switch (c) {
1364153f3212SHans Rosenfeld 		case 'c':
1365153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1366153f3212SHans Rosenfeld 			break;
1367153f3212SHans Rosenfeld 
1368153f3212SHans Rosenfeld 		case 'd':
1369153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1370153f3212SHans Rosenfeld 			break;
1371153f3212SHans Rosenfeld 
1372153f3212SHans Rosenfeld 		case '?':
1373153f3212SHans Rosenfeld 			errx(-1, "unknown option: -%c", optopt);
1374153f3212SHans Rosenfeld 
1375153f3212SHans Rosenfeld 		case ':':
1376153f3212SHans Rosenfeld 			errx(-1, "option -%c requires an argument", optopt);
1377153f3212SHans Rosenfeld 		}
1378153f3212SHans Rosenfeld 	}
1379153f3212SHans Rosenfeld }
1380153f3212SHans Rosenfeld 
1381153f3212SHans Rosenfeld static void
usage_identify_ns(const char * c_name)1382153f3212SHans Rosenfeld usage_identify_ns(const char *c_name)
1383153f3212SHans Rosenfeld {
1384153f3212SHans Rosenfeld 	(void) fprintf(stderr, "%s [-c | -d ] <ctl>/<ns>[,...]\n\n"
1385153f3212SHans Rosenfeld 	    "  Print detailed information about the specified NVMe "
1386153f3212SHans Rosenfeld 	    "namespaces.\n", c_name);
1387153f3212SHans Rosenfeld }
1388153f3212SHans Rosenfeld 
1389153f3212SHans Rosenfeld static int
do_identify_ns(const nvme_process_arg_t * npa)1390533affcbSRobert Mustacchi do_identify_ns(const nvme_process_arg_t *npa)
1391153f3212SHans Rosenfeld {
1392533affcbSRobert Mustacchi 	uint32_t nsid;
1393533affcbSRobert Mustacchi 
1394533affcbSRobert Mustacchi 	if (npa->npa_ns == NULL)
1395153f3212SHans Rosenfeld 		errx(-1, "identify-namespace cannot be used on controllers");
1396153f3212SHans Rosenfeld 
1397533affcbSRobert Mustacchi 	if (npa->npa_argc > 0) {
1398533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
1399533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1400533affcbSRobert Mustacchi 	}
1401533affcbSRobert Mustacchi 
1402153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1403153f3212SHans Rosenfeld 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1404153f3212SHans Rosenfeld 		errx(-1, "-c cannot be combined with other flags");
1405153f3212SHans Rosenfeld 	}
1406153f3212SHans Rosenfeld 
1407153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1408153f3212SHans Rosenfeld 	    npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1409153f3212SHans Rosenfeld 		errx(-1, "-d cannot be combined with other flags");
1410153f3212SHans Rosenfeld 	}
1411153f3212SHans Rosenfeld 
1412153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
1413153f3212SHans Rosenfeld 		errx(-1, "-a cannot be used on namespaces");
1414153f3212SHans Rosenfeld 	}
1415153f3212SHans Rosenfeld 
1416533affcbSRobert Mustacchi 	nsid = nvme_ns_info_nsid(npa->npa_ns_info);
1417533affcbSRobert Mustacchi 
1418153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1419153f3212SHans Rosenfeld 		nvme_identify_ctrl_list_t *ctlist;
1420533affcbSRobert Mustacchi 		nvme_id_req_t *req;
1421153f3212SHans Rosenfeld 
1422533affcbSRobert Mustacchi 		if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1423533affcbSRobert Mustacchi 			err(-1, "failed to allocate identify buffer size");
1424153f3212SHans Rosenfeld 		}
1425153f3212SHans Rosenfeld 
1426533affcbSRobert Mustacchi 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1427533affcbSRobert Mustacchi 		    NVME_IDENTIFY_NSID_CTRL_LIST, &req)) {
1428533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to initialize identify "
1429533affcbSRobert Mustacchi 			    "request");
1430153f3212SHans Rosenfeld 		}
1431153f3212SHans Rosenfeld 
1432533affcbSRobert Mustacchi 		if (!nvme_id_req_set_nsid(req, nsid) ||
1433533affcbSRobert Mustacchi 		    !nvme_id_req_set_ctrlid(req, 0) ||
1434533affcbSRobert Mustacchi 		    !nvme_id_req_set_output(req, ctlist,
1435533affcbSRobert Mustacchi 		    NVME_IDENTIFY_BUFSIZE)) {
1436533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to set required fields for "
1437533affcbSRobert Mustacchi 			    "identify request");
1438533affcbSRobert Mustacchi 		}
1439533affcbSRobert Mustacchi 
1440533affcbSRobert Mustacchi 		if (!nvme_id_req_exec(req)) {
1441533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to execute identify "
1442533affcbSRobert Mustacchi 			    "request");
1443533affcbSRobert Mustacchi 		}
1444533affcbSRobert Mustacchi 		nvme_id_req_fini(req);
1445153f3212SHans Rosenfeld 
1446533affcbSRobert Mustacchi 		(void) printf("%s: ", npa->npa_name);
1447153f3212SHans Rosenfeld 		nvme_print_identify_ctrl_list(
1448153f3212SHans Rosenfeld 		    "Identify Attached Controller List", ctlist);
1449153f3212SHans Rosenfeld 		free(ctlist);
1450153f3212SHans Rosenfeld 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) {
1451153f3212SHans Rosenfeld 		nvme_identify_nsid_desc_t *nsdesc;
1452533affcbSRobert Mustacchi 		nvme_id_req_t *req;
1453153f3212SHans Rosenfeld 
1454533affcbSRobert Mustacchi 		if ((nsdesc = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1455533affcbSRobert Mustacchi 			err(-1, "failed to allocate identify buffer size");
1456153f3212SHans Rosenfeld 		}
1457153f3212SHans Rosenfeld 
1458533affcbSRobert Mustacchi 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1459533affcbSRobert Mustacchi 		    NVME_IDENTIFY_NSID_DESC, &req)) {
1460533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to initialize identify "
1461533affcbSRobert Mustacchi 			    "request");
1462533affcbSRobert Mustacchi 		}
1463533affcbSRobert Mustacchi 
1464533affcbSRobert Mustacchi 		if (!nvme_id_req_set_nsid(req, nsid) ||
1465533affcbSRobert Mustacchi 		    !nvme_id_req_set_output(req, nsdesc,
1466533affcbSRobert Mustacchi 		    NVME_IDENTIFY_BUFSIZE)) {
1467533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to set required fields for "
1468533affcbSRobert Mustacchi 			    "identify request");
1469533affcbSRobert Mustacchi 		}
1470153f3212SHans Rosenfeld 
1471533affcbSRobert Mustacchi 		if (!nvme_id_req_exec(req)) {
1472533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to execute identify "
1473533affcbSRobert Mustacchi 			    "request");
1474533affcbSRobert Mustacchi 		}
1475533affcbSRobert Mustacchi 		nvme_id_req_fini(req);
1476533affcbSRobert Mustacchi 
1477533affcbSRobert Mustacchi 		(void) printf("%s: ", npa->npa_name);
1478153f3212SHans Rosenfeld 		nvme_print_identify_nsid_desc(nsdesc);
1479153f3212SHans Rosenfeld 		free(nsdesc);
14803d9b1a2aSHans Rosenfeld 	} else {
1481533affcbSRobert Mustacchi 		const nvme_identify_nsid_t *idns;
1482533affcbSRobert Mustacchi 
1483533affcbSRobert Mustacchi 		(void) printf("%s: ", npa->npa_name);
1484533affcbSRobert Mustacchi 		idns = nvme_ns_info_identify(npa->npa_ns_info);
1485533affcbSRobert Mustacchi 		nvme_print_identify_nsid(idns, npa->npa_version);
14863d9b1a2aSHans Rosenfeld 	}
14873d9b1a2aSHans Rosenfeld 
14883d9b1a2aSHans Rosenfeld 	return (0);
14893d9b1a2aSHans Rosenfeld }
14903d9b1a2aSHans Rosenfeld 
1491153f3212SHans Rosenfeld static void
optparse_identify(nvme_process_arg_t * npa)1492153f3212SHans Rosenfeld optparse_identify(nvme_process_arg_t *npa)
1493153f3212SHans Rosenfeld {
1494153f3212SHans Rosenfeld 	int c;
1495153f3212SHans Rosenfeld 
1496153f3212SHans Rosenfeld 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacdn")) != -1) {
1497153f3212SHans Rosenfeld 		switch (c) {
1498153f3212SHans Rosenfeld 		case 'C':
1499153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
1500153f3212SHans Rosenfeld 			break;
1501153f3212SHans Rosenfeld 
1502153f3212SHans Rosenfeld 		case 'a':
1503153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
1504153f3212SHans Rosenfeld 			break;
1505153f3212SHans Rosenfeld 
1506153f3212SHans Rosenfeld 		case 'c':
1507153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1508153f3212SHans Rosenfeld 			break;
1509153f3212SHans Rosenfeld 
1510153f3212SHans Rosenfeld 		case 'd':
1511153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1512153f3212SHans Rosenfeld 			break;
1513153f3212SHans Rosenfeld 
1514153f3212SHans Rosenfeld 		case 'n':
1515153f3212SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
1516153f3212SHans Rosenfeld 			break;
1517153f3212SHans Rosenfeld 
1518153f3212SHans Rosenfeld 		case '?':
1519153f3212SHans Rosenfeld 			errx(-1, "unknown option: -%c", optopt);
1520153f3212SHans Rosenfeld 
1521153f3212SHans Rosenfeld 		case ':':
1522153f3212SHans Rosenfeld 			errx(-1, "option -%c requires an argument", optopt);
1523153f3212SHans Rosenfeld 
1524153f3212SHans Rosenfeld 		}
1525153f3212SHans Rosenfeld 	}
1526153f3212SHans Rosenfeld 
1527153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
1528153f3212SHans Rosenfeld 	    (npa->npa_cmdflags &
1529153f3212SHans Rosenfeld 	    ~(NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) != 0) {
1530153f3212SHans Rosenfeld 		errx(-1, "-a can only be used alone or together with -n");
1531153f3212SHans Rosenfeld 	}
1532153f3212SHans Rosenfeld 
1533153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
1534153f3212SHans Rosenfeld 	    npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
1535153f3212SHans Rosenfeld 		errx(-1, "-C cannot be combined with other flags");
1536153f3212SHans Rosenfeld 
1537153f3212SHans Rosenfeld 	}
1538153f3212SHans Rosenfeld 
1539153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1540153f3212SHans Rosenfeld 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1541153f3212SHans Rosenfeld 		errx(-1, "-c cannot be combined with other flags");
1542153f3212SHans Rosenfeld 	}
1543153f3212SHans Rosenfeld 
1544153f3212SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1545153f3212SHans Rosenfeld 	    npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1546153f3212SHans Rosenfeld 		errx(-1, "-d cannot be combined with other flags");
1547153f3212SHans Rosenfeld 	}
1548153f3212SHans Rosenfeld }
1549153f3212SHans Rosenfeld 
1550153f3212SHans Rosenfeld static void
usage_identify(const char * c_name)1551153f3212SHans Rosenfeld usage_identify(const char *c_name)
1552153f3212SHans Rosenfeld {
1553153f3212SHans Rosenfeld 	(void) fprintf(stderr,
1554153f3212SHans Rosenfeld 	    "%s [ -C | -c | -d | [-a] -n ] <ctl>[/<ns>][,...]\n\n"
1555153f3212SHans Rosenfeld 	    "  Print detailed information about the specified NVMe "
1556153f3212SHans Rosenfeld 	    "controllers and/or name-\n  spaces.\n", c_name);
1557153f3212SHans Rosenfeld }
1558153f3212SHans Rosenfeld 
1559153f3212SHans Rosenfeld static int
do_identify(const nvme_process_arg_t * npa)1560533affcbSRobert Mustacchi do_identify(const nvme_process_arg_t *npa)
1561153f3212SHans Rosenfeld {
1562533affcbSRobert Mustacchi 	if (npa->npa_argc > 0) {
1563533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
1564533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1565533affcbSRobert Mustacchi 	}
1566533affcbSRobert Mustacchi 
1567533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
1568153f3212SHans Rosenfeld 		if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0)
1569153f3212SHans Rosenfeld 			errx(-1, "-C cannot be used on namespaces");
1570153f3212SHans Rosenfeld 
1571153f3212SHans Rosenfeld 		if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0)
1572153f3212SHans Rosenfeld 			errx(-1, "-a cannot be used on namespaces");
1573153f3212SHans Rosenfeld 
1574153f3212SHans Rosenfeld 		if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0)
1575153f3212SHans Rosenfeld 			errx(-1, "-n cannot be used on namespaces");
1576153f3212SHans Rosenfeld 
1577533affcbSRobert Mustacchi 		return (do_identify_ns(npa));
1578153f3212SHans Rosenfeld 	} else {
1579153f3212SHans Rosenfeld 		if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0)
1580153f3212SHans Rosenfeld 			errx(-1, "-d cannot be used on controllers");
1581153f3212SHans Rosenfeld 
1582533affcbSRobert Mustacchi 		return (do_identify_ctrl(npa));
1583153f3212SHans Rosenfeld 	}
1584153f3212SHans Rosenfeld }
1585153f3212SHans Rosenfeld 
1586533affcbSRobert Mustacchi static void
optparse_list_logs(nvme_process_arg_t * npa)1587533affcbSRobert Mustacchi optparse_list_logs(nvme_process_arg_t *npa)
1588533affcbSRobert Mustacchi {
1589533affcbSRobert Mustacchi 	int c;
1590533affcbSRobert Mustacchi 	uint_t oflags = 0;
1591533affcbSRobert Mustacchi 	boolean_t parse = B_FALSE;
1592533affcbSRobert Mustacchi 	const char *fields = NULL;
1593533affcbSRobert Mustacchi 	char *scope = NULL;
1594533affcbSRobert Mustacchi 	ofmt_status_t oferr;
1595533affcbSRobert Mustacchi 	nvmeadm_list_logs_t *nll;
1596533affcbSRobert Mustacchi 
1597533affcbSRobert Mustacchi 	if ((nll = calloc(1, sizeof (nvmeadm_list_logs_t))) == NULL) {
1598533affcbSRobert Mustacchi 		err(-1, "failed to allocate memory to track log information");
1599533affcbSRobert Mustacchi 	}
1600533affcbSRobert Mustacchi 
1601533affcbSRobert Mustacchi 	npa->npa_cmd_arg = nll;
1602533affcbSRobert Mustacchi 
1603533affcbSRobert Mustacchi 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:ps:")) != -1) {
1604533affcbSRobert Mustacchi 		switch (c) {
1605533affcbSRobert Mustacchi 		case 'a':
1606533affcbSRobert Mustacchi 			nll->nll_unimpl = B_TRUE;
1607533affcbSRobert Mustacchi 			break;
1608533affcbSRobert Mustacchi 		case 'H':
1609533affcbSRobert Mustacchi 			oflags |= OFMT_NOHEADER;
1610533affcbSRobert Mustacchi 			break;
1611533affcbSRobert Mustacchi 		case 'o':
1612533affcbSRobert Mustacchi 			fields = optarg;
1613533affcbSRobert Mustacchi 			break;
1614533affcbSRobert Mustacchi 		case 'p':
1615533affcbSRobert Mustacchi 			parse = B_TRUE;
1616533affcbSRobert Mustacchi 			oflags |= OFMT_PARSABLE;
1617533affcbSRobert Mustacchi 			break;
1618533affcbSRobert Mustacchi 		case 's':
1619533affcbSRobert Mustacchi 			scope = optarg;
1620533affcbSRobert Mustacchi 			break;
1621533affcbSRobert Mustacchi 		case '?':
1622533affcbSRobert Mustacchi 			errx(-1, "unknown option: -%c", optopt);
1623533affcbSRobert Mustacchi 		case ':':
1624533affcbSRobert Mustacchi 			errx(-1, "option -%c requires an argument", optopt);
1625533affcbSRobert Mustacchi 		}
1626533affcbSRobert Mustacchi 	}
1627533affcbSRobert Mustacchi 
1628533affcbSRobert Mustacchi 	if (!parse) {
1629533affcbSRobert Mustacchi 		oflags |= OFMT_WRAP;
1630533affcbSRobert Mustacchi 	}
1631533affcbSRobert Mustacchi 
1632533affcbSRobert Mustacchi 	if (parse && fields == NULL) {
1633533affcbSRobert Mustacchi 		errx(-1, "parsable mode (-p) requires fields specified with "
1634533affcbSRobert Mustacchi 		    "-o");
1635533affcbSRobert Mustacchi 	}
1636533affcbSRobert Mustacchi 
1637533affcbSRobert Mustacchi 	if (fields == NULL) {
1638533affcbSRobert Mustacchi 		if (nll->nll_unimpl) {
1639533affcbSRobert Mustacchi 			fields = nvmeadm_list_logs_fields_impl;
1640533affcbSRobert Mustacchi 		} else {
1641533affcbSRobert Mustacchi 			fields = nvmeadm_list_logs_fields;
1642533affcbSRobert Mustacchi 		}
1643533affcbSRobert Mustacchi 	}
1644533affcbSRobert Mustacchi 
1645533affcbSRobert Mustacchi 	if (scope != NULL) {
1646533affcbSRobert Mustacchi 		const char *str;
1647533affcbSRobert Mustacchi 
1648533affcbSRobert Mustacchi 		while ((str = strsep(&scope, ",")) != NULL) {
1649533affcbSRobert Mustacchi 			if (strcasecmp(str, "nvm") == 0) {
1650533affcbSRobert Mustacchi 				nll->nll_scope |= NVME_LOG_SCOPE_NVM;
1651533affcbSRobert Mustacchi 			} else if (strcasecmp(str, "ns") == 0 ||
1652533affcbSRobert Mustacchi 			    strcasecmp(str, "namespace") == 0) {
1653533affcbSRobert Mustacchi 				nll->nll_scope |= NVME_LOG_SCOPE_NS;
1654533affcbSRobert Mustacchi 			} else if (strcasecmp(str, "ctrl") == 0 ||
1655533affcbSRobert Mustacchi 			    strcasecmp(str, "controller") == 0) {
1656533affcbSRobert Mustacchi 				nll->nll_scope |= NVME_LOG_SCOPE_CTRL;
1657533affcbSRobert Mustacchi 			} else {
1658533affcbSRobert Mustacchi 				errx(-1, "unknown scope string: '%s'; valid "
1659533affcbSRobert Mustacchi 				    "values are 'nvm', 'namespace', and "
1660533affcbSRobert Mustacchi 				    "'controller'", str);
1661533affcbSRobert Mustacchi 			}
1662533affcbSRobert Mustacchi 		}
1663533affcbSRobert Mustacchi 	}
1664533affcbSRobert Mustacchi 
1665533affcbSRobert Mustacchi 	oferr = ofmt_open(fields, nvmeadm_list_logs_ofmt, oflags, 0,
1666533affcbSRobert Mustacchi 	    &npa->npa_ofmt);
1667533affcbSRobert Mustacchi 	ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
1668533affcbSRobert Mustacchi 
1669533affcbSRobert Mustacchi 	if (npa->npa_argc - optind > 1) {
1670533affcbSRobert Mustacchi 		nll->nll_nfilts = npa->npa_argc - optind - 1;
1671533affcbSRobert Mustacchi 		nll->nll_filts = npa->npa_argv + optind + 1;
1672533affcbSRobert Mustacchi 		nll->nll_used = calloc(nll->nll_nfilts, sizeof (boolean_t));
1673533affcbSRobert Mustacchi 		if (nll->nll_used == NULL) {
1674533affcbSRobert Mustacchi 			err(-1, "failed to allocate memory for tracking log "
1675533affcbSRobert Mustacchi 			    "page filters");
1676533affcbSRobert Mustacchi 		}
1677533affcbSRobert Mustacchi 	}
1678533affcbSRobert Mustacchi }
1679533affcbSRobert Mustacchi 
1680533affcbSRobert Mustacchi static void
usage_list_logs(const char * c_name)1681533affcbSRobert Mustacchi usage_list_logs(const char *c_name)
1682533affcbSRobert Mustacchi {
1683533affcbSRobert Mustacchi 	(void) fprintf(stderr, "%s [-H] [-o field,[...] [-p]] [-s scope,[...]] "
1684533affcbSRobert Mustacchi 	    "[-a]\n\t  [<ctl>[/<ns>][,...] [logpage...]\n\n"
1685533affcbSRobert Mustacchi 	    "  List log pages supported by controllers or namespaces.\n",
1686533affcbSRobert Mustacchi 	    c_name);
1687533affcbSRobert Mustacchi }
1688533affcbSRobert Mustacchi 
1689533affcbSRobert Mustacchi static boolean_t
do_list_logs_match(const nvme_log_disc_t * disc,nvmeadm_list_logs_t * nll)1690533affcbSRobert Mustacchi do_list_logs_match(const nvme_log_disc_t *disc, nvmeadm_list_logs_t *nll)
1691533affcbSRobert Mustacchi {
1692*046911ebSRobert Mustacchi 	if (!nll->nll_unimpl && !nvme_log_disc_impl(disc)) {
1693*046911ebSRobert Mustacchi 		return (B_FALSE);
1694*046911ebSRobert Mustacchi 	}
1695*046911ebSRobert Mustacchi 
1696533affcbSRobert Mustacchi 	if (nll->nll_nfilts <= 0) {
1697533affcbSRobert Mustacchi 		return (B_TRUE);
1698533affcbSRobert Mustacchi 	}
1699533affcbSRobert Mustacchi 
1700533affcbSRobert Mustacchi 	for (int i = 0; i < nll->nll_nfilts; i++) {
1701533affcbSRobert Mustacchi 		if (strcmp(nvme_log_disc_name(disc), nll->nll_filts[i]) == 0) {
1702533affcbSRobert Mustacchi 			nll->nll_used[i] = B_TRUE;
1703533affcbSRobert Mustacchi 			return (B_TRUE);
1704533affcbSRobert Mustacchi 		}
1705533affcbSRobert Mustacchi 	}
1706533affcbSRobert Mustacchi 
1707533affcbSRobert Mustacchi 	return (B_FALSE);
1708533affcbSRobert Mustacchi }
1709533affcbSRobert Mustacchi 
1710533affcbSRobert Mustacchi static int
do_list_logs(const nvme_process_arg_t * npa)1711533affcbSRobert Mustacchi do_list_logs(const nvme_process_arg_t *npa)
1712533affcbSRobert Mustacchi {
1713533affcbSRobert Mustacchi 	nvme_log_disc_scope_t scope;
1714533affcbSRobert Mustacchi 	nvme_log_iter_t *iter;
1715533affcbSRobert Mustacchi 	nvme_iter_t ret;
1716533affcbSRobert Mustacchi 	const nvme_log_disc_t *disc;
1717533affcbSRobert Mustacchi 	nvmeadm_list_logs_t *nll = npa->npa_cmd_arg;
1718533affcbSRobert Mustacchi 
1719533affcbSRobert Mustacchi 	if (nll->nll_scope != 0) {
1720533affcbSRobert Mustacchi 		scope = nll->nll_scope;
1721533affcbSRobert Mustacchi 	} else if (npa->npa_ns != NULL) {
1722533affcbSRobert Mustacchi 		scope = NVME_LOG_SCOPE_NS;
1723533affcbSRobert Mustacchi 	} else {
1724533affcbSRobert Mustacchi 		scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM;
1725533affcbSRobert Mustacchi 	}
1726533affcbSRobert Mustacchi 
1727533affcbSRobert Mustacchi 	if (!nvme_log_discover_init(npa->npa_ctrl, scope, 0, &iter)) {
1728533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate logs on %s",
1729533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
1730533affcbSRobert Mustacchi 		return (-1);
1731533affcbSRobert Mustacchi 	}
1732533affcbSRobert Mustacchi 
1733533affcbSRobert Mustacchi 	while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) {
1734533affcbSRobert Mustacchi 		if (do_list_logs_match(disc, nll)) {
1735533affcbSRobert Mustacchi 			nvmeadm_list_logs_ofmt_arg_t print;
1736533affcbSRobert Mustacchi 
1737533affcbSRobert Mustacchi 			print.nlloa_name = npa->npa_name;
1738533affcbSRobert Mustacchi 			print.nlloa_disc = disc;
1739533affcbSRobert Mustacchi 			ofmt_print(npa->npa_ofmt, &print);
1740533affcbSRobert Mustacchi 			nll->nll_nprint++;
1741533affcbSRobert Mustacchi 		}
1742533affcbSRobert Mustacchi 	}
1743533affcbSRobert Mustacchi 
1744533affcbSRobert Mustacchi 	nvme_log_discover_fini(iter);
1745533affcbSRobert Mustacchi 	if (ret == NVME_ITER_ERROR) {
1746533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate logs on %s",
1747533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
1748533affcbSRobert Mustacchi 		return (-1);
1749533affcbSRobert Mustacchi 	}
1750533affcbSRobert Mustacchi 
1751533affcbSRobert Mustacchi 	for (int i = 0; i < nll->nll_nfilts; i++) {
1752533affcbSRobert Mustacchi 		if (!nll->nll_used[i]) {
1753533affcbSRobert Mustacchi 			warnx("log page filter '%s' did match any log pages",
1754533affcbSRobert Mustacchi 			    nll->nll_filts[i]);
1755533affcbSRobert Mustacchi 			exitcode = -1;
1756533affcbSRobert Mustacchi 		}
1757533affcbSRobert Mustacchi 	}
1758533affcbSRobert Mustacchi 
1759533affcbSRobert Mustacchi 	if (nll->nll_nprint == 0) {
1760533affcbSRobert Mustacchi 		if (nll->nll_nfilts == 0) {
1761533affcbSRobert Mustacchi 			warnx("no log pages found for %s", npa->npa_name);
1762533affcbSRobert Mustacchi 		}
1763533affcbSRobert Mustacchi 		exitcode = -1;
1764533affcbSRobert Mustacchi 	}
1765533affcbSRobert Mustacchi 
1766533affcbSRobert Mustacchi 	return (exitcode);
1767533affcbSRobert Mustacchi }
1768533affcbSRobert Mustacchi 
17693d9b1a2aSHans Rosenfeld static void
usage_get_logpage(const char * c_name)17703d9b1a2aSHans Rosenfeld usage_get_logpage(const char *c_name)
17713d9b1a2aSHans Rosenfeld {
1772f9fceaa5SRobert Mustacchi 	(void) fprintf(stderr, "%s [-O file] <ctl>[/<ns>][,...] <logpage>\n\n"
17733d9b1a2aSHans Rosenfeld 	    "  Print the specified log page of the specified NVMe "
1774533affcbSRobert Mustacchi 	    "controllers and/or name-\n  spaces. Run nvmeadm list-logpages "
1775533affcbSRobert Mustacchi 	    "for supported log pages. All devices\n support error, health, "
1776533affcbSRobert Mustacchi 	    "and firmware.\n", c_name);
17773d9b1a2aSHans Rosenfeld }
17783d9b1a2aSHans Rosenfeld 
177968df0c4fSGuy Morrogh static void
usage_firmware_list(const char * c_name)178068df0c4fSGuy Morrogh usage_firmware_list(const char *c_name)
178168df0c4fSGuy Morrogh {
178268df0c4fSGuy Morrogh 	(void) fprintf(stderr, "%s <ctl>\n\n"
178368df0c4fSGuy Morrogh 	    "  Print the log page that contains the list of firmware "
178468df0c4fSGuy Morrogh 	    "images installed on the specified NVMe controller.\n", c_name);
178568df0c4fSGuy Morrogh }
178668df0c4fSGuy Morrogh 
1787533affcbSRobert Mustacchi static uint64_t
do_get_logpage_size(const nvme_process_arg_t * npa,nvme_log_disc_t * disc,nvme_log_req_t * req)1788533affcbSRobert Mustacchi do_get_logpage_size(const nvme_process_arg_t *npa, nvme_log_disc_t *disc,
1789533affcbSRobert Mustacchi     nvme_log_req_t *req)
1790533affcbSRobert Mustacchi {
1791533affcbSRobert Mustacchi 	uint64_t len, ret;
1792533affcbSRobert Mustacchi 	void *buf;
1793533affcbSRobert Mustacchi 	nvme_log_size_kind_t kind;
1794533affcbSRobert Mustacchi 
1795533affcbSRobert Mustacchi 	kind = nvme_log_disc_size(disc, &len);
1796533affcbSRobert Mustacchi 	if (kind != NVME_LOG_SIZE_K_VAR) {
1797533affcbSRobert Mustacchi 		return (len);
1798533affcbSRobert Mustacchi 	}
1799533affcbSRobert Mustacchi 
1800533affcbSRobert Mustacchi 	/*
1801533affcbSRobert Mustacchi 	 * We have a log with a variable length size. To determine the actual
1802533affcbSRobert Mustacchi 	 * size we must actually determine the full length of this.
1803533affcbSRobert Mustacchi 	 */
1804533affcbSRobert Mustacchi 	if ((buf = malloc(len)) == NULL) {
1805533affcbSRobert Mustacchi 		errx(-1, "failed to allocate %zu byte buffer to get log "
1806533affcbSRobert Mustacchi 		    "page size", len);
1807533affcbSRobert Mustacchi 	}
1808533affcbSRobert Mustacchi 
1809533affcbSRobert Mustacchi 	if (!nvme_log_req_set_output(req, buf, len)) {
1810533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to set output parameters to "
1811533affcbSRobert Mustacchi 		    "determine log length");
1812533affcbSRobert Mustacchi 	}
1813533affcbSRobert Mustacchi 
1814533affcbSRobert Mustacchi 	if (!nvme_log_req_exec(req)) {
1815533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to execute log request %s to "
1816533affcbSRobert Mustacchi 		    "determine log length", npa->npa_argv[0]);
1817533affcbSRobert Mustacchi 	}
1818533affcbSRobert Mustacchi 
1819533affcbSRobert Mustacchi 	if (!nvme_log_disc_calc_size(disc, &ret, buf, len)) {
1820533affcbSRobert Mustacchi 		errx(-1, "failed to determine full %s log length",
1821533affcbSRobert Mustacchi 		    npa->npa_argv[0]);
1822533affcbSRobert Mustacchi 	}
1823533affcbSRobert Mustacchi 
1824533affcbSRobert Mustacchi 	free(buf);
1825533affcbSRobert Mustacchi 	return (ret);
1826533affcbSRobert Mustacchi }
1827533affcbSRobert Mustacchi 
1828f9fceaa5SRobert Mustacchi static void
do_get_logpage_dump(const void * buf,size_t len,const char * file)1829f9fceaa5SRobert Mustacchi do_get_logpage_dump(const void *buf, size_t len, const char *file)
1830f9fceaa5SRobert Mustacchi {
1831f9fceaa5SRobert Mustacchi 	size_t off = 0;
1832f9fceaa5SRobert Mustacchi 	int fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
1833f9fceaa5SRobert Mustacchi 
1834f9fceaa5SRobert Mustacchi 	if (fd < 0) {
1835f9fceaa5SRobert Mustacchi 		err(-1, "failed to create output file %s", file);
1836f9fceaa5SRobert Mustacchi 	}
1837f9fceaa5SRobert Mustacchi 
1838f9fceaa5SRobert Mustacchi 	while (len > 0) {
1839f9fceaa5SRobert Mustacchi 		ssize_t ret = write(fd, buf + off, len - off);
1840f9fceaa5SRobert Mustacchi 		if (ret < 0) {
1841f9fceaa5SRobert Mustacchi 			err(EXIT_FAILURE, "failed to write log data to file %s "
1842f9fceaa5SRobert Mustacchi 			    "at offset %zu", file, off);
1843f9fceaa5SRobert Mustacchi 		}
1844f9fceaa5SRobert Mustacchi 
1845f9fceaa5SRobert Mustacchi 		off += (size_t)ret;
1846f9fceaa5SRobert Mustacchi 		len -= (size_t)ret;
1847f9fceaa5SRobert Mustacchi 	}
1848f9fceaa5SRobert Mustacchi 
1849f9fceaa5SRobert Mustacchi 	(void) close(fd);
1850f9fceaa5SRobert Mustacchi }
1851f9fceaa5SRobert Mustacchi 
18523d9b1a2aSHans Rosenfeld static int
do_get_logpage_common(const nvme_process_arg_t * npa,const char * page)1853533affcbSRobert Mustacchi do_get_logpage_common(const nvme_process_arg_t *npa, const char *page)
18543d9b1a2aSHans Rosenfeld {
1855533affcbSRobert Mustacchi 	int ret = 0;
1856533affcbSRobert Mustacchi 	nvme_log_disc_t *disc;
1857533affcbSRobert Mustacchi 	nvme_log_req_t *req;
1858533affcbSRobert Mustacchi 	nvme_log_disc_scope_t scope;
1859533affcbSRobert Mustacchi 	void *buf;
1860533affcbSRobert Mustacchi 	size_t toalloc;
1861f9fceaa5SRobert Mustacchi 	nvmeadm_get_logpage_t *log = npa->npa_cmd_arg;
18623d9b1a2aSHans Rosenfeld 
1863533affcbSRobert Mustacchi 	/*
1864533affcbSRobert Mustacchi 	 * If we have enough information to identify a log-page via libnvme (or
1865533affcbSRobert Mustacchi 	 * in the future take enough options to allow us to actually do this
1866533affcbSRobert Mustacchi 	 * manually), then we will fetch it. If we don't know how to print it,
1867533affcbSRobert Mustacchi 	 * then we'll just hex dump it for now.
1868533affcbSRobert Mustacchi 	 */
1869533affcbSRobert Mustacchi 	if (!nvme_log_req_init_by_name(npa->npa_ctrl, page, 0, &disc, &req)) {
1870533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "could not initialize log request for %s",
1871533affcbSRobert Mustacchi 		    page);
1872533affcbSRobert Mustacchi 	}
18733d9b1a2aSHans Rosenfeld 
1874533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
1875533affcbSRobert Mustacchi 		scope = NVME_LOG_SCOPE_NS;
1876533affcbSRobert Mustacchi 	} else {
1877533affcbSRobert Mustacchi 		scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM;
1878533affcbSRobert Mustacchi 	}
18793d9b1a2aSHans Rosenfeld 
1880533affcbSRobert Mustacchi 	if ((scope & nvme_log_disc_scopes(disc)) == 0) {
1881533affcbSRobert Mustacchi 		errx(-1, "log page %s does not support operating on %s", page,
1882533affcbSRobert Mustacchi 		    npa->npa_ns != NULL ? "namespaces" : "controllers");
1883533affcbSRobert Mustacchi 	}
1884533affcbSRobert Mustacchi 
1885533affcbSRobert Mustacchi 	/*
1886533affcbSRobert Mustacchi 	 * In the future we should add options to allow one to specify and set
1887533affcbSRobert Mustacchi 	 * the fields for the lsp, lsi, etc. and set them here.
1888533affcbSRobert Mustacchi 	 */
1889533affcbSRobert Mustacchi 
1890533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
1891533affcbSRobert Mustacchi 		uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
1892533affcbSRobert Mustacchi 
1893533affcbSRobert Mustacchi 		if (!nvme_log_req_set_nsid(req, nsid)) {
1894533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to set log request "
1895533affcbSRobert Mustacchi 			    "namespace ID to 0x%x", nsid);
1896533affcbSRobert Mustacchi 		}
1897533affcbSRobert Mustacchi 	}
1898533affcbSRobert Mustacchi 
1899533affcbSRobert Mustacchi 	/*
1900533affcbSRobert Mustacchi 	 * The output size should be the last thing that we determine as we may
1901533affcbSRobert Mustacchi 	 * need to issue a log request to figure out how much data we should
1902533affcbSRobert Mustacchi 	 * actually be reading.
1903533affcbSRobert Mustacchi 	 */
1904533affcbSRobert Mustacchi 	toalloc = do_get_logpage_size(npa, disc, req);
1905533affcbSRobert Mustacchi 	buf = malloc(toalloc);
1906533affcbSRobert Mustacchi 	if (buf == NULL) {
1907533affcbSRobert Mustacchi 		err(-1, "failed to allocate %zu bytes for log "
1908533affcbSRobert Mustacchi 		    "request %s", toalloc, page);
1909533affcbSRobert Mustacchi 	}
19103d9b1a2aSHans Rosenfeld 
1911533affcbSRobert Mustacchi 	if (!nvme_log_req_set_output(req, buf, toalloc)) {
1912533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to set output parameters");
1913533affcbSRobert Mustacchi 	}
1914533affcbSRobert Mustacchi 
1915533affcbSRobert Mustacchi 	if (!nvme_log_req_exec(req)) {
1916533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to execute log request %s",
1917533affcbSRobert Mustacchi 		    npa->npa_argv[0]);
1918533affcbSRobert Mustacchi 	}
19193d9b1a2aSHans Rosenfeld 
1920f9fceaa5SRobert Mustacchi 	if (log != NULL && log->ngl_output != NULL) {
1921f9fceaa5SRobert Mustacchi 		do_get_logpage_dump(buf, toalloc, log->ngl_output);
1922f9fceaa5SRobert Mustacchi 		goto done;
1923f9fceaa5SRobert Mustacchi 	}
1924f9fceaa5SRobert Mustacchi 
19253d9b1a2aSHans Rosenfeld 	(void) printf("%s: ", npa->npa_name);
1926533affcbSRobert Mustacchi 	if (strcmp(page, "error") == 0) {
1927533affcbSRobert Mustacchi 		size_t nlog = toalloc / sizeof (nvme_error_log_entry_t);
1928533affcbSRobert Mustacchi 		nvme_print_error_log(nlog, buf, npa->npa_version);
1929533affcbSRobert Mustacchi 	} else if (strcmp(page, "health") == 0) {
1930533affcbSRobert Mustacchi 		nvme_print_health_log(buf, npa->npa_idctl, npa->npa_version);
1931533affcbSRobert Mustacchi 	} else if (strcmp(page, "firmware") == 0) {
1932533affcbSRobert Mustacchi 		nvme_print_fwslot_log(buf, npa->npa_idctl);
1933533affcbSRobert Mustacchi 	} else {
1934533affcbSRobert Mustacchi 		(void) printf("%s (%s)\n", nvme_log_disc_desc(disc), page);
1935533affcbSRobert Mustacchi 		nvmeadm_dump_hex(buf, toalloc);
1936533affcbSRobert Mustacchi 	}
1937533affcbSRobert Mustacchi 
1938f9fceaa5SRobert Mustacchi done:
1939533affcbSRobert Mustacchi 	free(buf);
1940533affcbSRobert Mustacchi 	nvme_log_disc_free(disc);
1941533affcbSRobert Mustacchi 	nvme_log_req_fini(req);
19423d9b1a2aSHans Rosenfeld 
1943533affcbSRobert Mustacchi 	return (ret);
1944533affcbSRobert Mustacchi }
19453d9b1a2aSHans Rosenfeld 
1946533affcbSRobert Mustacchi static int
do_get_logpage_fwslot(const nvme_process_arg_t * npa)1947533affcbSRobert Mustacchi do_get_logpage_fwslot(const nvme_process_arg_t *npa)
1948533affcbSRobert Mustacchi {
1949533affcbSRobert Mustacchi 	if (npa->npa_argc >= 1) {
1950533affcbSRobert Mustacchi 		warnx("no additional arguments may be specified to %s",
1951533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name);
1952533affcbSRobert Mustacchi 		usage(npa->npa_cmd);
1953533affcbSRobert Mustacchi 		exit(-1);
1954533affcbSRobert Mustacchi 	}
1955533affcbSRobert Mustacchi 
1956533affcbSRobert Mustacchi 	return (do_get_logpage_common(npa, "firmware"));
1957533affcbSRobert Mustacchi }
1958533affcbSRobert Mustacchi 
1959f9fceaa5SRobert Mustacchi static void
optparse_get_logpage(nvme_process_arg_t * npa)1960f9fceaa5SRobert Mustacchi optparse_get_logpage(nvme_process_arg_t *npa)
1961f9fceaa5SRobert Mustacchi {
1962f9fceaa5SRobert Mustacchi 	int c;
1963f9fceaa5SRobert Mustacchi 	const char *output = NULL;
1964f9fceaa5SRobert Mustacchi 	nvmeadm_get_logpage_t *log;
1965f9fceaa5SRobert Mustacchi 
1966f9fceaa5SRobert Mustacchi 	if ((log = calloc(1, sizeof (nvmeadm_get_logpage_t))) == NULL) {
1967f9fceaa5SRobert Mustacchi 		err(-1, "failed to allocate memory to track log page "
1968f9fceaa5SRobert Mustacchi 		    "information");
1969f9fceaa5SRobert Mustacchi 	}
1970f9fceaa5SRobert Mustacchi 
1971f9fceaa5SRobert Mustacchi 	npa->npa_cmd_arg = log;
1972f9fceaa5SRobert Mustacchi 
1973f9fceaa5SRobert Mustacchi 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":O:")) != -1) {
1974f9fceaa5SRobert Mustacchi 		switch (c) {
1975f9fceaa5SRobert Mustacchi 		case 'O':
1976f9fceaa5SRobert Mustacchi 			output = optarg;
1977f9fceaa5SRobert Mustacchi 			break;
1978f9fceaa5SRobert Mustacchi 		case '?':
1979f9fceaa5SRobert Mustacchi 			errx(-1, "unknown option: -%c", optopt);
1980f9fceaa5SRobert Mustacchi 		case ':':
1981f9fceaa5SRobert Mustacchi 			errx(-1, "option -%c requires an argument", optopt);
1982f9fceaa5SRobert Mustacchi 		}
1983f9fceaa5SRobert Mustacchi 	}
1984f9fceaa5SRobert Mustacchi 
1985f9fceaa5SRobert Mustacchi 	log->ngl_output = output;
1986f9fceaa5SRobert Mustacchi }
1987f9fceaa5SRobert Mustacchi 
1988533affcbSRobert Mustacchi static int
do_get_logpage(const nvme_process_arg_t * npa)1989533affcbSRobert Mustacchi do_get_logpage(const nvme_process_arg_t *npa)
1990533affcbSRobert Mustacchi {
1991533affcbSRobert Mustacchi 
1992533affcbSRobert Mustacchi 	if (npa->npa_argc < 1) {
1993533affcbSRobert Mustacchi 		warnx("missing log page name");
1994533affcbSRobert Mustacchi 		usage(npa->npa_cmd);
1995533affcbSRobert Mustacchi 		exit(-1);
1996533affcbSRobert Mustacchi 	}
1997533affcbSRobert Mustacchi 
1998533affcbSRobert Mustacchi 	if (npa->npa_argc > 1) {
1999533affcbSRobert Mustacchi 		warnx("only a single log page may be specified at a time");
2000533affcbSRobert Mustacchi 		usage(npa->npa_cmd);
2001533affcbSRobert Mustacchi 		exit(-1);
2002533affcbSRobert Mustacchi 	}
2003533affcbSRobert Mustacchi 
2004533affcbSRobert Mustacchi 	return (do_get_logpage_common(npa, npa->npa_argv[0]));
2005533affcbSRobert Mustacchi }
2006533affcbSRobert Mustacchi 
2007533affcbSRobert Mustacchi static void
optparse_list_features(nvme_process_arg_t * npa)2008533affcbSRobert Mustacchi optparse_list_features(nvme_process_arg_t *npa)
2009533affcbSRobert Mustacchi {
2010533affcbSRobert Mustacchi 	int c;
2011533affcbSRobert Mustacchi 	uint_t oflags = 0;
2012533affcbSRobert Mustacchi 	boolean_t parse = B_FALSE;
2013533affcbSRobert Mustacchi 	const char *fields = NULL;
2014533affcbSRobert Mustacchi 	nvmeadm_features_t *feat;
2015533affcbSRobert Mustacchi 	ofmt_status_t oferr;
2016533affcbSRobert Mustacchi 
2017533affcbSRobert Mustacchi 	if ((feat = calloc(1, sizeof (nvmeadm_features_t))) == NULL) {
2018533affcbSRobert Mustacchi 		err(-1, "failed to allocate memory to track feature "
2019533affcbSRobert Mustacchi 		    "information");
2020533affcbSRobert Mustacchi 	}
2021533affcbSRobert Mustacchi 
2022533affcbSRobert Mustacchi 	npa->npa_cmd_arg = feat;
2023533affcbSRobert Mustacchi 
2024533affcbSRobert Mustacchi 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:p")) != -1) {
2025533affcbSRobert Mustacchi 		switch (c) {
2026533affcbSRobert Mustacchi 		case 'a':
2027533affcbSRobert Mustacchi 			feat->nf_unimpl = B_TRUE;
2028533affcbSRobert Mustacchi 			break;
2029533affcbSRobert Mustacchi 		case 'H':
2030533affcbSRobert Mustacchi 			oflags |= OFMT_NOHEADER;
2031533affcbSRobert Mustacchi 			break;
2032533affcbSRobert Mustacchi 		case 'o':
2033533affcbSRobert Mustacchi 			fields = optarg;
2034533affcbSRobert Mustacchi 			break;
2035533affcbSRobert Mustacchi 		case 'p':
2036533affcbSRobert Mustacchi 			parse = B_TRUE;
2037533affcbSRobert Mustacchi 			oflags |= OFMT_PARSABLE;
2038533affcbSRobert Mustacchi 			break;
2039533affcbSRobert Mustacchi 		case '?':
2040533affcbSRobert Mustacchi 			errx(-1, "unknown option: -%c", optopt);
2041533affcbSRobert Mustacchi 		case ':':
2042533affcbSRobert Mustacchi 			errx(-1, "option -%c requires an argument", optopt);
2043533affcbSRobert Mustacchi 		}
2044533affcbSRobert Mustacchi 	}
2045533affcbSRobert Mustacchi 
2046533affcbSRobert Mustacchi 	if (!parse) {
2047533affcbSRobert Mustacchi 		oflags |= OFMT_WRAP;
2048533affcbSRobert Mustacchi 	}
2049533affcbSRobert Mustacchi 
2050533affcbSRobert Mustacchi 	if (parse && fields == NULL) {
2051533affcbSRobert Mustacchi 		errx(-1, "parsable mode (-p) requires fields specified with "
2052533affcbSRobert Mustacchi 		    "-o");
2053533affcbSRobert Mustacchi 	}
2054533affcbSRobert Mustacchi 
2055533affcbSRobert Mustacchi 	if (fields == NULL) {
2056533affcbSRobert Mustacchi 		fields = nvmeadm_list_features_fields;
2057533affcbSRobert Mustacchi 	}
2058533affcbSRobert Mustacchi 
2059533affcbSRobert Mustacchi 	oferr = ofmt_open(fields, nvmeadm_list_features_ofmt, oflags, 0,
2060533affcbSRobert Mustacchi 	    &npa->npa_ofmt);
2061533affcbSRobert Mustacchi 	ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
2062533affcbSRobert Mustacchi 
2063533affcbSRobert Mustacchi 	if (npa->npa_argc - optind > 1) {
2064533affcbSRobert Mustacchi 		feat->nf_nfilts = (uint32_t)(npa->npa_argc - optind - 1);
2065533affcbSRobert Mustacchi 		feat->nf_filts = npa->npa_argv + optind + 1;
2066533affcbSRobert Mustacchi 		feat->nf_used = calloc(feat->nf_nfilts, sizeof (boolean_t));
2067533affcbSRobert Mustacchi 		if (feat->nf_used == NULL) {
2068533affcbSRobert Mustacchi 			err(-1, "failed to allocate memory for tracking "
2069533affcbSRobert Mustacchi 			    "feature filters");
2070533affcbSRobert Mustacchi 		}
2071533affcbSRobert Mustacchi 	}
20723d9b1a2aSHans Rosenfeld }
20733d9b1a2aSHans Rosenfeld 
2074533affcbSRobert Mustacchi static void
usage_list_features(const char * c_name)2075533affcbSRobert Mustacchi usage_list_features(const char *c_name)
2076533affcbSRobert Mustacchi {
2077533affcbSRobert Mustacchi 	(void) fprintf(stderr, "%s [-a] [-H] [-o field,[...] [-p]] "
2078533affcbSRobert Mustacchi 	    "<ctl>[/<ns>][,...]\n\t  [feature...]\n\n"
2079533affcbSRobert Mustacchi 	    "  List features supported by controllers or namespaces.\n",
2080533affcbSRobert Mustacchi 	    c_name);
2081533affcbSRobert Mustacchi }
2082533affcbSRobert Mustacchi 
2083533affcbSRobert Mustacchi static boolean_t
do_features_match(const nvme_feat_disc_t * disc,nvmeadm_features_t * nf)2084533affcbSRobert Mustacchi do_features_match(const nvme_feat_disc_t *disc, nvmeadm_features_t *nf)
2085533affcbSRobert Mustacchi {
2086533affcbSRobert Mustacchi 	if (nf->nf_nfilts == 0) {
2087533affcbSRobert Mustacchi 		return (B_TRUE);
2088533affcbSRobert Mustacchi 	}
2089533affcbSRobert Mustacchi 
2090533affcbSRobert Mustacchi 	for (uint32_t i = 0; i < nf->nf_nfilts; i++) {
2091533affcbSRobert Mustacchi 		const char *match = nf->nf_filts[i];
2092533affcbSRobert Mustacchi 		long long fid;
2093533affcbSRobert Mustacchi 		const char *err;
2094533affcbSRobert Mustacchi 
2095533affcbSRobert Mustacchi 		if (strcmp(nvme_feat_disc_short(disc), match) == 0 ||
2096533affcbSRobert Mustacchi 		    strcasecmp(nvme_feat_disc_spec(disc), match) == 0) {
2097533affcbSRobert Mustacchi 			nf->nf_used[i] = B_TRUE;
2098533affcbSRobert Mustacchi 			return (B_TRUE);
2099533affcbSRobert Mustacchi 		}
2100533affcbSRobert Mustacchi 
2101533affcbSRobert Mustacchi 		fid = strtonumx(match, 0, UINT32_MAX, &err, 0);
2102533affcbSRobert Mustacchi 		if (err == NULL && fid == nvme_feat_disc_fid(disc)) {
2103533affcbSRobert Mustacchi 			nf->nf_used[i] = B_TRUE;
2104533affcbSRobert Mustacchi 			return (B_TRUE);
2105533affcbSRobert Mustacchi 		}
2106533affcbSRobert Mustacchi 	}
2107533affcbSRobert Mustacchi 
2108533affcbSRobert Mustacchi 	return (B_FALSE);
2109533affcbSRobert Mustacchi }
2110533affcbSRobert Mustacchi 
2111533affcbSRobert Mustacchi 
2112533affcbSRobert Mustacchi /*
2113533affcbSRobert Mustacchi  * This is a common entry point for both list-features and get-features, which
2114533affcbSRobert Mustacchi  * iterate over all features and take action for each one.
2115533affcbSRobert Mustacchi  */
2116533affcbSRobert Mustacchi typedef void (*do_features_cb_f)(const nvme_process_arg_t *,
2117533affcbSRobert Mustacchi     const nvme_feat_disc_t *);
21183d9b1a2aSHans Rosenfeld static int
do_features(const nvme_process_arg_t * npa,nvmeadm_features_t * nf,do_features_cb_f func)2119533affcbSRobert Mustacchi do_features(const nvme_process_arg_t *npa, nvmeadm_features_t *nf,
2120533affcbSRobert Mustacchi     do_features_cb_f func)
21213d9b1a2aSHans Rosenfeld {
2122533affcbSRobert Mustacchi 	nvme_feat_scope_t scope;
2123533affcbSRobert Mustacchi 	nvme_feat_iter_t *iter;
2124533affcbSRobert Mustacchi 	nvme_iter_t ret;
2125533affcbSRobert Mustacchi 	const nvme_feat_disc_t *disc;
2126533affcbSRobert Mustacchi 
2127533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
2128533affcbSRobert Mustacchi 		scope = NVME_FEAT_SCOPE_NS;
2129533affcbSRobert Mustacchi 	} else {
2130533affcbSRobert Mustacchi 		scope = NVME_FEAT_SCOPE_CTRL;
2131533affcbSRobert Mustacchi 	}
21323d9b1a2aSHans Rosenfeld 
2133533affcbSRobert Mustacchi 	if (!nvme_feat_discover_init(npa->npa_ctrl, scope, 0, &iter)) {
2134533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate features on %s",
2135533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
2136533affcbSRobert Mustacchi 		return (-1);
21373d9b1a2aSHans Rosenfeld 	}
21383d9b1a2aSHans Rosenfeld 
2139533affcbSRobert Mustacchi 	while ((ret = nvme_feat_discover_step(iter, &disc)) ==
2140533affcbSRobert Mustacchi 	    NVME_ITER_VALID) {
2141533affcbSRobert Mustacchi 		if (do_features_match(disc, nf)) {
2142533affcbSRobert Mustacchi 			if (!nf->nf_unimpl && nvme_feat_disc_impl(disc) ==
2143533affcbSRobert Mustacchi 			    NVME_FEAT_IMPL_UNSUPPORTED) {
2144533affcbSRobert Mustacchi 				continue;
2145533affcbSRobert Mustacchi 			}
2146533affcbSRobert Mustacchi 
2147533affcbSRobert Mustacchi 			func(npa, disc);
2148533affcbSRobert Mustacchi 			nf->nf_nprint++;
2149533affcbSRobert Mustacchi 		}
2150533affcbSRobert Mustacchi 	}
21513d9b1a2aSHans Rosenfeld 
2152533affcbSRobert Mustacchi 	nvme_feat_discover_fini(iter);
2153533affcbSRobert Mustacchi 	if (ret == NVME_ITER_ERROR) {
2154533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate features on %s",
2155533affcbSRobert Mustacchi 		    npa->npa_ctrl_name);
21563d9b1a2aSHans Rosenfeld 		return (-1);
2157533affcbSRobert Mustacchi 	}
21583d9b1a2aSHans Rosenfeld 
2159533affcbSRobert Mustacchi 	for (uint32_t i = 0; i < nf->nf_nfilts; i++) {
2160533affcbSRobert Mustacchi 		if (!nf->nf_used[i]) {
2161533affcbSRobert Mustacchi 			warnx("feature filter '%s' did match any features",
2162533affcbSRobert Mustacchi 			    nf->nf_filts[i]);
2163533affcbSRobert Mustacchi 			exitcode = -1;
2164533affcbSRobert Mustacchi 		}
2165533affcbSRobert Mustacchi 	}
21663d9b1a2aSHans Rosenfeld 
2167533affcbSRobert Mustacchi 	if (nf->nf_nprint == 0) {
2168533affcbSRobert Mustacchi 		if (nf->nf_nfilts == 0) {
2169533affcbSRobert Mustacchi 			warnx("no features found for %s", npa->npa_name);
2170533affcbSRobert Mustacchi 		}
2171533affcbSRobert Mustacchi 		exitcode = -1;
2172533affcbSRobert Mustacchi 	}
21733d9b1a2aSHans Rosenfeld 
2174533affcbSRobert Mustacchi 	return (exitcode);
21753d9b1a2aSHans Rosenfeld }
21763d9b1a2aSHans Rosenfeld 
2177533affcbSRobert Mustacchi static void
do_list_features_cb(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)2178533affcbSRobert Mustacchi do_list_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc)
21793d9b1a2aSHans Rosenfeld {
2180533affcbSRobert Mustacchi 	nvmeadm_list_features_ofmt_arg_t print;
21813d9b1a2aSHans Rosenfeld 
2182533affcbSRobert Mustacchi 	print.nlfoa_name = npa->npa_name;
2183533affcbSRobert Mustacchi 	print.nlfoa_feat = disc;
2184533affcbSRobert Mustacchi 	ofmt_print(npa->npa_ofmt, &print);
2185533affcbSRobert Mustacchi }
21863d9b1a2aSHans Rosenfeld 
2187533affcbSRobert Mustacchi static int
do_list_features(const nvme_process_arg_t * npa)2188533affcbSRobert Mustacchi do_list_features(const nvme_process_arg_t *npa)
2189533affcbSRobert Mustacchi {
2190533affcbSRobert Mustacchi 	nvmeadm_features_t *nf = npa->npa_cmd_arg;
21913d9b1a2aSHans Rosenfeld 
2192533affcbSRobert Mustacchi 	return (do_features(npa, nf, do_list_features_cb));
2193533affcbSRobert Mustacchi }
21943d9b1a2aSHans Rosenfeld 
2195533affcbSRobert Mustacchi static void
usage_get_features(const char * c_name)2196533affcbSRobert Mustacchi usage_get_features(const char *c_name)
2197533affcbSRobert Mustacchi {
2198533affcbSRobert Mustacchi 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
2199533affcbSRobert Mustacchi 	    "  Print the specified features of the specified NVMe controllers "
2200533affcbSRobert Mustacchi 	    "and/or\n  namespaces. Feature support varies on the controller.\n"
2201533affcbSRobert Mustacchi 	    "Run 'nvmeadm list-features <ctl>' to see supported features.\n",
2202533affcbSRobert Mustacchi 	    c_name);
22033d9b1a2aSHans Rosenfeld }
22043d9b1a2aSHans Rosenfeld 
2205533affcbSRobert Mustacchi /*
2206533affcbSRobert Mustacchi  * The nvmeadm(8) get-features output has traditionally swallowed certain errors
2207533affcbSRobert Mustacchi  * for features that it considers unimplemented in tandem with the kernel. With
2208533affcbSRobert Mustacchi  * the introduction of libnvme and ioctl interface changes, the kernel no longer
2209533affcbSRobert Mustacchi  * caches information about features that are unimplemented.
2210533affcbSRobert Mustacchi  *
2211533affcbSRobert Mustacchi  * There are two cases that we currently swallow errors on and the following
2212533affcbSRobert Mustacchi  * must all be true:
2213533affcbSRobert Mustacchi  *
2214533affcbSRobert Mustacchi  * 1) We have a controller error.
2215533affcbSRobert Mustacchi  * 2) The system doesn't know whether the feature is implemented or not.
2216533affcbSRobert Mustacchi  * 3) The controller error indicates that we have an invalid field.
2217533affcbSRobert Mustacchi  *
2218533affcbSRobert Mustacchi  * There is one additional wrinkle that we are currently papering over due to
2219533affcbSRobert Mustacchi  * the history of nvmeadm swallowing errors. The error recovery feature was made
2220533affcbSRobert Mustacchi  * explicitly namespace-specific in NVMe 1.4. However, various NVMe 1.3 devices
2221533affcbSRobert Mustacchi  * will error if we ask for it without specifying a namespace. Conversely, older
2222533affcbSRobert Mustacchi  * devices will be upset if you do ask for a namespace. This case can be removed
2223533affcbSRobert Mustacchi  * once we better survey devices and come up with a heuristic for how to handle
2224533affcbSRobert Mustacchi  * this across older generations.
2225533affcbSRobert Mustacchi  *
2226533affcbSRobert Mustacchi  * If we add a single feature endpoint that gives flexibility over how the
2227533affcbSRobert Mustacchi  * feature are listed, then we should not swallow errors.
2228533affcbSRobert Mustacchi  */
2229533affcbSRobert Mustacchi static boolean_t
swallow_get_feat_err(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)2230533affcbSRobert Mustacchi swallow_get_feat_err(const nvme_process_arg_t *npa,
2231533affcbSRobert Mustacchi     const nvme_feat_disc_t *disc)
22323d9b1a2aSHans Rosenfeld {
2233533affcbSRobert Mustacchi 	uint32_t sct, sc;
22343d9b1a2aSHans Rosenfeld 
2235533affcbSRobert Mustacchi 	if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_CONTROLLER) {
2236533affcbSRobert Mustacchi 		return (B_FALSE);
22373d9b1a2aSHans Rosenfeld 	}
22383d9b1a2aSHans Rosenfeld 
2239533affcbSRobert Mustacchi 	nvme_ctrl_deverr(npa->npa_ctrl, &sct, &sc);
2240533affcbSRobert Mustacchi 	if (nvme_feat_disc_impl(disc) == NVME_FEAT_IMPL_UNKNOWN &&
2241533affcbSRobert Mustacchi 	    sct == NVME_CQE_SCT_GENERIC && sc == NVME_CQE_SC_GEN_INV_FLD) {
2242533affcbSRobert Mustacchi 		return (B_TRUE);
2243533affcbSRobert Mustacchi 	}
22443d9b1a2aSHans Rosenfeld 
2245533affcbSRobert Mustacchi 	if (nvme_feat_disc_fid(disc) == NVME_FEAT_ERROR &&
2246533affcbSRobert Mustacchi 	    sct == NVME_CQE_SCT_GENERIC && (sc == NVME_CQE_SC_GEN_INV_FLD ||
2247533affcbSRobert Mustacchi 	    sc == NVME_CQE_SC_GEN_INV_NS)) {
2248533affcbSRobert Mustacchi 		return (B_TRUE);
2249533affcbSRobert Mustacchi 	}
2250baf9a850SHans Rosenfeld 
2251533affcbSRobert Mustacchi 	return (B_FALSE);
22523d9b1a2aSHans Rosenfeld }
22533d9b1a2aSHans Rosenfeld 
2254533affcbSRobert Mustacchi static boolean_t
do_get_feat_common(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,uint32_t cdw11,uint32_t * cdw0,void ** datap,size_t * lenp)2255533affcbSRobert Mustacchi do_get_feat_common(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc,
2256533affcbSRobert Mustacchi     uint32_t cdw11, uint32_t *cdw0, void **datap, size_t *lenp)
22573d9b1a2aSHans Rosenfeld {
2258533affcbSRobert Mustacchi 	nvme_get_feat_req_t *req = NULL;
2259533affcbSRobert Mustacchi 	void *data = NULL;
2260533affcbSRobert Mustacchi 	uint64_t datalen = 0;
2261533affcbSRobert Mustacchi 	nvme_get_feat_fields_t fields = nvme_feat_disc_fields_get(disc);
2262533affcbSRobert Mustacchi 
2263533affcbSRobert Mustacchi 	if (!nvme_get_feat_req_init_by_disc(npa->npa_ctrl, disc, &req)) {
2264533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to initialize get feature request "
2265533affcbSRobert Mustacchi 		    "for feature %s", nvme_feat_disc_short(disc));
2266533affcbSRobert Mustacchi 		exitcode = -1;
2267533affcbSRobert Mustacchi 		goto err;
2268533affcbSRobert Mustacchi 	}
2269533affcbSRobert Mustacchi 
2270533affcbSRobert Mustacchi 	if ((fields & NVME_GET_FEAT_F_CDW11) != 0 &&
2271533affcbSRobert Mustacchi 	    !nvme_get_feat_req_set_cdw11(req, cdw11)) {
2272533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to set cdw11 to 0x%x for feature %s",
2273533affcbSRobert Mustacchi 		    cdw11, nvme_feat_disc_short(disc));
2274533affcbSRobert Mustacchi 		exitcode = -1;
2275533affcbSRobert Mustacchi 		goto err;
2276533affcbSRobert Mustacchi 	}
2277533affcbSRobert Mustacchi 
2278533affcbSRobert Mustacchi 	if ((fields & NVME_GET_FEAT_F_DATA) != 0) {
2279533affcbSRobert Mustacchi 		datalen = nvme_feat_disc_data_size(disc);
2280533affcbSRobert Mustacchi 		VERIFY3U(datalen, !=, 0);
2281533affcbSRobert Mustacchi 		data = malloc(datalen);
2282533affcbSRobert Mustacchi 		if (data == NULL) {
2283533affcbSRobert Mustacchi 			err(-1, "failed to allocate %zu bytes for feature %s "
2284533affcbSRobert Mustacchi 			    "data buffer", datalen, nvme_feat_disc_short(disc));
2285533affcbSRobert Mustacchi 		}
22863d9b1a2aSHans Rosenfeld 
2287533affcbSRobert Mustacchi 		if (!nvme_get_feat_req_set_output(req, data, datalen)) {
2288533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to set output data for "
2289533affcbSRobert Mustacchi 			    "feature %s", nvme_feat_disc_short(disc));
2290533affcbSRobert Mustacchi 			exitcode = -1;
2291533affcbSRobert Mustacchi 			goto err;
2292533affcbSRobert Mustacchi 		}
2293533affcbSRobert Mustacchi 	}
2294533affcbSRobert Mustacchi 
2295533affcbSRobert Mustacchi 	if ((fields & NVME_GET_FEAT_F_NSID) != 0) {
2296533affcbSRobert Mustacchi 		uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
22973d9b1a2aSHans Rosenfeld 
2298533affcbSRobert Mustacchi 		if (!nvme_get_feat_req_set_nsid(req, nsid)) {
2299533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to set nsid to 0x%x for "
2300533affcbSRobert Mustacchi 			    "feature %s", nsid, nvme_feat_disc_spec(disc));
2301533affcbSRobert Mustacchi 			exitcode = -1;
2302533affcbSRobert Mustacchi 			goto err;
2303533affcbSRobert Mustacchi 		}
23043d9b1a2aSHans Rosenfeld 	}
23053d9b1a2aSHans Rosenfeld 
2306533affcbSRobert Mustacchi 	if (!nvme_get_feat_req_exec(req)) {
2307533affcbSRobert Mustacchi 		if (!swallow_get_feat_err(npa, disc)) {
2308533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to get feature %s",
2309533affcbSRobert Mustacchi 			    nvme_feat_disc_spec(disc));
2310533affcbSRobert Mustacchi 			exitcode = -1;
2311533affcbSRobert Mustacchi 		}
23123d9b1a2aSHans Rosenfeld 
2313533affcbSRobert Mustacchi 		goto err;
2314533affcbSRobert Mustacchi 	}
23153d9b1a2aSHans Rosenfeld 
2316533affcbSRobert Mustacchi 	if (!nvme_get_feat_req_get_cdw0(req, cdw0)) {
2317533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to get cdw0 result data for %s",
2318533affcbSRobert Mustacchi 		    nvme_feat_disc_spec(disc));
2319533affcbSRobert Mustacchi 		goto err;
2320533affcbSRobert Mustacchi 	}
23213d9b1a2aSHans Rosenfeld 
2322533affcbSRobert Mustacchi 	*datap = data;
2323533affcbSRobert Mustacchi 	*lenp = datalen;
2324533affcbSRobert Mustacchi 	nvme_get_feat_req_fini(req);
2325533affcbSRobert Mustacchi 	return (B_TRUE);
23263d9b1a2aSHans Rosenfeld 
2327533affcbSRobert Mustacchi err:
2328533affcbSRobert Mustacchi 	free(data);
2329533affcbSRobert Mustacchi 	nvme_get_feat_req_fini(req);
2330533affcbSRobert Mustacchi 	return (B_FALSE);
23313d9b1a2aSHans Rosenfeld }
23323d9b1a2aSHans Rosenfeld 
2333533affcbSRobert Mustacchi static void
do_get_feat_temp_thresh_one(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,const nvmeadm_feature_t * feat,const char * label,uint16_t tmpsel,uint16_t thsel)2334533affcbSRobert Mustacchi do_get_feat_temp_thresh_one(const nvme_process_arg_t *npa,
2335533affcbSRobert Mustacchi     const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat,
2336533affcbSRobert Mustacchi     const char *label, uint16_t tmpsel, uint16_t thsel)
23374a663bacSRobert Mustacchi {
2338533affcbSRobert Mustacchi 	uint32_t cdw0;
23394a663bacSRobert Mustacchi 	void *buf = NULL;
2340533affcbSRobert Mustacchi 	size_t buflen;
23414a663bacSRobert Mustacchi 	nvme_temp_threshold_t tt;
23424a663bacSRobert Mustacchi 
23434a663bacSRobert Mustacchi 	tt.r = 0;
23444a663bacSRobert Mustacchi 	tt.b.tt_tmpsel = tmpsel;
23454a663bacSRobert Mustacchi 	tt.b.tt_thsel = thsel;
23464a663bacSRobert Mustacchi 
2347533affcbSRobert Mustacchi 	/*
2348533affcbSRobert Mustacchi 	 * The printing function treats the buffer argument as the label to
2349533affcbSRobert Mustacchi 	 * print for this threshold.
2350533affcbSRobert Mustacchi 	 */
2351533affcbSRobert Mustacchi 	if (!do_get_feat_common(npa, disc, tt.r, &cdw0, &buf, &buflen)) {
2352533affcbSRobert Mustacchi 		return;
23534a663bacSRobert Mustacchi 	}
23544a663bacSRobert Mustacchi 
2355533affcbSRobert Mustacchi 	feat->f_print(cdw0, (void *)label, 0, npa->npa_idctl,
2356533affcbSRobert Mustacchi 	    npa->npa_version);
23574a663bacSRobert Mustacchi 	free(buf);
23584a663bacSRobert Mustacchi }
23594a663bacSRobert Mustacchi 
23604a663bacSRobert Mustacchi /*
23614a663bacSRobert Mustacchi  * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the
23624a663bacSRobert Mustacchi  * device and changed the main device to have a composite temperature sensor. As
23634a663bacSRobert Mustacchi  * a result, there is a set of thresholds for each sensor. In addition, they
23644a663bacSRobert Mustacchi  * added both an over-temperature and under-temperature threshold. Since most
23654a663bacSRobert Mustacchi  * devices don't actually implement all the sensors, we get the health page and
23664a663bacSRobert Mustacchi  * see which sensors have a non-zero value to determine how to proceed.
23674a663bacSRobert Mustacchi  */
2368533affcbSRobert Mustacchi static boolean_t
do_get_feat_temp_thresh(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,const nvmeadm_feature_t * feat)2369533affcbSRobert Mustacchi do_get_feat_temp_thresh(const nvme_process_arg_t *npa,
2370533affcbSRobert Mustacchi     const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat)
23714a663bacSRobert Mustacchi {
2372533affcbSRobert Mustacchi 	nvme_log_req_t *req = NULL;
2373533affcbSRobert Mustacchi 	nvme_log_disc_t *log_disc = NULL;
2374533affcbSRobert Mustacchi 	size_t toalloc;
2375533affcbSRobert Mustacchi 	void *buf = NULL;
2376533affcbSRobert Mustacchi 	boolean_t ret = B_FALSE;
2377533affcbSRobert Mustacchi 	const nvme_health_log_t *hlog;
2378533affcbSRobert Mustacchi 
2379533affcbSRobert Mustacchi 	nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
2380533affcbSRobert Mustacchi 	do_get_feat_temp_thresh_one(npa, disc, feat,
2381533affcbSRobert Mustacchi 	    "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER);
23824a663bacSRobert Mustacchi 
2383533affcbSRobert Mustacchi 	if (!nvme_version_check(npa, &nvme_vers_1v2)) {
2384533affcbSRobert Mustacchi 		return (B_TRUE);
23854a663bacSRobert Mustacchi 	}
23864a663bacSRobert Mustacchi 
2387533affcbSRobert Mustacchi 	if (!nvme_log_req_init_by_name(npa->npa_ctrl, "health", 0, &log_disc,
2388533affcbSRobert Mustacchi 	    &req)) {
2389533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to initialize health log page "
2390533affcbSRobert Mustacchi 		    "request");
2391533affcbSRobert Mustacchi 		return (B_FALSE);
23924a663bacSRobert Mustacchi 	}
23934a663bacSRobert Mustacchi 
2394533affcbSRobert Mustacchi 	toalloc = do_get_logpage_size(npa, log_disc, req);
2395533affcbSRobert Mustacchi 	buf = malloc(toalloc);
2396533affcbSRobert Mustacchi 	if (buf == NULL) {
2397533affcbSRobert Mustacchi 		err(-1, "failed to allocate %zu bytes for health log page",
2398533affcbSRobert Mustacchi 		    toalloc);
23994a663bacSRobert Mustacchi 	}
24004a663bacSRobert Mustacchi 
2401533affcbSRobert Mustacchi 	if (!nvme_log_req_set_output(req, buf, toalloc)) {
2402533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to set output parameters for health "
2403533affcbSRobert Mustacchi 		    "log page");
2404533affcbSRobert Mustacchi 		goto out;
2405533affcbSRobert Mustacchi 	}
2406533affcbSRobert Mustacchi 
2407533affcbSRobert Mustacchi 	if (!nvme_log_req_exec(req)) {
2408533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to retrieve the health log page");
2409533affcbSRobert Mustacchi 		goto out;
24104a663bacSRobert Mustacchi 	}
24114a663bacSRobert Mustacchi 
2412533affcbSRobert Mustacchi 	/* cast required to prove our intentionality to smatch */
2413533affcbSRobert Mustacchi 	hlog = (const nvme_health_log_t *)buf;
2414533affcbSRobert Mustacchi 
2415533affcbSRobert Mustacchi 	do_get_feat_temp_thresh_one(npa, disc, feat,
2416533affcbSRobert Mustacchi 	    "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER);
24174a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_1 != 0) {
2418533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24194a663bacSRobert Mustacchi 		    "Temp. Sensor 1 Over Temp. Threshold", 1,
2420533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2421533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24224a663bacSRobert Mustacchi 		    "Temp. Sensor 1 Under Temp. Threshold", 1,
2423533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24244a663bacSRobert Mustacchi 	}
24254a663bacSRobert Mustacchi 
24264a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_2 != 0) {
2427533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24284a663bacSRobert Mustacchi 		    "Temp. Sensor 2 Over Temp. Threshold", 2,
2429533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2430533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24314a663bacSRobert Mustacchi 		    "Temp. Sensor 2 Under Temp. Threshold", 2,
2432533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24334a663bacSRobert Mustacchi 	}
24344a663bacSRobert Mustacchi 
24354a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_3 != 0) {
2436533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24374a663bacSRobert Mustacchi 		    "Temp. Sensor 3 Over Temp. Threshold", 3,
2438533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2439533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24404a663bacSRobert Mustacchi 		    "Temp. Sensor 3 Under Temp. Threshold", 3,
2441533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24424a663bacSRobert Mustacchi 	}
24434a663bacSRobert Mustacchi 
24444a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_4 != 0) {
2445533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24464a663bacSRobert Mustacchi 		    "Temp. Sensor 4 Over Temp. Threshold", 4,
2447533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2448533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24494a663bacSRobert Mustacchi 		    "Temp. Sensor 4 Under Temp. Threshold", 4,
2450533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24514a663bacSRobert Mustacchi 	}
24524a663bacSRobert Mustacchi 
24534a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_5 != 0) {
2454533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24554a663bacSRobert Mustacchi 		    "Temp. Sensor 5 Over Temp. Threshold", 5,
2456533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2457533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24584a663bacSRobert Mustacchi 		    "Temp. Sensor 5 Under Temp. Threshold", 5,
2459533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24604a663bacSRobert Mustacchi 	}
24614a663bacSRobert Mustacchi 
24624a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_6 != 0) {
2463533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24644a663bacSRobert Mustacchi 		    "Temp. Sensor 6 Over Temp. Threshold", 6,
2465533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2466533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24674a663bacSRobert Mustacchi 		    "Temp. Sensor 6 Under Temp. Threshold", 6,
2468533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24694a663bacSRobert Mustacchi 	}
24704a663bacSRobert Mustacchi 
24714a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_7 != 0) {
2472533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24734a663bacSRobert Mustacchi 		    "Temp. Sensor 7 Over Temp. Threshold", 7,
2474533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2475533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24764a663bacSRobert Mustacchi 		    "Temp. Sensor 7 Under Temp. Threshold", 7,
2477533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24784a663bacSRobert Mustacchi 	}
24794a663bacSRobert Mustacchi 
24804a663bacSRobert Mustacchi 	if (hlog->hl_temp_sensor_8 != 0) {
2481533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24824a663bacSRobert Mustacchi 		    "Temp. Sensor 8 Over Temp. Threshold", 8,
2483533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_OVER);
2484533affcbSRobert Mustacchi 		do_get_feat_temp_thresh_one(npa, disc, feat,
24854a663bacSRobert Mustacchi 		    "Temp. Sensor 8 Under Temp. Threshold", 8,
2486533affcbSRobert Mustacchi 		    NVME_TEMP_THRESH_UNDER);
24874a663bacSRobert Mustacchi 	}
2488533affcbSRobert Mustacchi 
2489533affcbSRobert Mustacchi 	ret = B_TRUE;
2490533affcbSRobert Mustacchi out:
2491533affcbSRobert Mustacchi 	nvme_log_req_fini(req);
2492533affcbSRobert Mustacchi 	free(buf);
2493533affcbSRobert Mustacchi 	return (ret);
24944a663bacSRobert Mustacchi }
24954a663bacSRobert Mustacchi 
2496533affcbSRobert Mustacchi static boolean_t
do_get_feat_intr_vect(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,const nvmeadm_feature_t * feat)2497533affcbSRobert Mustacchi do_get_feat_intr_vect(const nvme_process_arg_t *npa,
2498533affcbSRobert Mustacchi     const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat)
24993d9b1a2aSHans Rosenfeld {
2500533affcbSRobert Mustacchi 	uint32_t nintrs;
2501533affcbSRobert Mustacchi 	boolean_t ret = B_TRUE;
25023d9b1a2aSHans Rosenfeld 
2503533affcbSRobert Mustacchi 	if (!nvme_ctrl_info_pci_nintrs(npa->npa_ctrl_info, &nintrs)) {
2504533affcbSRobert Mustacchi 		nvmeadm_ctrl_info_warn(npa, "failed to get interrupt count "
2505533affcbSRobert Mustacchi 		    "from controller %s information snapshot", npa->npa_name);
2506533affcbSRobert Mustacchi 		return (B_FALSE);
2507533affcbSRobert Mustacchi 	}
25083d9b1a2aSHans Rosenfeld 
2509533affcbSRobert Mustacchi 	nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
2510533affcbSRobert Mustacchi 	for (uint32_t i = 0; i < nintrs; i++) {
2511533affcbSRobert Mustacchi 		uint32_t cdw0;
2512533affcbSRobert Mustacchi 		void *buf;
2513533affcbSRobert Mustacchi 		size_t buflen;
2514533affcbSRobert Mustacchi 		nvme_intr_vect_t vect;
25153d9b1a2aSHans Rosenfeld 
2516533affcbSRobert Mustacchi 		vect.r = 0;
2517533affcbSRobert Mustacchi 		vect.b.iv_iv = i;
25183d9b1a2aSHans Rosenfeld 
2519533affcbSRobert Mustacchi 		if (!do_get_feat_common(npa, disc, vect.r, &cdw0, &buf,
2520533affcbSRobert Mustacchi 		    &buflen)) {
2521533affcbSRobert Mustacchi 			ret = B_FALSE;
2522533affcbSRobert Mustacchi 			continue;
2523533affcbSRobert Mustacchi 		}
25243d9b1a2aSHans Rosenfeld 
2525533affcbSRobert Mustacchi 		feat->f_print(cdw0, buf, buflen, npa->npa_idctl,
2526533affcbSRobert Mustacchi 		    npa->npa_version);
2527533affcbSRobert Mustacchi 		free(buf);
25283d9b1a2aSHans Rosenfeld 	}
25293d9b1a2aSHans Rosenfeld 
2530533affcbSRobert Mustacchi 	return (ret);
2531533affcbSRobert Mustacchi }
2532533affcbSRobert Mustacchi 
2533533affcbSRobert Mustacchi /*
2534533affcbSRobert Mustacchi  * We've been asked to print the following feature that the controller probably
2535533affcbSRobert Mustacchi  * supports. Find our internal feature information for this to see if we know
2536533affcbSRobert Mustacchi  * how to deal with it.
2537533affcbSRobert Mustacchi  */
2538533affcbSRobert Mustacchi static void
do_get_features_cb(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)2539533affcbSRobert Mustacchi do_get_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc)
2540533affcbSRobert Mustacchi {
2541533affcbSRobert Mustacchi 	const nvmeadm_feature_t *feat = NULL;
2542533affcbSRobert Mustacchi 	uint32_t fid = nvme_feat_disc_fid(disc);
2543533affcbSRobert Mustacchi 	nvme_get_feat_fields_t fields;
2544533affcbSRobert Mustacchi 	void *data = NULL;
2545533affcbSRobert Mustacchi 	size_t datalen = 0;
2546533affcbSRobert Mustacchi 	uint32_t cdw0;
2547533affcbSRobert Mustacchi 
2548533affcbSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(features); i++) {
2549533affcbSRobert Mustacchi 		if (features[i].f_feature == fid) {
2550533affcbSRobert Mustacchi 			feat = &features[i];
2551533affcbSRobert Mustacchi 			break;
2552533affcbSRobert Mustacchi 		}
2553533affcbSRobert Mustacchi 	}
2554533affcbSRobert Mustacchi 
2555533affcbSRobert Mustacchi 	/*
2556533affcbSRobert Mustacchi 	 * Determine if we have enough logic in here to get and print the
2557533affcbSRobert Mustacchi 	 * feature. The vast majority of NVMe features only output a single
2558533affcbSRobert Mustacchi 	 * uint32_t in cdw0 and potentially a data buffer. As long as no input
2559533affcbSRobert Mustacchi 	 * arguments are required, then we can go ahead and get this and print
2560533affcbSRobert Mustacchi 	 * the data. If there is, then we will refuse unless we have a
2561533affcbSRobert Mustacchi 	 * particular function. If we have a specific get function, we expect it
2562533affcbSRobert Mustacchi 	 * to do all the printing.
2563533affcbSRobert Mustacchi 	 */
2564533affcbSRobert Mustacchi 	if (feat != NULL && feat->f_get != NULL) {
2565533affcbSRobert Mustacchi 		if (!feat->f_get(npa, disc, feat)) {
2566533affcbSRobert Mustacchi 			exitcode = -1;
2567533affcbSRobert Mustacchi 		}
2568533affcbSRobert Mustacchi 		return;
2569533affcbSRobert Mustacchi 	}
2570533affcbSRobert Mustacchi 
2571533affcbSRobert Mustacchi 	fields = nvme_feat_disc_fields_get(disc);
2572533affcbSRobert Mustacchi 	if ((fields & NVME_GET_FEAT_F_CDW11) != 0) {
2573533affcbSRobert Mustacchi 		warnx("unable to get feature %s due to missing nvmeadm(8) "
2574533affcbSRobert Mustacchi 		    "implementation logic", nvme_feat_disc_spec(disc));
2575533affcbSRobert Mustacchi 		exitcode = -1;
2576533affcbSRobert Mustacchi 		return;
2577533affcbSRobert Mustacchi 	}
2578533affcbSRobert Mustacchi 
2579533affcbSRobert Mustacchi 	/*
2580533affcbSRobert Mustacchi 	 * We do not set exitcode on failure here so that way we can swallow
2581533affcbSRobert Mustacchi 	 * errors from unimplemented features.
2582533affcbSRobert Mustacchi 	 */
2583533affcbSRobert Mustacchi 	if (!do_get_feat_common(npa, disc, 0, &cdw0, &data, &datalen)) {
2584533affcbSRobert Mustacchi 		return;
2585533affcbSRobert Mustacchi 	}
2586533affcbSRobert Mustacchi 
2587533affcbSRobert Mustacchi 	nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
2588533affcbSRobert Mustacchi 	if (feat != NULL && feat->f_print != NULL) {
2589533affcbSRobert Mustacchi 		feat->f_print(cdw0, data, datalen, npa->npa_idctl,
2590533affcbSRobert Mustacchi 		    npa->npa_version);
2591533affcbSRobert Mustacchi 	} else {
2592533affcbSRobert Mustacchi 		nvme_feat_output_t output = nvme_feat_disc_output_get(disc);
2593533affcbSRobert Mustacchi 		nvme_print_feat_unknown(output, cdw0, data, datalen);
2594533affcbSRobert Mustacchi 	}
2595533affcbSRobert Mustacchi 
2596533affcbSRobert Mustacchi 	free(data);
25973d9b1a2aSHans Rosenfeld }
25983d9b1a2aSHans Rosenfeld 
2599533affcbSRobert Mustacchi /*
2600533affcbSRobert Mustacchi  * This is an entry point which prints every feature that we know about. We
2601533affcbSRobert Mustacchi  * often go to lengths to discover all the variable inputs that can be used for
2602533affcbSRobert Mustacchi  * a given feature that requires an argument in cdw11. Due to the semantics of
2603533affcbSRobert Mustacchi  * filtering being used for features and the need to print each feature, this is
2604533affcbSRobert Mustacchi  * not the place to add general field filtering or a means to request a specific
2605533affcbSRobert Mustacchi  * cdw11 argument or similar. Instead, a new get-feature which requires someone
2606533affcbSRobert Mustacchi  * to specify the short name for a feature and then allows particular fields to
2607533affcbSRobert Mustacchi  * be grabbed and arguments should be created instead.
2608533affcbSRobert Mustacchi  *
2609533affcbSRobert Mustacchi  * This uses the same general feature logic that underpins do_list_features()
2610533affcbSRobert Mustacchi  * and therefore we transform filter arguments into the same style used there.
2611533affcbSRobert Mustacchi  */
26123d9b1a2aSHans Rosenfeld static int
do_get_features(const nvme_process_arg_t * npa)2613533affcbSRobert Mustacchi do_get_features(const nvme_process_arg_t *npa)
26143d9b1a2aSHans Rosenfeld {
2615533affcbSRobert Mustacchi 	char *fstr = NULL;
2616533affcbSRobert Mustacchi 	char **filts = NULL;
2617533affcbSRobert Mustacchi 	boolean_t *used = NULL;
2618533affcbSRobert Mustacchi 	nvmeadm_features_t nf;
2619533affcbSRobert Mustacchi 	int ret;
26203d9b1a2aSHans Rosenfeld 
26213d9b1a2aSHans Rosenfeld 	if (npa->npa_argc > 1)
26223d9b1a2aSHans Rosenfeld 		errx(-1, "unexpected arguments");
26233d9b1a2aSHans Rosenfeld 
2624533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) <
2625533affcbSRobert Mustacchi 	    NVME_NS_DISC_F_ACTIVE) {
2626baf9a850SHans Rosenfeld 		errx(-1, "cannot get feature: namespace is inactive");
2627533affcbSRobert Mustacchi 	}
2628baf9a850SHans Rosenfeld 
26293d9b1a2aSHans Rosenfeld 	/*
2630533affcbSRobert Mustacchi 	 * We always leave nf_unimpl set to false as we don't want to bother
2631533affcbSRobert Mustacchi 	 * trying to print a feature that we know the device doesn't support.
26323d9b1a2aSHans Rosenfeld 	 */
2633533affcbSRobert Mustacchi 	(void) memset(&nf, 0, sizeof (nvmeadm_features_t));
26343d9b1a2aSHans Rosenfeld 
26353d9b1a2aSHans Rosenfeld 	/*
2636533affcbSRobert Mustacchi 	 * If we've been given a series of features to print, treat those as
2637533affcbSRobert Mustacchi 	 * filters on the features as we're walking them to determine which to
2638533affcbSRobert Mustacchi 	 * print or not.
26393d9b1a2aSHans Rosenfeld 	 */
2640533affcbSRobert Mustacchi 	if (npa->npa_argc == 1) {
2641533affcbSRobert Mustacchi 		char *f;
2642533affcbSRobert Mustacchi 		uint32_t i;
26433d9b1a2aSHans Rosenfeld 
2644533affcbSRobert Mustacchi 		nf.nf_nfilts = 1;
2645533affcbSRobert Mustacchi 		fstr = strdup(npa->npa_argv[0]);
26463d9b1a2aSHans Rosenfeld 
2647533affcbSRobert Mustacchi 		if (fstr == NULL) {
2648533affcbSRobert Mustacchi 			err(-1, "failed to allocate memory to duplicate "
2649533affcbSRobert Mustacchi 			    "feature string");
26503d9b1a2aSHans Rosenfeld 		}
26513d9b1a2aSHans Rosenfeld 
2652533affcbSRobert Mustacchi 		for (const char *c = strchr(fstr, ','); c != NULL;
2653533affcbSRobert Mustacchi 		    c = strchr(c + 1, ',')) {
2654533affcbSRobert Mustacchi 			nf.nf_nfilts++;
26553d9b1a2aSHans Rosenfeld 		}
26563d9b1a2aSHans Rosenfeld 
2657533affcbSRobert Mustacchi 		filts = calloc(nf.nf_nfilts, sizeof (char *));
2658533affcbSRobert Mustacchi 		if (filts == NULL) {
2659533affcbSRobert Mustacchi 			err(-1, "failed to allocate memory for filter list");
26603d9b1a2aSHans Rosenfeld 		}
26613d9b1a2aSHans Rosenfeld 
2662533affcbSRobert Mustacchi 		i = 0;
2663533affcbSRobert Mustacchi 		while ((f = strsep(&fstr, ",")) != NULL) {
2664533affcbSRobert Mustacchi 			filts[i] = f;
2665533affcbSRobert Mustacchi 			i++;
26663d9b1a2aSHans Rosenfeld 		}
2667533affcbSRobert Mustacchi 		VERIFY3U(i, ==, nf.nf_nfilts);
2668533affcbSRobert Mustacchi 		nf.nf_filts = filts;
26693d9b1a2aSHans Rosenfeld 
2670533affcbSRobert Mustacchi 		used = calloc(nf.nf_nfilts, sizeof (boolean_t));
2671533affcbSRobert Mustacchi 		if (used == NULL) {
2672533affcbSRobert Mustacchi 			err(-1, "failed to allocate memory for filter use "
2673533affcbSRobert Mustacchi 			    "tracking");
26743d9b1a2aSHans Rosenfeld 		}
2675533affcbSRobert Mustacchi 		nf.nf_used = used;
26763d9b1a2aSHans Rosenfeld 	}
26773d9b1a2aSHans Rosenfeld 
2678533affcbSRobert Mustacchi 	(void) printf("%s: Get Features\n", npa->npa_name);
2679533affcbSRobert Mustacchi 	ret = do_features(npa, &nf, do_get_features_cb);
2680533affcbSRobert Mustacchi 
2681533affcbSRobert Mustacchi 	free(fstr);
2682533affcbSRobert Mustacchi 	free(filts);
2683533affcbSRobert Mustacchi 	free(used);
2684533affcbSRobert Mustacchi 	return (ret);
26853d9b1a2aSHans Rosenfeld }
26863d9b1a2aSHans Rosenfeld 
26873d9b1a2aSHans Rosenfeld static int
do_format_common(const nvme_process_arg_t * npa,uint32_t lbaf,uint32_t ses)2688533affcbSRobert Mustacchi do_format_common(const nvme_process_arg_t *npa, uint32_t lbaf,
2689533affcbSRobert Mustacchi     uint32_t ses)
26903d9b1a2aSHans Rosenfeld {
2691533affcbSRobert Mustacchi 	int ret = 0;
2692533affcbSRobert Mustacchi 	nvme_format_req_t *req;
26933d9b1a2aSHans Rosenfeld 
2694533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) <
2695533affcbSRobert Mustacchi 	    NVME_NS_DISC_F_ACTIVE) {
2696baf9a850SHans Rosenfeld 		errx(-1, "cannot %s: namespace is inactive",
2697baf9a850SHans Rosenfeld 		    npa->npa_cmd->c_name);
2698baf9a850SHans Rosenfeld 	}
2699baf9a850SHans Rosenfeld 
2700533affcbSRobert Mustacchi 	if (!nvme_format_req_init(npa->npa_ctrl, &req)) {
2701533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to initialize format request for "
2702533affcbSRobert Mustacchi 		    "%s", npa->npa_name);
2703533affcbSRobert Mustacchi 	}
2704533affcbSRobert Mustacchi 
2705533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
2706533affcbSRobert Mustacchi 		uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
27073d9b1a2aSHans Rosenfeld 
2708533affcbSRobert Mustacchi 		if (!nvme_format_req_set_nsid(req, nsid)) {
2709533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to set format request "
2710533affcbSRobert Mustacchi 			    "namespace ID to 0x%x", nsid);
2711533affcbSRobert Mustacchi 		}
27123d9b1a2aSHans Rosenfeld 	}
27133d9b1a2aSHans Rosenfeld 
2714533affcbSRobert Mustacchi 	if (!nvme_format_req_set_lbaf(req, lbaf) ||
2715533affcbSRobert Mustacchi 	    !nvme_format_req_set_ses(req, ses)) {
2716533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to set format request fields for %s",
2717533affcbSRobert Mustacchi 		    npa->npa_name);
2718533affcbSRobert Mustacchi 	}
2719533affcbSRobert Mustacchi 
2720533affcbSRobert Mustacchi 	if (do_detach(npa) != 0) {
2721533affcbSRobert Mustacchi 		errx(-1, "cannot %s %s due to namespace detach failure",
2722533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_name);
2723533affcbSRobert Mustacchi 	}
2724533affcbSRobert Mustacchi 
2725533affcbSRobert Mustacchi 	if (!nvme_format_req_exec(req)) {
2726533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to %s %s", npa->npa_cmd->c_name,
2727533affcbSRobert Mustacchi 		    npa->npa_name);
2728533affcbSRobert Mustacchi 		ret = -1;
2729533affcbSRobert Mustacchi 	}
2730533affcbSRobert Mustacchi 
2731533affcbSRobert Mustacchi 	if (do_attach(npa) != 0)
2732533affcbSRobert Mustacchi 		ret = -1;
2733533affcbSRobert Mustacchi 
2734533affcbSRobert Mustacchi 	return (ret);
27353d9b1a2aSHans Rosenfeld }
27363d9b1a2aSHans Rosenfeld 
27373d9b1a2aSHans Rosenfeld static void
usage_format(const char * c_name)27383d9b1a2aSHans Rosenfeld usage_format(const char *c_name)
27393d9b1a2aSHans Rosenfeld {
27403d9b1a2aSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
27413d9b1a2aSHans Rosenfeld 	    "  Format one or all namespaces of the specified NVMe "
27423d9b1a2aSHans Rosenfeld 	    "controller. Supported LBA\n  formats can be queried with "
27433d9b1a2aSHans Rosenfeld 	    "the \"%s identify\" command on the namespace\n  to be "
27443d9b1a2aSHans Rosenfeld 	    "formatted.\n", c_name, getprogname());
27453d9b1a2aSHans Rosenfeld }
27463d9b1a2aSHans Rosenfeld 
2747533affcbSRobert Mustacchi static uint32_t
do_format_determine_lbaf(const nvme_process_arg_t * npa)2748533affcbSRobert Mustacchi do_format_determine_lbaf(const nvme_process_arg_t *npa)
27493d9b1a2aSHans Rosenfeld {
2750533affcbSRobert Mustacchi 	const nvme_nvm_lba_fmt_t *fmt;
2751533affcbSRobert Mustacchi 	nvme_ns_info_t *ns_info = NULL;
2752533affcbSRobert Mustacchi 	uint32_t lbaf;
27533d9b1a2aSHans Rosenfeld 
2754533affcbSRobert Mustacchi 	if (npa->npa_argc > 0) {
2755533affcbSRobert Mustacchi 		unsigned long lba;
2756533affcbSRobert Mustacchi 		uint32_t nlbaf = nvme_ctrl_info_nformats(npa->npa_ctrl_info);
27573d9b1a2aSHans Rosenfeld 
2758533affcbSRobert Mustacchi 		errno = 0;
2759533affcbSRobert Mustacchi 		lba = strtoul(npa->npa_argv[0], NULL, 10);
2760533affcbSRobert Mustacchi 		if (errno != 0 || lba >= nlbaf)
2761533affcbSRobert Mustacchi 			errx(-1, "invalid LBA format %s", npa->npa_argv[0]);
2762533affcbSRobert Mustacchi 
2763533affcbSRobert Mustacchi 		if (!nvme_ctrl_info_format(npa->npa_ctrl_info, (uint32_t)lba,
2764533affcbSRobert Mustacchi 		    &fmt)) {
2765533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to get LBA format %lu "
2766533affcbSRobert Mustacchi 			    "information", lba);
2767533affcbSRobert Mustacchi 		}
2768533affcbSRobert Mustacchi 	} else {
2769533affcbSRobert Mustacchi 		/*
2770533affcbSRobert Mustacchi 		 * If we have a namespace then we use the current namespace's
2771533affcbSRobert Mustacchi 		 * LBA format. If we don't have a namespace, then we promised
2772533affcbSRobert Mustacchi 		 * we'd look at namespace 1 in the manual page.
2773533affcbSRobert Mustacchi 		 */
2774533affcbSRobert Mustacchi 		if (npa->npa_ns_info == NULL) {
2775533affcbSRobert Mustacchi 			if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, 1,
2776533affcbSRobert Mustacchi 			    &ns_info)) {
2777533affcbSRobert Mustacchi 				nvmeadm_fatal(npa, "failed to get namespace 1 "
2778533affcbSRobert Mustacchi 				    "information, please explicitly specify an "
2779533affcbSRobert Mustacchi 				    "LBA format");
2780533affcbSRobert Mustacchi 			}
27813d9b1a2aSHans Rosenfeld 
2782533affcbSRobert Mustacchi 			if (!nvme_ns_info_curformat(ns_info, &fmt)) {
2783533affcbSRobert Mustacchi 				nvmeadm_fatal(npa, "failed to retrieve current "
2784533affcbSRobert Mustacchi 				    "namespace format from namespace 1");
2785533affcbSRobert Mustacchi 			}
2786533affcbSRobert Mustacchi 		} else {
2787533affcbSRobert Mustacchi 			if (!nvme_ns_info_curformat(npa->npa_ns_info, &fmt)) {
2788533affcbSRobert Mustacchi 				nvmeadm_fatal(npa, "failed to get the current "
2789533affcbSRobert Mustacchi 				    "format information from %s",
2790533affcbSRobert Mustacchi 				    npa->npa_name);
2791533affcbSRobert Mustacchi 			}
2792533affcbSRobert Mustacchi 		}
2793533affcbSRobert Mustacchi 	}
27943d9b1a2aSHans Rosenfeld 
2795533affcbSRobert Mustacchi 	if (nvme_nvm_lba_fmt_meta_size(fmt) != 0) {
2796533affcbSRobert Mustacchi 		errx(-1, "LBA formats with metadata are not supported");
2797533affcbSRobert Mustacchi 	}
27983d9b1a2aSHans Rosenfeld 
2799533affcbSRobert Mustacchi 	lbaf = nvme_nvm_lba_fmt_id(fmt);
2800533affcbSRobert Mustacchi 	nvme_ns_info_free(ns_info);
2801533affcbSRobert Mustacchi 	return (lbaf);
2802533affcbSRobert Mustacchi }
28033d9b1a2aSHans Rosenfeld 
2804533affcbSRobert Mustacchi static int
do_format(const nvme_process_arg_t * npa)2805533affcbSRobert Mustacchi do_format(const nvme_process_arg_t *npa)
2806533affcbSRobert Mustacchi {
2807533affcbSRobert Mustacchi 	uint32_t lbaf;
2808533affcbSRobert Mustacchi 
2809533affcbSRobert Mustacchi 	if (npa->npa_argc > 1) {
2810533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
2811533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[1]);
28123d9b1a2aSHans Rosenfeld 	}
28133d9b1a2aSHans Rosenfeld 
2814533affcbSRobert Mustacchi 	lbaf = do_format_determine_lbaf(npa);
2815533affcbSRobert Mustacchi 	return (do_format_common(npa, lbaf, 0));
28163d9b1a2aSHans Rosenfeld }
28173d9b1a2aSHans Rosenfeld 
28183d9b1a2aSHans Rosenfeld static void
usage_secure_erase(const char * c_name)28193d9b1a2aSHans Rosenfeld usage_secure_erase(const char *c_name)
28203d9b1a2aSHans Rosenfeld {
2821a3bac573SHans Rosenfeld 	(void) fprintf(stderr, "%s [-c] <ctl>[/<ns>]\n\n"
28223d9b1a2aSHans Rosenfeld 	    "  Secure-Erase one or all namespaces of the specified "
28233d9b1a2aSHans Rosenfeld 	    "NVMe controller.\n", c_name);
28243d9b1a2aSHans Rosenfeld }
28253d9b1a2aSHans Rosenfeld 
2826a3bac573SHans Rosenfeld static void
optparse_secure_erase(nvme_process_arg_t * npa)2827a3bac573SHans Rosenfeld optparse_secure_erase(nvme_process_arg_t *npa)
2828a3bac573SHans Rosenfeld {
2829a3bac573SHans Rosenfeld 	int c;
2830a3bac573SHans Rosenfeld 
2831a3bac573SHans Rosenfeld 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":c")) != -1) {
2832a3bac573SHans Rosenfeld 		switch (c) {
2833a3bac573SHans Rosenfeld 		case 'c':
2834a3bac573SHans Rosenfeld 			npa->npa_cmdflags |= NVMEADM_O_SE_CRYPTO;
2835a3bac573SHans Rosenfeld 			break;
2836153f3212SHans Rosenfeld 
2837a3bac573SHans Rosenfeld 		case '?':
2838153f3212SHans Rosenfeld 			errx(-1, "unknown option: -%c", optopt);
2839153f3212SHans Rosenfeld 
2840153f3212SHans Rosenfeld 		case ':':
2841153f3212SHans Rosenfeld 			errx(-1, "option -%c requires an argument", optopt);
2842153f3212SHans Rosenfeld 
2843a3bac573SHans Rosenfeld 		}
2844a3bac573SHans Rosenfeld 	}
2845a3bac573SHans Rosenfeld }
2846a3bac573SHans Rosenfeld 
28473d9b1a2aSHans Rosenfeld static int
do_secure_erase(const nvme_process_arg_t * npa)2848533affcbSRobert Mustacchi do_secure_erase(const nvme_process_arg_t *npa)
28493d9b1a2aSHans Rosenfeld {
28503d9b1a2aSHans Rosenfeld 	unsigned long lbaf;
28513d9b1a2aSHans Rosenfeld 	uint8_t ses = NVME_FRMT_SES_USER;
28523d9b1a2aSHans Rosenfeld 
2853533affcbSRobert Mustacchi 	if (npa->npa_argc > 0) {
2854533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
2855533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
2856533affcbSRobert Mustacchi 	}
28573d9b1a2aSHans Rosenfeld 
2858a3bac573SHans Rosenfeld 	if ((npa->npa_cmdflags & NVMEADM_O_SE_CRYPTO) != 0)
2859a3bac573SHans Rosenfeld 		ses = NVME_FRMT_SES_CRYPTO;
28603d9b1a2aSHans Rosenfeld 
2861533affcbSRobert Mustacchi 	lbaf = do_format_determine_lbaf(npa);
2862533affcbSRobert Mustacchi 	return (do_format_common(npa, lbaf, ses));
28633d9b1a2aSHans Rosenfeld }
28643d9b1a2aSHans Rosenfeld 
28653d9b1a2aSHans Rosenfeld static void
usage_attach_detach(const char * c_name)28663d9b1a2aSHans Rosenfeld usage_attach_detach(const char *c_name)
28673d9b1a2aSHans Rosenfeld {
28683d9b1a2aSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
2869bbf21555SRichard Lowe 	    "  %c%s blkdev(4D) %s one or all namespaces of the "
28703d9b1a2aSHans Rosenfeld 	    "specified NVMe controller.\n",
28713d9b1a2aSHans Rosenfeld 	    c_name, toupper(c_name[0]), &c_name[1],
28723d9b1a2aSHans Rosenfeld 	    c_name[0] == 'd' ? "from" : "to");
28733d9b1a2aSHans Rosenfeld }
28743d9b1a2aSHans Rosenfeld 
28753d9b1a2aSHans Rosenfeld static int
do_attach(const nvme_process_arg_t * npa)2876533affcbSRobert Mustacchi do_attach(const nvme_process_arg_t *npa)
28773d9b1a2aSHans Rosenfeld {
2878533affcbSRobert Mustacchi 	int rv;
2879533affcbSRobert Mustacchi 	nvme_ns_iter_t *iter = NULL;
2880533affcbSRobert Mustacchi 	nvme_iter_t ret;
2881533affcbSRobert Mustacchi 	const nvme_ns_disc_t *disc;
2882533affcbSRobert Mustacchi 
2883533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
2884533affcbSRobert Mustacchi 		if (!nvme_ns_bd_attach(npa->npa_ns)) {
2885533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "faild to attach %s", npa->npa_name);
2886533affcbSRobert Mustacchi 			return (-1);
2887533affcbSRobert Mustacchi 		}
2888533affcbSRobert Mustacchi 		return (0);
2889533affcbSRobert Mustacchi 	}
28903d9b1a2aSHans Rosenfeld 
2891533affcbSRobert Mustacchi 	if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_NOT_IGNORED,
2892533affcbSRobert Mustacchi 	    &iter))  {
2893533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to initialize namespace discovery "
2894533affcbSRobert Mustacchi 		    "on %s", npa->npa_name);
2895533affcbSRobert Mustacchi 	}
2896533affcbSRobert Mustacchi 
2897533affcbSRobert Mustacchi 	rv = 0;
2898533affcbSRobert Mustacchi 	while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
2899533affcbSRobert Mustacchi 		nvme_ns_t *ns;
2900533affcbSRobert Mustacchi 		uint32_t nsid;
2901533affcbSRobert Mustacchi 
2902533affcbSRobert Mustacchi 		if (nvme_ns_disc_level(disc) == NVME_NS_DISC_F_BLKDEV)
2903533affcbSRobert Mustacchi 			continue;
29043d9b1a2aSHans Rosenfeld 
2905533affcbSRobert Mustacchi 		nsid = nvme_ns_disc_nsid(disc);
2906533affcbSRobert Mustacchi 		if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) {
2907533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to open namespace %s/%u "
2908533affcbSRobert Mustacchi 			    "handle", npa->npa_name, nsid);
2909533affcbSRobert Mustacchi 			rv = -1;
2910533affcbSRobert Mustacchi 			continue;
2911533affcbSRobert Mustacchi 		}
29123d9b1a2aSHans Rosenfeld 
2913533affcbSRobert Mustacchi 		if (!nvme_ns_bd_attach(ns)) {
2914533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to attach namespace "
2915533affcbSRobert Mustacchi 			    "%s/%u", npa->npa_name, nsid);
2916533affcbSRobert Mustacchi 			rv = -1;
2917533affcbSRobert Mustacchi 		}
2918533affcbSRobert Mustacchi 		nvme_ns_fini(ns);
2919533affcbSRobert Mustacchi 	}
29203d9b1a2aSHans Rosenfeld 
2921533affcbSRobert Mustacchi 	nvme_ns_discover_fini(iter);
2922533affcbSRobert Mustacchi 	if (ret == NVME_ITER_ERROR) {
2923533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate namespaces on %s",
2924533affcbSRobert Mustacchi 		    npa->npa_name);
2925533affcbSRobert Mustacchi 		rv = -1;
292663cdc4a2SHans Rosenfeld 	}
292763cdc4a2SHans Rosenfeld 
2928533affcbSRobert Mustacchi 	return (rv);
2929533affcbSRobert Mustacchi }
2930533affcbSRobert Mustacchi 
2931533affcbSRobert Mustacchi static int
do_detach(const nvme_process_arg_t * npa)2932533affcbSRobert Mustacchi do_detach(const nvme_process_arg_t *npa)
2933533affcbSRobert Mustacchi {
2934533affcbSRobert Mustacchi 	int rv;
2935533affcbSRobert Mustacchi 	nvme_ns_iter_t *iter = NULL;
2936533affcbSRobert Mustacchi 	nvme_iter_t ret;
2937533affcbSRobert Mustacchi 	const nvme_ns_disc_t *disc;
2938533affcbSRobert Mustacchi 
2939533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL) {
2940533affcbSRobert Mustacchi 		if (!nvme_ns_bd_detach(npa->npa_ns)) {
2941533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to detach %s", npa->npa_name);
2942533affcbSRobert Mustacchi 			return (-1);
2943533affcbSRobert Mustacchi 		}
294463cdc4a2SHans Rosenfeld 		return (0);
2945533affcbSRobert Mustacchi 	}
294663cdc4a2SHans Rosenfeld 
2947533affcbSRobert Mustacchi 	if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_BLKDEV,
2948533affcbSRobert Mustacchi 	    &iter))  {
2949533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to initialize namespace discovery "
2950533affcbSRobert Mustacchi 		    "on %s", npa->npa_name);
29513d9b1a2aSHans Rosenfeld 	}
29523d9b1a2aSHans Rosenfeld 
2953533affcbSRobert Mustacchi 	rv = 0;
2954533affcbSRobert Mustacchi 	while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
2955533affcbSRobert Mustacchi 		nvme_ns_t *ns;
2956533affcbSRobert Mustacchi 		uint32_t nsid = nvme_ns_disc_nsid(disc);
2957533affcbSRobert Mustacchi 
2958533affcbSRobert Mustacchi 		if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) {
2959533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to open namespace %s/%u "
2960533affcbSRobert Mustacchi 			    "handle", npa->npa_name, nsid);
2961533affcbSRobert Mustacchi 			rv = -1;
2962533affcbSRobert Mustacchi 			continue;
2963533affcbSRobert Mustacchi 		}
2964533affcbSRobert Mustacchi 
2965533affcbSRobert Mustacchi 		if (!nvme_ns_bd_detach(ns)) {
2966533affcbSRobert Mustacchi 			nvmeadm_warn(npa, "failed to detach namespace "
2967533affcbSRobert Mustacchi 			    "%s/%u", npa->npa_name, nsid);
2968533affcbSRobert Mustacchi 			rv = -1;
2969533affcbSRobert Mustacchi 		}
2970533affcbSRobert Mustacchi 		nvme_ns_fini(ns);
2971533affcbSRobert Mustacchi 	}
2972533affcbSRobert Mustacchi 
2973533affcbSRobert Mustacchi 	nvme_ns_discover_fini(iter);
2974533affcbSRobert Mustacchi 	if (ret == NVME_ITER_ERROR) {
2975533affcbSRobert Mustacchi 		nvmeadm_warn(npa, "failed to iterate namespaces on %s",
2976533affcbSRobert Mustacchi 		    npa->npa_name);
2977533affcbSRobert Mustacchi 		rv = -1;
2978533affcbSRobert Mustacchi 	}
2979533affcbSRobert Mustacchi 
2980533affcbSRobert Mustacchi 	return (rv);
29813d9b1a2aSHans Rosenfeld }
2982cf840871SPaul Winder 
2983cf840871SPaul Winder static void
usage_firmware_load(const char * c_name)2984cf840871SPaul Winder usage_firmware_load(const char *c_name)
2985cf840871SPaul Winder {
2986cf840871SPaul Winder 	(void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n"
2987cf840871SPaul Winder 	    "  Load firmware <image file> to offset <offset>.\n"
2988cf840871SPaul Winder 	    "  The firmware needs to be committed to a slot using "
2989cf840871SPaul Winder 	    "\"nvmeadm commit-firmware\"\n  command.\n", c_name);
2990cf840871SPaul Winder }
2991cf840871SPaul Winder 
2992cf840871SPaul Winder /*
2993cf840871SPaul Winder  * Read exactly len bytes, or until eof.
2994cf840871SPaul Winder  */
2995533affcbSRobert Mustacchi static size_t
read_block(const nvme_process_arg_t * npa,int fd,char * buf,size_t len)2996533affcbSRobert Mustacchi read_block(const nvme_process_arg_t *npa, int fd, char *buf, size_t len)
2997cf840871SPaul Winder {
2998cf840871SPaul Winder 	size_t remain;
2999cf840871SPaul Winder 
3000cf840871SPaul Winder 	remain = len;
3001cf840871SPaul Winder 	while (remain > 0) {
3002533affcbSRobert Mustacchi 		ssize_t bytes = read(fd, buf, remain);
3003cf840871SPaul Winder 		if (bytes == 0)
3004cf840871SPaul Winder 			break;
3005cf840871SPaul Winder 
3006cf840871SPaul Winder 		if (bytes < 0) {
3007cf840871SPaul Winder 			if (errno == EINTR)
3008cf840871SPaul Winder 				continue;
3009cf840871SPaul Winder 
3010533affcbSRobert Mustacchi 			err(-1, "Error reading \"%s\"", npa->npa_argv[0]);
3011cf840871SPaul Winder 		}
3012cf840871SPaul Winder 
3013533affcbSRobert Mustacchi 		buf += (size_t)bytes;
3014533affcbSRobert Mustacchi 		remain -= (size_t)bytes;
3015cf840871SPaul Winder 	}
3016cf840871SPaul Winder 
3017cf840871SPaul Winder 	return (len - remain);
3018cf840871SPaul Winder }
3019cf840871SPaul Winder 
3020cf840871SPaul Winder /*
3021cf840871SPaul Winder  * Convert a string to a valid firmware upload offset (in bytes).
3022cf840871SPaul Winder  */
3023533affcbSRobert Mustacchi static uint64_t
get_fw_offsetb(char * str)3024cf840871SPaul Winder get_fw_offsetb(char *str)
3025cf840871SPaul Winder {
3026cf840871SPaul Winder 	longlong_t offsetb;
3027cf840871SPaul Winder 	char *valend;
3028cf840871SPaul Winder 
3029cf840871SPaul Winder 	errno = 0;
3030cf840871SPaul Winder 	offsetb = strtoll(str, &valend, 0);
3031cf840871SPaul Winder 	if (errno != 0 || *valend != '\0' || offsetb < 0 ||
3032cf840871SPaul Winder 	    offsetb > NVME_FW_OFFSETB_MAX)
3033cf840871SPaul Winder 		errx(-1, "Offset must be numeric and in the range of 0 to %llu",
3034cf840871SPaul Winder 		    NVME_FW_OFFSETB_MAX);
3035cf840871SPaul Winder 
3036cf840871SPaul Winder 	if ((offsetb & NVME_DWORD_MASK) != 0)
3037cf840871SPaul Winder 		errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE);
3038cf840871SPaul Winder 
3039533affcbSRobert Mustacchi 	return ((uint64_t)offsetb);
3040cf840871SPaul Winder }
3041cf840871SPaul Winder 
3042cf840871SPaul Winder #define	FIRMWARE_READ_BLKSIZE	(64 * 1024)		/* 64K */
3043cf840871SPaul Winder 
3044cf840871SPaul Winder static int
do_firmware_load(const nvme_process_arg_t * npa)3045533affcbSRobert Mustacchi do_firmware_load(const nvme_process_arg_t *npa)
3046cf840871SPaul Winder {
3047cf840871SPaul Winder 	int fw_fd;
3048533affcbSRobert Mustacchi 	uint64_t offset = 0;
3049533affcbSRobert Mustacchi 	size_t size, len;
3050cf840871SPaul Winder 	char buf[FIRMWARE_READ_BLKSIZE];
3051cf840871SPaul Winder 
3052cf840871SPaul Winder 	if (npa->npa_argc > 2)
3053533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
3054533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[2]);
3055cf840871SPaul Winder 
3056cf840871SPaul Winder 	if (npa->npa_argc == 0)
3057cf840871SPaul Winder 		errx(-1, "Requires firmware file name, and an "
3058cf840871SPaul Winder 		    "optional offset");
3059cf840871SPaul Winder 
3060533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL)
30618466ab88SHans Rosenfeld 		errx(-1, "Firmware loading not available on a per-namespace "
30628466ab88SHans Rosenfeld 		    "basis");
30638466ab88SHans Rosenfeld 
3064cf840871SPaul Winder 	if (npa->npa_argc == 2)
3065cf840871SPaul Winder 		offset = get_fw_offsetb(npa->npa_argv[1]);
3066cf840871SPaul Winder 
3067cf840871SPaul Winder 	fw_fd = open(npa->npa_argv[0], O_RDONLY);
3068cf840871SPaul Winder 	if (fw_fd < 0)
3069cf840871SPaul Winder 		errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0],
3070cf840871SPaul Winder 		    strerror(errno));
3071cf840871SPaul Winder 
3072cf840871SPaul Winder 	size = 0;
3073cf840871SPaul Winder 	do {
3074533affcbSRobert Mustacchi 		len = read_block(npa, fw_fd, buf, sizeof (buf));
3075cf840871SPaul Winder 
3076cf840871SPaul Winder 		if (len == 0)
3077cf840871SPaul Winder 			break;
3078cf840871SPaul Winder 
3079533affcbSRobert Mustacchi 		if (!nvme_fw_load(npa->npa_ctrl, buf, len, offset)) {
3080533affcbSRobert Mustacchi 			nvmeadm_fatal(npa, "failed to load firmware image "
3081533affcbSRobert Mustacchi 			    "\"%s\" at offset %" PRIu64, npa->npa_argv[0],
3082533affcbSRobert Mustacchi 			    offset);
3083533affcbSRobert Mustacchi 		}
3084cf840871SPaul Winder 
3085cf840871SPaul Winder 		offset += len;
3086cf840871SPaul Winder 		size += len;
3087cf840871SPaul Winder 	} while (len == sizeof (buf));
3088cf840871SPaul Winder 
30898d5300d3SRobert Mustacchi 	(void) close(fw_fd);
3090cf840871SPaul Winder 
3091cf840871SPaul Winder 	if (verbose)
3092cf840871SPaul Winder 		(void) printf("%zu bytes downloaded.\n", size);
3093cf840871SPaul Winder 
3094cf840871SPaul Winder 	return (0);
3095cf840871SPaul Winder }
3096cf840871SPaul Winder 
3097533affcbSRobert Mustacchi /*
3098533affcbSRobert Mustacchi  * Common firmware commit for nvmeadm commit-firmware and activate-firmware.
3099533affcbSRobert Mustacchi  */
3100533affcbSRobert Mustacchi static void
nvmeadm_firmware_commit(const nvme_process_arg_t * npa,uint32_t slot,uint32_t act)3101533affcbSRobert Mustacchi nvmeadm_firmware_commit(const nvme_process_arg_t *npa, uint32_t slot,
3102533affcbSRobert Mustacchi     uint32_t act)
3103533affcbSRobert Mustacchi {
3104533affcbSRobert Mustacchi 	nvme_fw_commit_req_t *req;
3105533affcbSRobert Mustacchi 
3106533affcbSRobert Mustacchi 	if (!nvme_fw_commit_req_init(npa->npa_ctrl, &req)) {
3107533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to initialize firmware commit "
3108533affcbSRobert Mustacchi 		    "request for %s", npa->npa_name);
3109533affcbSRobert Mustacchi 	}
3110533affcbSRobert Mustacchi 
3111533affcbSRobert Mustacchi 	if (!nvme_fw_commit_req_set_slot(req, slot) ||
3112533affcbSRobert Mustacchi 	    !nvme_fw_commit_req_set_action(req, act)) {
3113533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to set firmware commit fields for "
3114533affcbSRobert Mustacchi 		    "%s", npa->npa_name);
3115533affcbSRobert Mustacchi 	}
3116533affcbSRobert Mustacchi 
3117533affcbSRobert Mustacchi 	if (!nvme_fw_commit_req_exec(req)) {
3118533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "failed to %s firmware on %s",
3119533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_name);
3120533affcbSRobert Mustacchi 	}
3121533affcbSRobert Mustacchi 
3122533affcbSRobert Mustacchi 	nvme_fw_commit_req_fini(req);
3123533affcbSRobert Mustacchi }
3124533affcbSRobert Mustacchi 
3125cf840871SPaul Winder /*
3126cf840871SPaul Winder  * Convert str to a valid firmware slot number.
3127cf840871SPaul Winder  */
3128533affcbSRobert Mustacchi static uint32_t
get_slot_number(char * str)3129cf840871SPaul Winder get_slot_number(char *str)
3130cf840871SPaul Winder {
3131cf840871SPaul Winder 	longlong_t slot;
3132cf840871SPaul Winder 	char *valend;
3133cf840871SPaul Winder 
3134cf840871SPaul Winder 	errno = 0;
3135cf840871SPaul Winder 	slot = strtoll(str, &valend, 0);
3136cf840871SPaul Winder 	if (errno != 0 || *valend != '\0' ||
3137cf840871SPaul Winder 	    slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX)
3138533affcbSRobert Mustacchi 		errx(-1, "Slot must be numeric and in the range of %u to %u",
3139cf840871SPaul Winder 		    NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX);
3140cf840871SPaul Winder 
3141533affcbSRobert Mustacchi 	return ((uint32_t)slot);
3142cf840871SPaul Winder }
3143cf840871SPaul Winder 
3144cf840871SPaul Winder static void
usage_firmware_commit(const char * c_name)3145cf840871SPaul Winder usage_firmware_commit(const char *c_name)
3146cf840871SPaul Winder {
3147cf840871SPaul Winder 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
3148cf840871SPaul Winder 	    "  Commit previously downloaded firmware to slot <slot>.\n"
3149cf840871SPaul Winder 	    "  The firmware is only activated after a "
3150cf840871SPaul Winder 	    "\"nvmeadm activate-firmware\" command.\n", c_name);
3151cf840871SPaul Winder }
3152cf840871SPaul Winder 
3153cf840871SPaul Winder static int
do_firmware_commit(const nvme_process_arg_t * npa)3154533affcbSRobert Mustacchi do_firmware_commit(const nvme_process_arg_t *npa)
3155cf840871SPaul Winder {
3156533affcbSRobert Mustacchi 	uint32_t slot;
3157cf840871SPaul Winder 
3158cf840871SPaul Winder 	if (npa->npa_argc > 1)
3159533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
3160533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[1]);
3161cf840871SPaul Winder 
3162cf840871SPaul Winder 	if (npa->npa_argc == 0)
3163cf840871SPaul Winder 		errx(-1, "Firmware slot number is required");
3164cf840871SPaul Winder 
3165533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL)
31668466ab88SHans Rosenfeld 		errx(-1, "Firmware committing not available on a per-namespace "
31678466ab88SHans Rosenfeld 		    "basis");
31688466ab88SHans Rosenfeld 
3169cf840871SPaul Winder 	slot = get_slot_number(npa->npa_argv[0]);
3170cf840871SPaul Winder 
31718466ab88SHans Rosenfeld 	if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly)
31728466ab88SHans Rosenfeld 		errx(-1, "Cannot commit firmware to slot 1: slot is read-only");
31738466ab88SHans Rosenfeld 
3174533affcbSRobert Mustacchi 	nvmeadm_firmware_commit(npa, slot, NVME_FWC_SAVE);
3175cf840871SPaul Winder 
3176cf840871SPaul Winder 	if (verbose)
3177cf840871SPaul Winder 		(void) printf("Firmware committed to slot %u.\n", slot);
3178cf840871SPaul Winder 
3179cf840871SPaul Winder 	return (0);
3180cf840871SPaul Winder }
3181cf840871SPaul Winder 
3182cf840871SPaul Winder static void
usage_firmware_activate(const char * c_name)3183cf840871SPaul Winder usage_firmware_activate(const char *c_name)
3184cf840871SPaul Winder {
3185cf840871SPaul Winder 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
3186cf840871SPaul Winder 	    "  Activate firmware in slot <slot>.\n"
3187cf840871SPaul Winder 	    "  The firmware will be in use after the next system reset.\n",
3188cf840871SPaul Winder 	    c_name);
3189cf840871SPaul Winder }
3190cf840871SPaul Winder 
3191cf840871SPaul Winder static int
do_firmware_activate(const nvme_process_arg_t * npa)3192533affcbSRobert Mustacchi do_firmware_activate(const nvme_process_arg_t *npa)
3193cf840871SPaul Winder {
3194533affcbSRobert Mustacchi 	uint32_t slot;
3195cf840871SPaul Winder 
3196cf840871SPaul Winder 	if (npa->npa_argc > 1)
3197533affcbSRobert Mustacchi 		errx(-1, "%s passed extraneous arguments starting with %s",
3198533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, npa->npa_argv[1]);
3199cf840871SPaul Winder 
3200cf840871SPaul Winder 	if (npa->npa_argc == 0)
3201cf840871SPaul Winder 		errx(-1, "Firmware slot number is required");
3202cf840871SPaul Winder 
3203533affcbSRobert Mustacchi 	if (npa->npa_ns != NULL)
32048466ab88SHans Rosenfeld 		errx(-1, "Firmware activation not available on a per-namespace "
32058466ab88SHans Rosenfeld 		    "basis");
32068466ab88SHans Rosenfeld 
3207cf840871SPaul Winder 	slot = get_slot_number(npa->npa_argv[0]);
3208cf840871SPaul Winder 
3209533affcbSRobert Mustacchi 	nvmeadm_firmware_commit(npa, slot, NVME_FWC_ACTIVATE);
3210cf840871SPaul Winder 
3211cf840871SPaul Winder 	if (verbose)
3212533affcbSRobert Mustacchi 		(void) printf("Slot %u successfully activated.\n", slot);
3213cf840871SPaul Winder 
3214cf840871SPaul Winder 	return (0);
3215cf840871SPaul Winder }
32168d5300d3SRobert Mustacchi 
3217533affcbSRobert Mustacchi nvme_vuc_disc_t *
nvmeadm_vuc_init(const nvme_process_arg_t * npa,const char * name)3218533affcbSRobert Mustacchi nvmeadm_vuc_init(const nvme_process_arg_t *npa, const char *name)
32198d5300d3SRobert Mustacchi {
3220533affcbSRobert Mustacchi 	nvme_vuc_disc_t *vuc;
3221533affcbSRobert Mustacchi 	nvme_vuc_disc_lock_t lock;
3222533affcbSRobert Mustacchi 
3223533affcbSRobert Mustacchi 	if (!nvme_vuc_discover_by_name(npa->npa_ctrl, name, 0, &vuc)) {
3224533affcbSRobert Mustacchi 		nvmeadm_fatal(npa, "%s does not support operation %s: device "
3225533affcbSRobert Mustacchi 		    "does not support vendor unique command %s", npa->npa_name,
3226533affcbSRobert Mustacchi 		    npa->npa_cmd->c_name, name);
3227533affcbSRobert Mustacchi 	}
3228533affcbSRobert Mustacchi 
3229533affcbSRobert Mustacchi 	lock = nvme_vuc_disc_lock(vuc);
3230533affcbSRobert Mustacchi 	switch (lock) {
3231533affcbSRobert Mustacchi 	case NVME_VUC_DISC_LOCK_NONE:
3232533affcbSRobert Mustacchi 		break;
3233533affcbSRobert Mustacchi 	case NVME_VUC_DISC_LOCK_READ:
3234533affcbSRobert Mustacchi 		nvmeadm_excl(npa, NVME_LOCK_L_READ);
3235533affcbSRobert Mustacchi 		break;
3236533affcbSRobert Mustacchi 	case NVME_VUC_DISC_LOCK_WRITE:
3237533affcbSRobert Mustacchi 		nvmeadm_excl(npa, NVME_LOCK_L_WRITE);
3238533affcbSRobert Mustacchi 		break;
3239533affcbSRobert Mustacchi 	}
3240533affcbSRobert Mustacchi 
3241533affcbSRobert Mustacchi 	return (vuc);
3242533affcbSRobert Mustacchi }
3243533affcbSRobert Mustacchi 
3244533affcbSRobert Mustacchi void
nvmeadm_vuc_fini(const nvme_process_arg_t * npa,nvme_vuc_disc_t * vuc)3245533affcbSRobert Mustacchi nvmeadm_vuc_fini(const nvme_process_arg_t *npa, nvme_vuc_disc_t *vuc)
3246533affcbSRobert Mustacchi {
3247533affcbSRobert Mustacchi 	if (nvme_vuc_disc_lock(vuc) != NVME_VUC_DISC_LOCK_NONE) {
3248533affcbSRobert Mustacchi 		if (npa->npa_ns != NULL) {
3249533affcbSRobert Mustacchi 			nvme_ns_unlock(npa->npa_ns);
3250533affcbSRobert Mustacchi 		} else if (npa->npa_ctrl != NULL) {
3251533affcbSRobert Mustacchi 			nvme_ctrl_unlock(npa->npa_ctrl);
3252533affcbSRobert Mustacchi 		}
32538d5300d3SRobert Mustacchi 	}
32548d5300d3SRobert Mustacchi 
3255533affcbSRobert Mustacchi 	nvme_vuc_disc_free(vuc);
32568d5300d3SRobert Mustacchi }
3257