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