xref: /freebsd/sbin/nvmecontrol/ns.c (revision d3d381b2b194b4d24853e92eecef55f262688d1a)
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 /* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */
45 
46 #define NSCREATE_USAGE							\
47 "       nvmecontrol ns create -s size [-c cap] [-f fmt] [-m mset] [-n nmic] [-p pi] [-l pil] nvmeN\n"
48 
49 #define NSDELETE_USAGE							\
50 "       nvmecontrol ns delete -n nsid nvmeN\n"
51 
52 #define NSATTACH_USAGE							\
53 "       nvmecontrol ns attach -n nsid [-c ctrlrid] nvmeN \n"
54 
55 #define NSDETACH_USAGE							\
56 "       nvmecontrol ns detach -n nsid [-c ctrlrid] nvmeN\n"
57 
58 void nscreate(int argc, char *argv[]);
59 void nsdelete(int argc, char *argv[]);
60 void nsattach(int argc, char *argv[]);
61 void nsdetach(int argc, char *argv[]);
62 
63 static struct nvme_function ns_funcs[] = {
64 	{"create",	nscreate, NSCREATE_USAGE},
65 	{"delete",	nsdelete, NSDELETE_USAGE},
66 	{"attach",	nsattach, NSATTACH_USAGE},
67 	{"detach",	nsdetach, NSDETACH_USAGE},
68 	{NULL,		NULL,		NULL},
69 };
70 
71 static void
72 nscreate_usage(void)
73 {
74 	fprintf(stderr, "usage:\n");
75 	fprintf(stderr, NSCREATE_USAGE);
76 	exit(1);
77 }
78 
79 static void
80 nsdelete_usage(void)
81 {
82 	fprintf(stderr, "usage:\n");
83 	fprintf(stderr, NSDELETE_USAGE);
84 	exit(1);
85 }
86 
87 static void
88 nsattach_usage(void)
89 {
90 	fprintf(stderr, "usage:\n");
91 	fprintf(stderr, NSATTACH_USAGE);
92 	exit(1);
93 }
94 
95 static void
96 nsdetach_usage(void)
97 {
98 	fprintf(stderr, "usage:\n");
99 	fprintf(stderr, NSDETACH_USAGE);
100 	exit(1);
101 }
102 
103 struct ns_result_str {
104 	uint16_t res;
105 	const char * str;
106 };
107 
108 static struct ns_result_str ns_result[] = {
109 	{ 0x2,  "Invalid Field"},
110 	{ 0xa,  "Invalid Format"},
111 	{ 0xb,  "Invalid Namespace or format"},
112 	{ 0x15, "Namespace insufficent capacity"},
113 	{ 0x16, "Namespace ID unavaliable"},
114 	{ 0x18, "Namespace already attached"},
115 	{ 0x19, "Namespace is private"},
116 	{ 0x1a, "Namespace is not attached"},
117 	{ 0x1b, "Thin provisioning not supported"},
118 	{ 0x1c, "Controller list invalid"},
119 	{ 0xFFFF, "Unknown"}
120 };
121 
122 static const char *
123 get_res_str(uint16_t res)
124 {
125 	struct ns_result_str *t = ns_result;
126 
127 	while (t->res != 0xFFFF) {
128 		if (t->res == res)
129 			return (t->str);
130 		t++;
131 	}
132 	return t->str;
133 }
134 
135 /*
136  * NS MGMT Command specific status values:
137  * 0xa = Invalid Format
138  * 0x15 = Namespace Insuffience capacity
139  * 0x16 = Namespace ID  unavailable (number namespaces exceeded)
140  * 0xb = Thin Provisioning Not supported
141  */
142 void
143 nscreate(int argc, char *argv[])
144 {
145 	struct nvme_pt_command	pt;
146 	struct nvme_controller_data cd;
147 	struct nvme_namespace_data nsdata;
148 	int64_t	nsze = -1, cap = -1;
149 	int	ch, fd, result, lbaf = 0, mset = 0, nmic = -1, pi = 0, pil = 0;
150 
151 	if (optind >= argc)
152 		nscreate_usage();
153 
154 	while ((ch = getopt(argc, argv, "s:c:f:m:n:p:l:")) != -1) {
155 		switch (ch) {
156 		case 's':
157 			nsze = strtol(optarg, (char **)NULL, 0);
158 			break;
159 		case 'c':
160 			cap = strtol(optarg, (char **)NULL, 0);
161 			break;
162 		case 'f':
163 			lbaf = strtol(optarg, (char **)NULL, 0);
164 			break;
165 		case 'm':
166 			mset = strtol(optarg, NULL, 0);
167 			break;
168 		case 'n':
169 			nmic = strtol(optarg, NULL, 0);
170 			break;
171 		case 'p':
172 			pi = strtol(optarg, NULL, 0);
173 			break;
174 		case 'l':
175 			pil = strtol(optarg, NULL, 0);
176 			break;
177 		default:
178 			nscreate_usage();
179 		}
180 	}
181 
182 	if (optind >= argc)
183 		nscreate_usage();
184 
185 	if (cap == -1)
186 		cap = nsze;
187 	if (nsze == -1 || cap == -1)
188 		nscreate_usage();
189 
190 	open_dev(argv[optind], &fd, 1, 1);
191 	read_controller_data(fd, &cd);
192 
193 	/* Check that controller can execute this command. */
194 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
195 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
196 		errx(1, "controller does not support namespace management");
197 
198 	/* Allow namespaces sharing if Multi-Path I/O is supported. */
199 	if (nmic == -1) {
200 		nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK <<
201 		     NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0;
202 	}
203 
204 	memset(&nsdata, 0, sizeof(nsdata));
205 	nsdata.nsze = (uint64_t)nsze;
206 	nsdata.ncap = (uint64_t)cap;
207 	nsdata.flbas = ((lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK)
208 	     << NVME_NS_DATA_FLBAS_FORMAT_SHIFT) |
209 	    ((mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK)
210 	     << NVME_NS_DATA_FLBAS_EXTENDED_SHIFT);
211 	nsdata.dps = ((pi & NVME_NS_DATA_DPS_MD_START_MASK)
212 	     << NVME_NS_DATA_DPS_MD_START_SHIFT) |
213 	    ((pil & NVME_NS_DATA_DPS_PIT_MASK)
214 	     << NVME_NS_DATA_DPS_PIT_SHIFT);
215 	nsdata.nmic = nmic;
216 	nvme_namespace_data_swapbytes(&nsdata);
217 
218 	memset(&pt, 0, sizeof(pt));
219 	pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
220 
221 	pt.cmd.cdw10 = 0; /* create */
222 	pt.buf = &nsdata;
223 	pt.len = sizeof(struct nvme_namespace_data);
224 	pt.is_read = 0; /* passthrough writes data to ctrlr */
225 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
226 		errx(1, "ioctl request to %s failed: %d", argv[optind], result);
227 
228 	if (nvme_completion_is_error(&pt.cpl)) {
229 		errx(1, "namespace creation failed: %s",
230 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
231 		    NVME_STATUS_SC_MASK));
232 	}
233 	printf("namespace %d created\n", pt.cpl.cdw0);
234 	exit(0);
235 }
236 
237 void
238 nsdelete(int argc, char *argv[])
239 {
240 	struct nvme_pt_command	pt;
241 	struct nvme_controller_data cd;
242 	int	ch, fd, result, nsid = -2;
243 	char buf[2];
244 
245 	if (optind >= argc)
246 		nsdelete_usage();
247 
248 	while ((ch = getopt(argc, argv, "n:")) != -1) {
249 		switch ((char)ch) {
250 		case  'n':
251 			nsid = strtol(optarg, (char **)NULL, 0);
252 			break;
253 		default:
254 			nsdelete_usage();
255 		}
256 	}
257 
258 	if (optind >= argc || nsid == -2)
259 		nsdelete_usage();
260 
261 	open_dev(argv[optind], &fd, 1, 1);
262 	read_controller_data(fd, &cd);
263 
264 	/* Check that controller can execute this command. */
265 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
266 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
267 		errx(1, "controller does not support namespace management");
268 
269 	memset(&pt, 0, sizeof(pt));
270 	pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
271 	pt.cmd.cdw10 = 1; /* delete */
272 	pt.buf = buf;
273 	pt.len = sizeof(buf);
274 	pt.is_read = 1;
275 	pt.cmd.nsid = (uint32_t)nsid;
276 
277 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
278 		errx(1, "ioctl request to %s failed: %d", argv[optind], result);
279 
280 	if (nvme_completion_is_error(&pt.cpl)) {
281 		errx(1, "namespace deletion failed: %s",
282 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
283 		    NVME_STATUS_SC_MASK));
284 	}
285 	printf("namespace %d deleted\n", nsid);
286 	exit(0);
287 }
288 
289 /*
290  * Attach and Detach use Dword 10, and a controller list (section 4.9)
291  * This struct is 4096 bytes in size.
292  * 0h = attach
293  * 1h = detach
294  *
295  * Result values for both attach/detach:
296  *
297  * Completion 18h = Already attached
298  *            19h = NS is private and already attached to a controller
299  *            1Ah = Not attached, request could not be completed
300  *            1Ch = Controller list invalid.
301  *
302  * 0x2 Invalid Field can occur if ctrlrid d.n.e in system.
303  */
304 void
305 nsattach(int argc, char *argv[])
306 {
307 	struct nvme_pt_command	pt;
308 	struct nvme_controller_data cd;
309 	int	ctrlrid = -2;
310 	int	fd, ch, result, nsid = -1;
311 	uint16_t clist[2048];
312 
313 	if (optind >= argc)
314 		nsattach_usage();
315 
316 	while ((ch = getopt(argc, argv, "n:c:")) != -1) {
317 		switch (ch) {
318 		case 'n':
319 			nsid = strtol(optarg, (char **)NULL, 0);
320 			break;
321 		case 'c':
322 			ctrlrid = strtol(optarg, (char **)NULL, 0);
323 			break;
324 		default:
325 			nsattach_usage();
326 		}
327 	}
328 
329 	if (optind >= argc)
330 		nsattach_usage();
331 
332 	if (nsid == -1 )
333 		nsattach_usage();
334 
335 	open_dev(argv[optind], &fd, 1, 1);
336 	read_controller_data(fd, &cd);
337 
338 	/* Check that controller can execute this command. */
339 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
340 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
341 		errx(1, "controller does not support namespace management");
342 
343 	if (ctrlrid == -1) {
344 		/* Get full list of controllers to attach to. */
345 		memset(&pt, 0, sizeof(pt));
346 		pt.cmd.opc = NVME_OPC_IDENTIFY;
347 		pt.cmd.cdw10 = htole32(0x13);
348 		pt.buf = clist;
349 		pt.len = sizeof(clist);
350 		pt.is_read = 1;
351 		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
352 			err(1, "identify request failed");
353 		if (nvme_completion_is_error(&pt.cpl))
354 			errx(1, "identify request returned error");
355 	} else {
356 		/* By default attach to this controller. */
357 		if (ctrlrid == -2)
358 			ctrlrid = cd.ctrlr_id;
359 		memset(&clist, 0, sizeof(clist));
360 		clist[0] = htole16(1);
361 		clist[1] = htole16(ctrlrid);
362 	}
363 
364 	memset(&pt, 0, sizeof(pt));
365 	pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
366 	pt.cmd.cdw10 = 0; /* attach */
367 	pt.cmd.nsid = (uint32_t)nsid;
368 	pt.buf = &clist;
369 	pt.len = sizeof(clist);
370 
371 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
372 		errx(1, "ioctl request to %s failed: %d", argv[optind], result);
373 
374 	if (nvme_completion_is_error(&pt.cpl)) {
375 		errx(1, "namespace attach failed: %s",
376 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
377 		    NVME_STATUS_SC_MASK));
378 	}
379 	printf("namespace %d attached\n", nsid);
380 	exit(0);
381 }
382 
383 void
384 nsdetach(int argc, char *argv[])
385 {
386 	struct nvme_pt_command	pt;
387 	struct nvme_controller_data cd;
388 	int	ctrlrid = -2;
389 	int	fd, ch, result, nsid = -1;
390 	uint16_t clist[2048];
391 
392 	if (optind >= argc)
393 		nsdetach_usage();
394 
395 	while ((ch = getopt(argc, argv, "n:c:")) != -1) {
396 		switch (ch) {
397 		case 'n':
398 			nsid = strtol(optarg, (char **)NULL, 0);
399 			break;
400 		case 'c':
401 			ctrlrid = strtol(optarg, (char **)NULL, 0);
402 			break;
403 		default:
404 			nsdetach_usage();
405 		}
406 	}
407 
408 	if (optind >= argc)
409 		nsdetach_usage();
410 
411 	if (nsid == -1)
412 		nsdetach_usage();
413 
414 	open_dev(argv[optind], &fd, 1, 1);
415 	read_controller_data(fd, &cd);
416 
417 	/* Check that controller can execute this command. */
418 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
419 	    NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
420 		errx(1, "controller does not support namespace management");
421 
422 	if (ctrlrid == -1) {
423 		/* Get list of controllers this namespace attached to. */
424 		memset(&pt, 0, sizeof(pt));
425 		pt.cmd.opc = NVME_OPC_IDENTIFY;
426 		pt.cmd.nsid = htole32(nsid);
427 		pt.cmd.cdw10 = htole32(0x12);
428 		pt.buf = clist;
429 		pt.len = sizeof(clist);
430 		pt.is_read = 1;
431 		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
432 			err(1, "identify request failed");
433 		if (nvme_completion_is_error(&pt.cpl))
434 			errx(1, "identify request returned error");
435 		if (clist[0] == 0) {
436 			ctrlrid = cd.ctrlr_id;
437 			memset(&clist, 0, sizeof(clist));
438 			clist[0] = htole16(1);
439 			clist[1] = htole16(ctrlrid);
440 		}
441 	} else {
442 		/* By default detach from this controller. */
443 		if (ctrlrid == -2)
444 			ctrlrid = cd.ctrlr_id;
445 		memset(&clist, 0, sizeof(clist));
446 		clist[0] = htole16(1);
447 		clist[1] = htole16(ctrlrid);
448 	}
449 
450 	memset(&pt, 0, sizeof(pt));
451 	pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
452 	pt.cmd.cdw10 = 1; /* detach */
453 	pt.cmd.nsid = (uint32_t)nsid;
454 	pt.buf = &clist;
455 	pt.len = sizeof(clist);
456 
457 	if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
458 		errx(1, "ioctl request to %s failed: %d", argv[optind], result);
459 
460 	if (nvme_completion_is_error(&pt.cpl)) {
461 		errx(1, "namespace detach failed: %s",
462 		    get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
463 		    NVME_STATUS_SC_MASK));
464 	}
465 	printf("namespace %d detached\n", nsid);
466 	exit(0);
467 }
468 
469 void
470 ns(int argc, char *argv[])
471 {
472 
473 	dispatch(argc, argv, ns_funcs);
474 }
475