xref: /freebsd/sbin/nvmecontrol/ns.c (revision d6eb98610fa65663bf0df4574b7cb2c5c4ffda71)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2017 Netflix, Inc.
5  * Copyright (C) 2018 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 <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include "nvmecontrol.h"
43 
44 /* Tables for command line parsing */
45 
46 static cmd_fn_t ns;
47 static cmd_fn_t nscreate;
48 static cmd_fn_t nsdelete;
49 static cmd_fn_t nsattach;
50 static cmd_fn_t nsdetach;
51 
52 #define NONE 0xffffffffu
53 #define NONE64 0xffffffffffffffffull
54 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
55 #define OPT_END	{ NULL, 0, arg_none, NULL, NULL }
56 
57 static struct cmd ns_cmd = {
58 	.name = "ns", .fn = ns, .descr = "Namespace commands", .ctx_size = 0, .opts = NULL, .args = NULL,
59 };
60 
61 CMD_COMMAND(ns_cmd);
62 
63 static struct create_options {
64 	uint64_t nsze;
65 	uint64_t cap;
66 	uint32_t lbaf;
67 	uint32_t mset;
68 	uint32_t nmic;
69 	uint32_t pi;
70 	uint32_t pil;
71 	uint32_t flbas;
72 	uint32_t dps;
73 //	uint32_t block_size;
74 	const char *dev;
75 } create_opt = {
76 	.nsze = NONE64,
77 	.cap = NONE64,
78 	.lbaf = NONE,
79 	.mset = NONE,
80 	.nmic = NONE,
81 	.pi = NONE,
82 	.pil = NONE,
83 	.flbas = NONE,
84 	.dps = NONE,
85 	.dev = NULL,
86 //	.block_size = NONE,
87 };
88 
89 static const struct opts create_opts[] = {
90 	OPT("nsze", 's', arg_uint64, create_opt, nsze,
91 	    "The namespace size"),
92 	OPT("ncap", 'c', arg_uint64, create_opt, cap,
93 	    "The capacity of the namespace (<= ns size)"),
94 	OPT("lbaf", 'f', arg_uint32, create_opt, lbaf,
95 	    "The FMT field of the FLBAS"),
96 	OPT("mset", 'm', arg_uint32, create_opt, mset,
97 	    "The MSET field of the FLBAS"),
98 	OPT("nmic", 'n', arg_uint32, create_opt, nmic,
99 	    "Namespace multipath and sharing capabilities"),
100 	OPT("pi", 'p', arg_uint32, create_opt, pi,
101 	    "PI field of FLBAS"),
102 	OPT("pil", 'l', arg_uint32, create_opt, pil,
103 	    "PIL field of FLBAS"),
104 	OPT("flbas", 'l', arg_uint32, create_opt, flbas,
105 	    "Namespace formatted logical block size setting"),
106 	OPT("dps", 'd', arg_uint32, create_opt, dps,
107 	    "Data protection settings"),
108 //	OPT("block-size", 'b', arg_uint32, create_opt, block_size,
109 //	    "Blocksize of the namespace"),
110 	OPT_END
111 };
112 
113 static const struct args create_args[] = {
114 	{ arg_string, &create_opt.dev, "controller-id" },
115 	{ arg_none, NULL, NULL },
116 };
117 
118 static struct cmd create_cmd = {
119 	.name = "create",
120 	.fn = nscreate,
121 	.descr = "Create a new namespace",
122 	.ctx_size = sizeof(create_opt),
123 	.opts = create_opts,
124 	.args = create_args,
125 };
126 
127 CMD_SUBCOMMAND(ns_cmd, create_cmd);
128 
129 static struct delete_options {
130 	uint32_t	nsid;
131 	const char	*dev;
132 } delete_opt = {
133 	.nsid = NONE,
134 	.dev = NULL,
135 };
136 
137 static const struct opts delete_opts[] = {
138 	OPT("namespace-id", 'n', arg_uint32, delete_opt, nsid,
139 	    "The namespace ID to delete"),
140 	OPT_END
141 };
142 
143 static const struct args delete_args[] = {
144 	{ arg_string, &delete_opt.dev, "controller-id" },
145 	{ arg_none, NULL, NULL },
146 };
147 
148 static struct cmd delete_cmd = {
149 	.name = "delete",
150 	.fn = nsdelete,
151 	.descr = "Delete a new namespace",
152 	.ctx_size = sizeof(delete_opt),
153 	.opts = delete_opts,
154 	.args = delete_args,
155 };
156 
157 CMD_SUBCOMMAND(ns_cmd, delete_cmd);
158 
159 static struct attach_options {
160 	uint32_t	nsid;
161 	uint32_t	ctrlrid;
162 	const char	*dev;
163 } attach_opt = {
164 	.nsid = NONE,
165 	.ctrlrid = NONE - 1,
166 	.dev = NULL,
167 };
168 
169 static const struct opts attach_opts[] = {
170 	OPT("namespace-id", 'n', arg_uint32, attach_opt, nsid,
171 	    "The namespace ID to attach"),
172 	OPT("controller", 'c', arg_uint32, attach_opt, nsid,
173 	    "The controller ID to attach"),
174 	OPT_END
175 };
176 
177 static const struct args attach_args[] = {
178 	{ arg_string, &attach_opt.dev, "controller-id" },
179 	{ arg_none, NULL, NULL },
180 };
181 
182 static struct cmd attach_cmd = {
183 	.name = "attach",
184 	.fn = nsattach,
185 	.descr = "Attach a new namespace",
186 	.ctx_size = sizeof(attach_opt),
187 	.opts = attach_opts,
188 	.args = attach_args,
189 };
190 
191 CMD_SUBCOMMAND(ns_cmd, attach_cmd);
192 
193 static struct detach_options {
194 	uint32_t	nsid;
195 	uint32_t	ctrlrid;
196 	const char	*dev;
197 } detach_opt = {
198 	.nsid = NONE,
199 	.ctrlrid = NONE - 1,
200 	.dev = NULL,
201 };
202 
203 static const struct opts detach_opts[] = {
204 	OPT("namespace-id", 'n', arg_uint32, detach_opt, nsid,
205 	    "The namespace ID to detach"),
206 	OPT("controller", 'c', arg_uint32, detach_opt, nsid,
207 	    "The controller ID to detach"),
208 	OPT_END
209 };
210 
211 static const struct args detach_args[] = {
212 	{ arg_string, &detach_opt.dev, "controller-id" },
213 	{ arg_none, NULL, NULL },
214 };
215 
216 static struct cmd detach_cmd = {
217 	.name = "detach",
218 	.fn = nsdetach,
219 	.descr = "Detach a new namespace",
220 	.ctx_size = sizeof(detach_opt),
221 	.opts = detach_opts,
222 	.args = detach_args,
223 };
224 
225 CMD_SUBCOMMAND(ns_cmd, detach_cmd);
226 
227 #define NS_USAGE							\
228 	"ns (create|delete|attach|detach)\n"
229 
230 /* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */
231 
232 struct ns_result_str {
233 	uint16_t res;
234 	const char * str;
235 };
236 
237 static struct ns_result_str ns_result[] = {
238 	{ 0x2,  "Invalid Field"},
239 	{ 0xa,  "Invalid Format"},
240 	{ 0xb,  "Invalid Namespace or format"},
241 	{ 0x15, "Namespace insufficent capacity"},
242 	{ 0x16, "Namespace ID unavaliable"},
243 	{ 0x18, "Namespace already attached"},
244 	{ 0x19, "Namespace is private"},
245 	{ 0x1a, "Namespace is not attached"},
246 	{ 0x1b, "Thin provisioning not supported"},
247 	{ 0x1c, "Controller list invalid"},
248 	{ 0xFFFF, "Unknown"}
249 };
250 
251 static const char *
252 get_res_str(uint16_t res)
253 {
254 	struct ns_result_str *t = ns_result;
255 
256 	while (t->res != 0xFFFF) {
257 		if (t->res == res)
258 			return (t->str);
259 		t++;
260 	}
261 	return t->str;
262 }
263 
264 /*
265  * NS MGMT Command specific status values:
266  * 0xa = Invalid Format
267  * 0x15 = Namespace Insuffience capacity
268  * 0x16 = Namespace ID  unavailable (number namespaces exceeded)
269  * 0xb = Thin Provisioning Not supported
270  */
271 static void
272 nscreate(const struct cmd *f, int argc, char *argv[])
273 {
274 	struct nvme_pt_command	pt;
275 	struct nvme_controller_data cd;
276 	struct nvme_namespace_data nsdata;
277 	int	fd, result;
278 
279 	if (arg_parse(argc, argv, f))
280 		return;
281 
282 	if (create_opt.cap == NONE64)
283 		create_opt.cap = create_opt.nsze;
284 	if (create_opt.nsze == NONE64) {
285 		fprintf(stderr,
286 		    "Size not specified\n");
287 		arg_help(argc, argv, f);
288 	}
289 
290 	open_dev(create_opt.dev, &fd, 1, 1);
291 	read_controller_data(fd, &cd);
292 
293 	/* Check that controller can execute this command. */
294 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
295 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
296 		errx(1, "controller does not support namespace management");
297 
298 	/* Allow namespaces sharing if Multi-Path I/O is supported. */
299 	if (create_opt.nmic == NONE) {
300 		create_opt.nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK <<
301 		     NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0;
302 	}
303 
304 	memset(&nsdata, 0, sizeof(nsdata));
305 	nsdata.nsze = create_opt.nsze;
306 	nsdata.ncap = create_opt.cap;
307 	if (create_opt.flbas == NONE)
308 		nsdata.flbas = ((create_opt.lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK)
309 		    << NVME_NS_DATA_FLBAS_FORMAT_SHIFT) |
310 		    ((create_opt.mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK)
311 			<< NVME_NS_DATA_FLBAS_EXTENDED_SHIFT);
312 	else
313 		nsdata.flbas = create_opt.flbas;
314 	if (create_opt.dps == NONE)
315 		nsdata.dps = ((create_opt.pi & NVME_NS_DATA_DPS_MD_START_MASK)
316 		    << NVME_NS_DATA_DPS_MD_START_SHIFT) |
317 		    ((create_opt.pil & NVME_NS_DATA_DPS_PIT_MASK)
318 			<< NVME_NS_DATA_DPS_PIT_SHIFT);
319 	else
320 		nsdata.dps = create_opt.dps;
321 	nsdata.nmic = create_opt.nmic;
322 	nvme_namespace_data_swapbytes(&nsdata);
323 
324 	memset(&pt, 0, sizeof(pt));
325 	pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
326 
327 	pt.cmd.cdw10 = 0; /* create */
328 	pt.buf = &nsdata;
329 	pt.len = sizeof(struct nvme_namespace_data);
330 	pt.is_read = 0; /* passthrough writes data to ctrlr */
331 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
332 		errx(1, "ioctl request to %s failed: %d", argv[optind], result);
333 
334 	if (nvme_completion_is_error(&pt.cpl)) {
335 		errx(1, "namespace creation failed: %s",
336 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
337 		    NVME_STATUS_SC_MASK));
338 	}
339 	printf("namespace %d created\n", pt.cpl.cdw0);
340 	exit(0);
341 }
342 
343 static void
344 nsdelete(const struct cmd *f, int argc, char *argv[])
345 {
346 	struct nvme_pt_command	pt;
347 	struct nvme_controller_data cd;
348 	int	fd, result;
349 	char buf[2];
350 
351 	if (arg_parse(argc, argv, f))
352 		return;
353 	if (delete_opt.nsid == NONE) {
354 		fprintf(stderr,
355 		    "No NSID specified");
356 		arg_help(argc, argv, f);
357 	}
358 
359 	open_dev(delete_opt.dev, &fd, 1, 1);
360 	read_controller_data(fd, &cd);
361 
362 	/* Check that controller can execute this command. */
363 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
364 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
365 		errx(1, "controller does not support namespace management");
366 
367 	memset(&pt, 0, sizeof(pt));
368 	pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
369 	pt.cmd.cdw10 = 1; /* delete */
370 	pt.buf = buf;
371 	pt.len = sizeof(buf);
372 	pt.is_read = 1;
373 	pt.cmd.nsid = delete_opt.nsid;
374 
375 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
376 		errx(1, "ioctl request to %s failed: %d", delete_opt.dev, result);
377 
378 	if (nvme_completion_is_error(&pt.cpl)) {
379 		errx(1, "namespace deletion failed: %s",
380 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
381 		    NVME_STATUS_SC_MASK));
382 	}
383 	printf("namespace %d deleted\n", delete_opt.nsid);
384 	exit(0);
385 }
386 
387 /*
388  * Attach and Detach use Dword 10, and a controller list (section 4.9)
389  * This struct is 4096 bytes in size.
390  * 0h = attach
391  * 1h = detach
392  *
393  * Result values for both attach/detach:
394  *
395  * Completion 18h = Already attached
396  *            19h = NS is private and already attached to a controller
397  *            1Ah = Not attached, request could not be completed
398  *            1Ch = Controller list invalid.
399  *
400  * 0x2 Invalid Field can occur if ctrlrid d.n.e in system.
401  */
402 static void
403 nsattach(const struct cmd *f, int argc, char *argv[])
404 {
405 	struct nvme_pt_command	pt;
406 	struct nvme_controller_data cd;
407 	int	fd, result;
408 	uint16_t clist[2048];
409 
410 	if (arg_parse(argc, argv, f))
411 		return;
412 	if (attach_opt.nsid == NONE) {
413 		fprintf(stderr, "No valid NSID specified\n");
414 		arg_help(argc, argv, f);
415 	}
416 	open_dev(attach_opt.dev, &fd, 1, 1);
417 	read_controller_data(fd, &cd);
418 
419 	/* Check that controller can execute this command. */
420 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
421 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
422 		errx(1, "controller does not support namespace management");
423 
424 	if (attach_opt.ctrlrid == NONE) {
425 		/* Get full list of controllers to attach to. */
426 		memset(&pt, 0, sizeof(pt));
427 		pt.cmd.opc = NVME_OPC_IDENTIFY;
428 		pt.cmd.cdw10 = htole32(0x13);
429 		pt.buf = clist;
430 		pt.len = sizeof(clist);
431 		pt.is_read = 1;
432 		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
433 			err(1, "identify request failed");
434 		if (nvme_completion_is_error(&pt.cpl))
435 			errx(1, "identify request returned error");
436 	} else {
437 		/* By default attach to this controller. */
438 		if (attach_opt.ctrlrid == NONE - 1)
439 			attach_opt.ctrlrid = cd.ctrlr_id;
440 		memset(&clist, 0, sizeof(clist));
441 		clist[0] = htole16(1);
442 		clist[1] = htole16(attach_opt.ctrlrid);
443 	}
444 
445 	memset(&pt, 0, sizeof(pt));
446 	pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
447 	pt.cmd.cdw10 = 0; /* attach */
448 	pt.cmd.nsid = attach_opt.nsid;
449 	pt.buf = &clist;
450 	pt.len = sizeof(clist);
451 
452 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
453 		errx(1, "ioctl request to %s failed: %d", attach_opt.dev, result);
454 
455 	if (nvme_completion_is_error(&pt.cpl)) {
456 		errx(1, "namespace attach failed: %s",
457 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
458 		    NVME_STATUS_SC_MASK));
459 	}
460 	printf("namespace %d attached\n", attach_opt.nsid);
461 	exit(0);
462 }
463 
464 static void
465 nsdetach(const struct cmd *f, int argc, char *argv[])
466 {
467 	struct nvme_pt_command	pt;
468 	struct nvme_controller_data cd;
469 	int	fd, result;
470 	uint16_t clist[2048];
471 
472 	if (arg_parse(argc, argv, f))
473 		return;
474 	if (attach_opt.nsid == NONE) {
475 		fprintf(stderr, "No valid NSID specified\n");
476 		arg_help(argc, argv, f);
477 	}
478 	open_dev(attach_opt.dev, &fd, 1, 1);
479 	read_controller_data(fd, &cd);
480 
481 	/* Check that controller can execute this command. */
482 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
483 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
484 		errx(1, "controller does not support namespace management");
485 
486 	if (detach_opt.ctrlrid == NONE) {
487 		/* Get list of controllers this namespace attached to. */
488 		memset(&pt, 0, sizeof(pt));
489 		pt.cmd.opc = NVME_OPC_IDENTIFY;
490 		pt.cmd.nsid = htole32(detach_opt.nsid);
491 		pt.cmd.cdw10 = htole32(0x12);
492 		pt.buf = clist;
493 		pt.len = sizeof(clist);
494 		pt.is_read = 1;
495 		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
496 			err(1, "identify request failed");
497 		if (nvme_completion_is_error(&pt.cpl))
498 			errx(1, "identify request returned error");
499 		if (clist[0] == 0) {
500 			detach_opt.ctrlrid = cd.ctrlr_id;
501 			memset(&clist, 0, sizeof(clist));
502 			clist[0] = htole16(1);
503 			clist[1] = htole16(detach_opt.ctrlrid);
504 		}
505 	} else {
506 		/* By default detach from this controller. */
507 		if (detach_opt.ctrlrid == NONE - 1)
508 			detach_opt.ctrlrid = cd.ctrlr_id;
509 		memset(&clist, 0, sizeof(clist));
510 		clist[0] = htole16(1);
511 		clist[1] = htole16(detach_opt.ctrlrid);
512 	}
513 
514 	memset(&pt, 0, sizeof(pt));
515 	pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
516 	pt.cmd.cdw10 = 1; /* detach */
517 	pt.cmd.nsid = detach_opt.nsid;
518 	pt.buf = &clist;
519 	pt.len = sizeof(clist);
520 
521 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
522 		errx(1, "ioctl request to %s failed: %d", argv[optind], result);
523 
524 	if (nvme_completion_is_error(&pt.cpl)) {
525 		errx(1, "namespace detach failed: %s",
526 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
527 		    NVME_STATUS_SC_MASK));
528 	}
529 	printf("namespace %d detached\n", detach_opt.nsid);
530 	exit(0);
531 }
532 
533 static void
534 ns(const struct cmd *nf __unused, int argc, char *argv[])
535 {
536 
537 	cmd_dispatch(argc, argv, &ns_cmd);
538 }
539