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