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