1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2017 Netflix, Inc.
5 * Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer,
12 *    without modification, immediately at the beginning of the file.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/ioccom.h>
34
35#include <err.h>
36#include <fcntl.h>
37#include <stdbool.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sysexits.h>
43#include <unistd.h>
44
45#include "nvmecontrol.h"
46
47/* Tables for command line parsing */
48
49static cmd_fn_t ns;
50static cmd_fn_t nsactive;
51static cmd_fn_t nsallocated;
52static cmd_fn_t nscontrollers;
53static cmd_fn_t nscreate;
54static cmd_fn_t nsdelete;
55static cmd_fn_t nsattach;
56static cmd_fn_t nsdetach;
57static cmd_fn_t nsattached;
58static cmd_fn_t nsidentify;
59
60#define NONE 0xffffffffu
61#define NONE64 0xffffffffffffffffull
62#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
63#define OPT_END	{ NULL, 0, arg_none, NULL, NULL }
64
65static struct cmd ns_cmd = {
66	.name = "ns",
67	.fn = ns,
68	.descr = "Namespace management commands",
69	.ctx_size = 0,
70	.opts = NULL,
71	.args = NULL,
72};
73
74CMD_COMMAND(ns_cmd);
75
76static struct active_options {
77	const char	*dev;
78} active_opt = {
79	.dev = NULL,
80};
81
82static const struct args active_args[] = {
83	{ arg_string, &active_opt.dev, "controller-id|namespace-id" },
84	{ arg_none, NULL, NULL },
85};
86
87static struct cmd active_cmd = {
88	.name = "active",
89	.fn = nsactive,
90	.descr = "List active (attached) namespaces",
91	.ctx_size = sizeof(active_opt),
92	.opts = NULL,
93	.args = active_args,
94};
95
96CMD_SUBCOMMAND(ns_cmd, active_cmd);
97
98static struct cmd allocated_cmd = {
99	.name = "allocated",
100	.fn = nsallocated,
101	.descr = "List allocated (created) namespaces",
102	.ctx_size = sizeof(active_opt),
103	.opts = NULL,
104	.args = active_args,
105};
106
107CMD_SUBCOMMAND(ns_cmd, allocated_cmd);
108
109static struct controllers_options {
110	const char	*dev;
111} controllers_opt = {
112	.dev = NULL,
113};
114
115static const struct args controllers_args[] = {
116	{ arg_string, &controllers_opt.dev, "controller-id|namespace-id" },
117	{ arg_none, NULL, NULL },
118};
119
120static struct cmd controllers_cmd = {
121	.name = "controllers",
122	.fn = nscontrollers,
123	.descr = "List all controllers in NVM subsystem",
124	.ctx_size = sizeof(controllers_opt),
125	.opts = NULL,
126	.args = controllers_args,
127};
128
129CMD_SUBCOMMAND(ns_cmd, controllers_cmd);
130
131static struct create_options {
132	uint64_t nsze;
133	uint64_t cap;
134	uint32_t lbaf;
135	uint32_t mset;
136	uint32_t nmic;
137	uint32_t pi;
138	uint32_t pil;
139	uint32_t flbas;
140	uint32_t dps;
141//	uint32_t block_size;
142	const char *dev;
143} create_opt = {
144	.nsze = NONE64,
145	.cap = NONE64,
146	.lbaf = NONE,
147	.mset = NONE,
148	.nmic = NONE,
149	.pi = NONE,
150	.pil = NONE,
151	.flbas = NONE,
152	.dps = NONE,
153	.dev = NULL,
154//	.block_size = NONE,
155};
156
157static const struct opts create_opts[] = {
158	OPT("nsze", 's', arg_uint64, create_opt, nsze,
159	    "The namespace size"),
160	OPT("ncap", 'c', arg_uint64, create_opt, cap,
161	    "The capacity of the namespace (<= ns size)"),
162	OPT("lbaf", 'f', arg_uint32, create_opt, lbaf,
163	    "The FMT field of the FLBAS"),
164	OPT("mset", 'm', arg_uint32, create_opt, mset,
165	    "The MSET field of the FLBAS"),
166	OPT("nmic", 'n', arg_uint32, create_opt, nmic,
167	    "Namespace multipath and sharing capabilities"),
168	OPT("pi", 'p', arg_uint32, create_opt, pi,
169	    "PI field of FLBAS"),
170	OPT("pil", 'l', arg_uint32, create_opt, pil,
171	    "PIL field of FLBAS"),
172	OPT("flbas", 'L', arg_uint32, create_opt, flbas,
173	    "Namespace formatted logical block size setting"),
174	OPT("dps", 'd', arg_uint32, create_opt, dps,
175	    "Data protection settings"),
176//	OPT("block-size", 'b', arg_uint32, create_opt, block_size,
177//	    "Blocksize of the namespace"),
178	OPT_END
179};
180
181static const struct args create_args[] = {
182	{ arg_string, &create_opt.dev, "controller-id|namespace-id" },
183	{ arg_none, NULL, NULL },
184};
185
186static struct cmd create_cmd = {
187	.name = "create",
188	.fn = nscreate,
189	.descr = "Create a namespace",
190	.ctx_size = sizeof(create_opt),
191	.opts = create_opts,
192	.args = create_args,
193};
194
195CMD_SUBCOMMAND(ns_cmd, create_cmd);
196
197static struct delete_options {
198	uint32_t	nsid;
199	const char	*dev;
200} delete_opt = {
201	.nsid = NONE,
202	.dev = NULL,
203};
204
205static const struct opts delete_opts[] = {
206	OPT("namespace-id", 'n', arg_uint32, delete_opt, nsid,
207	    "The namespace ID to delete"),
208	OPT_END
209};
210
211static const struct args delete_args[] = {
212	{ arg_string, &delete_opt.dev, "controller-id|namespace-id" },
213	{ arg_none, NULL, NULL },
214};
215
216static struct cmd delete_cmd = {
217	.name = "delete",
218	.fn = nsdelete,
219	.descr = "Delete a namespace",
220	.ctx_size = sizeof(delete_opt),
221	.opts = delete_opts,
222	.args = delete_args,
223};
224
225CMD_SUBCOMMAND(ns_cmd, delete_cmd);
226
227static struct attach_options {
228	uint32_t	nsid;
229	uint32_t	ctrlrid;
230	const char	*dev;
231} attach_opt = {
232	.nsid = NONE,
233	.ctrlrid = NONE - 1,
234	.dev = NULL,
235};
236
237static const struct opts attach_opts[] = {
238	OPT("namespace-id", 'n', arg_uint32, attach_opt, nsid,
239	    "The namespace ID to attach"),
240	OPT("controller", 'c', arg_uint32, attach_opt, ctrlrid,
241	    "The controller ID to attach"),
242	OPT_END
243};
244
245static const struct args attach_args[] = {
246	{ arg_string, &attach_opt.dev, "controller-id|namespace-id" },
247	{ arg_none, NULL, NULL },
248};
249
250static struct cmd attach_cmd = {
251	.name = "attach",
252	.fn = nsattach,
253	.descr = "Attach a controller to a namespace",
254	.ctx_size = sizeof(attach_opt),
255	.opts = attach_opts,
256	.args = attach_args,
257};
258
259CMD_SUBCOMMAND(ns_cmd, attach_cmd);
260
261static struct attached_options {
262	uint32_t	nsid;
263	const char	*dev;
264} attached_opt = {
265	.nsid = NONE,
266	.dev = NULL,
267};
268
269static const struct opts attached_opts[] = {
270	OPT("namespace-id", 'n', arg_uint32, attached_opt, nsid,
271	    "The namespace ID to request attached controllers"),
272	OPT_END
273};
274
275static const struct args attached_args[] = {
276	{ arg_string, &attached_opt.dev, "controller-id|namespace-id" },
277	{ arg_none, NULL, NULL },
278};
279
280static struct cmd attached_cmd = {
281	.name = "attached",
282	.fn = nsattached,
283	.descr = "List controllers attached to a namespace",
284	.ctx_size = sizeof(attached_opt),
285	.opts = attached_opts,
286	.args = attached_args,
287};
288
289CMD_SUBCOMMAND(ns_cmd, attached_cmd);
290
291static struct detach_options {
292	uint32_t	nsid;
293	uint32_t	ctrlrid;
294	const char	*dev;
295} detach_opt = {
296	.nsid = NONE,
297	.ctrlrid = NONE - 1,
298	.dev = NULL,
299};
300
301static const struct opts detach_opts[] = {
302	OPT("namespace-id", 'n', arg_uint32, detach_opt, nsid,
303	    "The namespace ID to detach"),
304	OPT("controller", 'c', arg_uint32, detach_opt, ctrlrid,
305	    "The controller ID to detach"),
306	OPT_END
307};
308
309static const struct args detach_args[] = {
310	{ arg_string, &detach_opt.dev, "controller-id|namespace-id" },
311	{ arg_none, NULL, NULL },
312};
313
314static struct cmd detach_cmd = {
315	.name = "detach",
316	.fn = nsdetach,
317	.descr = "Detach a controller from a namespace",
318	.ctx_size = sizeof(detach_opt),
319	.opts = detach_opts,
320	.args = detach_args,
321};
322
323CMD_SUBCOMMAND(ns_cmd, detach_cmd);
324
325static struct identify_options {
326	bool		hex;
327	bool		verbose;
328	const char	*dev;
329	uint32_t	nsid;
330} identify_opt = {
331	.hex = false,
332	.verbose = false,
333	.dev = NULL,
334	.nsid = NONE,
335};
336
337static const struct opts identify_opts[] = {
338	OPT("hex", 'x', arg_none, identify_opt, hex,
339	    "Print identiy information in hex"),
340	OPT("verbose", 'v', arg_none, identify_opt, verbose,
341	    "More verbosity: print entire identify table"),
342	OPT("nsid", 'n', arg_uint32, identify_opt, nsid,
343	    "The namespace ID to print IDENTIFY for"),
344	{ NULL, 0, arg_none, NULL, NULL }
345};
346
347static const struct args identify_args[] = {
348	{ arg_string, &identify_opt.dev, "controller-id|namespace-id" },
349	{ arg_none, NULL, NULL },
350};
351
352static struct cmd identify_cmd = {
353	.name = "identify",
354	.fn = nsidentify,
355	.descr = "Print IDENTIFY for allocated namespace",
356	.ctx_size = sizeof(identify_opt),
357	.opts = identify_opts,
358	.args = identify_args,
359};
360
361CMD_SUBCOMMAND(ns_cmd, identify_cmd);
362
363/* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */
364
365struct ns_result_str {
366	uint16_t res;
367	const char * str;
368};
369
370static struct ns_result_str ns_result[] = {
371	{ 0x2,  "Invalid Field"},
372	{ 0xa,  "Invalid Format"},
373	{ 0xb,  "Invalid Namespace or format"},
374	{ 0x15, "Namespace insufficent capacity"},
375	{ 0x16, "Namespace ID unavaliable"},
376	{ 0x18, "Namespace already attached"},
377	{ 0x19, "Namespace is private"},
378	{ 0x1a, "Namespace is not attached"},
379	{ 0x1b, "Thin provisioning not supported"},
380	{ 0x1c, "Controller list invalid"},
381	{ 0x24, "ANA Group Identifier Invalid"},
382	{ 0x25, "ANA Attach Failed"},
383	{ 0xFFFF, "Unknown"}
384};
385
386static const char *
387get_res_str(uint16_t res)
388{
389	struct ns_result_str *t = ns_result;
390
391	while (t->res != 0xFFFF) {
392		if (t->res == res)
393			return (t->str);
394		t++;
395	}
396	return t->str;
397}
398
399static void
400nsactive(const struct cmd *f, int argc, char *argv[])
401{
402	struct nvme_pt_command	pt;
403	struct nvme_controller_data cd;
404	int	fd, i;
405	char	*path;
406	uint32_t nsid;
407	uint32_t list[1024];
408
409	if (arg_parse(argc, argv, f))
410		return;
411	open_dev(active_opt.dev, &fd, 0, 1);
412	get_nsid(fd, &path, &nsid);
413	if (nsid != 0) {
414		close(fd);
415		open_dev(path, &fd, 0, 1);
416	}
417	free(path);
418	if (read_controller_data(fd, &cd))
419		errx(EX_IOERR, "Identify request failed");
420
421	/* Check that controller can execute this command. */
422	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
423	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
424		errx(EX_UNAVAILABLE, "controller does not support namespace management");
425
426	memset(&pt, 0, sizeof(pt));
427	pt.cmd.opc = NVME_OPC_IDENTIFY;
428	pt.cmd.nsid = htole32(0);
429	pt.cmd.cdw10 = htole32(0x02);
430	pt.buf = list;
431	pt.len = sizeof(list);
432	pt.is_read = 1;
433	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
434		err(EX_IOERR, "identify request failed");
435	if (nvme_completion_is_error(&pt.cpl))
436		errx(EX_IOERR, "identify request returned error");
437
438	printf("Active namespaces:\n");
439	for (i = 0; list[i] != 0; i++)
440		printf("%10d\n", le32toh(list[i]));
441
442	exit(0);
443}
444
445static void
446nsallocated(const struct cmd *f, int argc, char *argv[])
447{
448	struct nvme_pt_command	pt;
449	struct nvme_controller_data cd;
450	int	fd, i;
451	char	*path;
452	uint32_t nsid;
453	uint32_t list[1024];
454
455	if (arg_parse(argc, argv, f))
456		return;
457	open_dev(active_opt.dev, &fd, 0, 1);
458	get_nsid(fd, &path, &nsid);
459	if (nsid != 0) {
460		close(fd);
461		open_dev(path, &fd, 0, 1);
462	}
463	free(path);
464	if (read_controller_data(fd, &cd))
465		errx(EX_IOERR, "Identify request failed");
466
467	/* Check that controller can execute this command. */
468	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
469	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
470		errx(EX_UNAVAILABLE, "controller does not support namespace management");
471
472	memset(&pt, 0, sizeof(pt));
473	pt.cmd.opc = NVME_OPC_IDENTIFY;
474	pt.cmd.nsid = htole32(0);
475	pt.cmd.cdw10 = htole32(0x10);
476	pt.buf = list;
477	pt.len = sizeof(list);
478	pt.is_read = 1;
479	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
480		err(EX_IOERR, "identify request failed");
481	if (nvme_completion_is_error(&pt.cpl))
482		errx(EX_IOERR, "identify request returned error");
483
484	printf("Allocated namespaces:\n");
485	for (i = 0; list[i] != 0; i++)
486		printf("%10d\n", le32toh(list[i]));
487
488	exit(0);
489}
490
491static void
492nscontrollers(const struct cmd *f, int argc, char *argv[])
493{
494	struct nvme_pt_command	pt;
495	struct nvme_controller_data cd;
496	int	fd, i, n;
497	char	*path;
498	uint32_t nsid;
499	uint16_t clist[2048];
500
501	if (arg_parse(argc, argv, f))
502		return;
503	open_dev(controllers_opt.dev, &fd, 0, 1);
504	get_nsid(fd, &path, &nsid);
505	if (nsid != 0) {
506		close(fd);
507		open_dev(path, &fd, 0, 1);
508	}
509	free(path);
510	if (read_controller_data(fd, &cd))
511		errx(EX_IOERR, "Identify request failed");
512
513	/* Check that controller can execute this command. */
514	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
515	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
516		errx(EX_UNAVAILABLE, "controller does not support namespace management");
517
518	memset(&pt, 0, sizeof(pt));
519	pt.cmd.opc = NVME_OPC_IDENTIFY;
520	pt.cmd.cdw10 = htole32(0x13);
521	pt.buf = clist;
522	pt.len = sizeof(clist);
523	pt.is_read = 1;
524	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
525		err(EX_IOERR, "identify request failed");
526	if (nvme_completion_is_error(&pt.cpl))
527		errx(EX_IOERR, "identify request returned error");
528
529	n = le16toh(clist[0]);
530	printf("NVM subsystem includes %d controller(s):\n", n);
531	for (i = 0; i < n; i++)
532		printf("  0x%04x\n", le16toh(clist[i + 1]));
533
534	exit(0);
535}
536
537/*
538 * NS MGMT Command specific status values:
539 * 0xa = Invalid Format
540 * 0x15 = Namespace Insuffience capacity
541 * 0x16 = Namespace ID  unavailable (number namespaces exceeded)
542 * 0xb = Thin Provisioning Not supported
543 */
544static void
545nscreate(const struct cmd *f, int argc, char *argv[])
546{
547	struct nvme_pt_command	pt;
548	struct nvme_controller_data cd;
549	struct nvme_namespace_data nsdata;
550	int	fd, result;
551	char	*path;
552	uint32_t nsid;
553
554	if (arg_parse(argc, argv, f))
555		return;
556
557	if (create_opt.cap == NONE64)
558		create_opt.cap = create_opt.nsze;
559	if (create_opt.nsze == NONE64) {
560		fprintf(stderr,
561		    "Size not specified\n");
562		arg_help(argc, argv, f);
563	}
564
565	open_dev(create_opt.dev, &fd, 1, 1);
566	get_nsid(fd, &path, &nsid);
567	if (nsid != 0) {
568		close(fd);
569		open_dev(path, &fd, 1, 1);
570	}
571	free(path);
572	if (read_controller_data(fd, &cd))
573		errx(EX_IOERR, "Identify request failed");
574
575	/* Check that controller can execute this command. */
576	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
577	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
578		errx(EX_UNAVAILABLE, "controller does not support namespace management");
579
580	/* Allow namespaces sharing if Multi-Path I/O is supported. */
581	if (create_opt.nmic == NONE) {
582		create_opt.nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK <<
583		     NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0;
584	}
585
586	memset(&nsdata, 0, sizeof(nsdata));
587	nsdata.nsze = create_opt.nsze;
588	nsdata.ncap = create_opt.cap;
589	if (create_opt.flbas == NONE)
590		nsdata.flbas = ((create_opt.lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK)
591		    << NVME_NS_DATA_FLBAS_FORMAT_SHIFT) |
592		    ((create_opt.mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK)
593			<< NVME_NS_DATA_FLBAS_EXTENDED_SHIFT);
594	else
595		nsdata.flbas = create_opt.flbas;
596	if (create_opt.dps == NONE)
597		nsdata.dps = ((create_opt.pi & NVME_NS_DATA_DPS_MD_START_MASK)
598		    << NVME_NS_DATA_DPS_MD_START_SHIFT) |
599		    ((create_opt.pil & NVME_NS_DATA_DPS_PIT_MASK)
600			<< NVME_NS_DATA_DPS_PIT_SHIFT);
601	else
602		nsdata.dps = create_opt.dps;
603	nsdata.nmic = create_opt.nmic;
604	nvme_namespace_data_swapbytes(&nsdata);
605
606	memset(&pt, 0, sizeof(pt));
607	pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
608	pt.cmd.cdw10 = htole32(0); /* create */
609	pt.buf = &nsdata;
610	pt.len = sizeof(struct nvme_namespace_data);
611	pt.is_read = 0; /* passthrough writes data to ctrlr */
612	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
613		errx(EX_IOERR, "ioctl request to %s failed: %d", create_opt.dev, result);
614
615	if (nvme_completion_is_error(&pt.cpl)) {
616		errx(EX_IOERR, "namespace creation failed: %s",
617		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
618		    NVME_STATUS_SC_MASK));
619	}
620	printf("namespace %d created\n", pt.cpl.cdw0);
621	exit(0);
622}
623
624static void
625nsdelete(const struct cmd *f, int argc, char *argv[])
626{
627	struct nvme_pt_command	pt;
628	struct nvme_controller_data cd;
629	int	fd, result;
630	char	*path;
631	uint32_t nsid;
632	char buf[2];
633
634	if (arg_parse(argc, argv, f))
635		return;
636
637	open_dev(delete_opt.dev, &fd, 1, 1);
638	get_nsid(fd, &path, &nsid);
639	if (nsid != 0) {
640		close(fd);
641		open_dev(path, &fd, 1, 1);
642	} else if (delete_opt.nsid == NONE) {
643		close(fd);
644		fprintf(stderr, "No NSID specified");
645		arg_help(argc, argv, f);
646	}
647	if (delete_opt.nsid != NONE)
648		nsid = delete_opt.nsid;
649	free(path);
650	if (read_controller_data(fd, &cd))
651		errx(EX_IOERR, "Identify request failed");
652
653	/* Check that controller can execute this command. */
654	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
655	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
656		errx(EX_UNAVAILABLE, "controller does not support namespace management");
657
658	memset(&pt, 0, sizeof(pt));
659	pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
660	pt.cmd.cdw10 = htole32(1); /* delete */
661	pt.buf = buf;
662	pt.len = sizeof(buf);
663	pt.is_read = 1;
664	pt.cmd.nsid = nsid;
665
666	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
667		errx(EX_IOERR, "ioctl request to %s failed: %d", delete_opt.dev, result);
668
669	if (nvme_completion_is_error(&pt.cpl)) {
670		errx(EX_IOERR, "namespace deletion failed: %s",
671		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
672		    NVME_STATUS_SC_MASK));
673	}
674	printf("namespace %d deleted\n", nsid);
675	exit(0);
676}
677
678/*
679 * Attach and Detach use Dword 10, and a controller list (section 4.9)
680 * This struct is 4096 bytes in size.
681 * 0h = attach
682 * 1h = detach
683 *
684 * Result values for both attach/detach:
685 *
686 * Completion 18h = Already attached
687 *            19h = NS is private and already attached to a controller
688 *            1Ah = Not attached, request could not be completed
689 *            1Ch = Controller list invalid.
690 *
691 * 0x2 Invalid Field can occur if ctrlrid d.n.e in system.
692 */
693static void
694nsattach(const struct cmd *f, int argc, char *argv[])
695{
696	struct nvme_pt_command	pt;
697	struct nvme_controller_data cd;
698	int	fd, result;
699	char	*path;
700	uint32_t nsid;
701	uint16_t clist[2048];
702
703	if (arg_parse(argc, argv, f))
704		return;
705	open_dev(attach_opt.dev, &fd, 1, 1);
706	get_nsid(fd, &path, &nsid);
707	if (nsid != 0) {
708		close(fd);
709		open_dev(path, &fd, 1, 1);
710	} else if (attach_opt.nsid == NONE) {
711		close(fd);
712		fprintf(stderr, "No NSID specified");
713		arg_help(argc, argv, f);
714	}
715	if (attach_opt.nsid != NONE)
716		nsid = attach_opt.nsid;
717	if (read_controller_data(fd, &cd))
718		errx(EX_IOERR, "Identify request failed");
719
720	/* Check that controller can execute this command. */
721	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
722	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
723		errx(EX_UNAVAILABLE, "controller does not support namespace management");
724
725	if (attach_opt.ctrlrid == NONE) {
726		/* Get full list of controllers to attach to. */
727		memset(&pt, 0, sizeof(pt));
728		pt.cmd.opc = NVME_OPC_IDENTIFY;
729		pt.cmd.cdw10 = htole32(0x13);
730		pt.buf = clist;
731		pt.len = sizeof(clist);
732		pt.is_read = 1;
733		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
734			err(EX_IOERR, "identify request failed");
735		if (nvme_completion_is_error(&pt.cpl))
736			errx(EX_IOERR, "identify request returned error");
737	} else {
738		/* By default attach to this controller. */
739		if (attach_opt.ctrlrid == NONE - 1)
740			attach_opt.ctrlrid = cd.ctrlr_id;
741		memset(&clist, 0, sizeof(clist));
742		clist[0] = htole16(1);
743		clist[1] = htole16(attach_opt.ctrlrid);
744	}
745
746	memset(&pt, 0, sizeof(pt));
747	pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
748	pt.cmd.cdw10 = htole32(0); /* attach */
749	pt.cmd.nsid = nsid;
750	pt.buf = &clist;
751	pt.len = sizeof(clist);
752
753	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
754		errx(EX_IOERR, "ioctl request to %s failed: %d", attach_opt.dev, result);
755
756	if (nvme_completion_is_error(&pt.cpl)) {
757		errx(EX_IOERR, "namespace attach failed: %s",
758		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
759		    NVME_STATUS_SC_MASK));
760	}
761	printf("namespace %d attached\n", nsid);
762	exit(0);
763}
764
765static void
766nsdetach(const struct cmd *f, int argc, char *argv[])
767{
768	struct nvme_pt_command	pt;
769	struct nvme_controller_data cd;
770	int	fd, result;
771	char	*path;
772	uint32_t nsid;
773	uint16_t clist[2048];
774
775	if (arg_parse(argc, argv, f))
776		return;
777	open_dev(detach_opt.dev, &fd, 1, 1);
778	get_nsid(fd, &path, &nsid);
779	if (nsid != 0) {
780		close(fd);
781		open_dev(path, &fd, 1, 1);
782	} else if (detach_opt.nsid == NONE) {
783		close(fd);
784		fprintf(stderr, "No NSID specified");
785		arg_help(argc, argv, f);
786	}
787	if (detach_opt.nsid != NONE)
788		nsid = detach_opt.nsid;
789	if (read_controller_data(fd, &cd))
790		errx(EX_IOERR, "Identify request failed");
791
792	/* Check that controller can execute this command. */
793	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
794	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
795		errx(EX_UNAVAILABLE, "controller does not support namespace management");
796
797	if (detach_opt.ctrlrid == NONE) {
798		/* Get list of controllers this namespace attached to. */
799		memset(&pt, 0, sizeof(pt));
800		pt.cmd.opc = NVME_OPC_IDENTIFY;
801		pt.cmd.nsid = htole32(nsid);
802		pt.cmd.cdw10 = htole32(0x12);
803		pt.buf = clist;
804		pt.len = sizeof(clist);
805		pt.is_read = 1;
806		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
807			err(EX_IOERR, "identify request failed");
808		if (nvme_completion_is_error(&pt.cpl))
809			errx(EX_IOERR, "identify request returned error");
810		if (clist[0] == 0) {
811			detach_opt.ctrlrid = cd.ctrlr_id;
812			memset(&clist, 0, sizeof(clist));
813			clist[0] = htole16(1);
814			clist[1] = htole16(detach_opt.ctrlrid);
815		}
816	} else {
817		/* By default detach from this controller. */
818		if (detach_opt.ctrlrid == NONE - 1)
819			detach_opt.ctrlrid = cd.ctrlr_id;
820		memset(&clist, 0, sizeof(clist));
821		clist[0] = htole16(1);
822		clist[1] = htole16(detach_opt.ctrlrid);
823	}
824
825	memset(&pt, 0, sizeof(pt));
826	pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
827	pt.cmd.cdw10 = htole32(1); /* detach */
828	pt.cmd.nsid = nsid;
829	pt.buf = &clist;
830	pt.len = sizeof(clist);
831
832	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
833		errx(EX_IOERR, "ioctl request to %s failed: %d", detach_opt.dev, result);
834
835	if (nvme_completion_is_error(&pt.cpl)) {
836		errx(EX_IOERR, "namespace detach failed: %s",
837		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
838		    NVME_STATUS_SC_MASK));
839	}
840	printf("namespace %d detached\n", nsid);
841	exit(0);
842}
843
844static void
845nsattached(const struct cmd *f, int argc, char *argv[])
846{
847	struct nvme_pt_command	pt;
848	struct nvme_controller_data cd;
849	int	fd, i, n;
850	char	*path;
851	uint32_t nsid;
852	uint16_t clist[2048];
853
854	if (arg_parse(argc, argv, f))
855		return;
856	open_dev(attached_opt.dev, &fd, 0, 1);
857	get_nsid(fd, &path, &nsid);
858	if (nsid != 0) {
859		close(fd);
860		open_dev(path, &fd, 1, 1);
861	} else if (attached_opt.nsid == NONE) {
862		close(fd);
863		fprintf(stderr, "No NSID specified");
864		arg_help(argc, argv, f);
865	}
866	if (attached_opt.nsid != NONE)
867		nsid = attached_opt.nsid;
868	if (read_controller_data(fd, &cd))
869		errx(EX_IOERR, "Identify request failed");
870
871	/* Check that controller can execute this command. */
872	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
873	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
874		errx(EX_UNAVAILABLE, "controller does not support namespace management");
875
876	memset(&pt, 0, sizeof(pt));
877	pt.cmd.opc = NVME_OPC_IDENTIFY;
878	pt.cmd.nsid = htole32(nsid);
879	pt.cmd.cdw10 = htole32(0x12);
880	pt.buf = clist;
881	pt.len = sizeof(clist);
882	pt.is_read = 1;
883	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
884		err(EX_IOERR, "identify request failed");
885	if (nvme_completion_is_error(&pt.cpl))
886		errx(EX_IOERR, "identify request returned error");
887
888	n = le16toh(clist[0]);
889	printf("Attached %d controller(s):\n", n);
890	for (i = 0; i < n; i++)
891		printf("  0x%04x\n", le16toh(clist[i + 1]));
892
893	exit(0);
894}
895
896static void
897nsidentify(const struct cmd *f, int argc, char *argv[])
898{
899	struct nvme_pt_command	pt;
900	struct nvme_controller_data cd;
901	struct nvme_namespace_data nsdata;
902	uint8_t	*data;
903	int	fd;
904	char	*path;
905	uint32_t nsid;
906	u_int	i;
907
908	if (arg_parse(argc, argv, f))
909		return;
910	open_dev(identify_opt.dev, &fd, 0, 1);
911	get_nsid(fd, &path, &nsid);
912	if (nsid != 0) {
913		close(fd);
914		open_dev(path, &fd, 1, 1);
915	} else if (identify_opt.nsid == NONE) {
916		close(fd);
917		fprintf(stderr, "No NSID specified");
918		arg_help(argc, argv, f);
919	}
920	if (identify_opt.nsid != NONE)
921		nsid = identify_opt.nsid;
922	if (read_controller_data(fd, &cd))
923		errx(EX_IOERR, "Identify request failed");
924
925	/* Check that controller can execute this command. */
926	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
927	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
928		errx(EX_UNAVAILABLE, "controller does not support namespace management");
929
930	memset(&pt, 0, sizeof(pt));
931	pt.cmd.opc = NVME_OPC_IDENTIFY;
932	pt.cmd.nsid = htole32(nsid);
933	pt.cmd.cdw10 = htole32(0x11);
934	pt.buf = &nsdata;
935	pt.len = sizeof(nsdata);
936	pt.is_read = 1;
937
938	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
939		err(EX_IOERR, "identify request failed");
940
941	if (nvme_completion_is_error(&pt.cpl))
942		errx(EX_IOERR, "identify request returned error");
943
944	close(fd);
945
946	data = (uint8_t *)&nsdata;
947	for (i = 0; i < sizeof(nsdata); i++) {
948		if (data[i] != 0)
949			break;
950	}
951	if (i == sizeof(nsdata))
952		errx(EX_UNAVAILABLE, "namespace %d is not allocated", nsid);
953
954	/* Convert data to host endian */
955	nvme_namespace_data_swapbytes(&nsdata);
956
957	if (identify_opt.hex) {
958		i = sizeof(struct nvme_namespace_data);
959		if (!identify_opt.verbose) {
960			for (; i > 384; i--) {
961				if (data[i - 1] != 0)
962					break;
963			}
964		}
965		print_hex(&nsdata, i);
966		exit(0);
967	}
968
969	print_namespace(&nsdata);
970	exit(0);
971}
972
973static void
974ns(const struct cmd *nf __unused, int argc, char *argv[])
975{
976
977	cmd_dispatch(argc, argv, &ns_cmd);
978}
979