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