]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/ns.c
Merge clang trunk r338150 (just before the 7.0.0 branch point), and
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / ns.c
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_fuse = NVME_CMD_SET_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_fuse = NVME_CMD_SET_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_fuse = NVME_CMD_SET_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_fuse = NVME_CMD_SET_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_fuse = NVME_CMD_SET_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_fuse = NVME_CMD_SET_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 }