1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2025 Oxide Computer Company 14 */ 15 16 /* 17 * This implements support for Namespace Management and Namespace Attach 18 * commands. 19 */ 20 21 #include <string.h> 22 #include <unistd.h> 23 24 #include "libnvme_impl.h" 25 26 void 27 nvme_ns_attach_req_fini(nvme_ns_attach_req_t *req) 28 { 29 free(req); 30 } 31 32 static void 33 nvme_ns_attach_req_clear_need(nvme_ns_attach_req_t *req, 34 nvme_ns_attach_req_field_t field) 35 { 36 req->nar_need &= ~(1 << field); 37 } 38 39 bool 40 nvme_ns_attach_req_init_by_sel(nvme_ctrl_t *ctrl, uint32_t sel, 41 nvme_ns_attach_req_t **reqp) 42 { 43 nvme_ns_attach_req_t *req; 44 nvme_valid_ctrl_data_t ctrl_data; 45 46 if (reqp == NULL) { 47 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 48 "encountered invalid nvme_ns_attach_req_t output pointer: " 49 "%p", reqp)); 50 } 51 52 ctrl_data.vcd_vers = &ctrl->nc_vers; 53 ctrl_data.vcd_id = &ctrl->nc_info; 54 55 if (!nvme_nsmgmt_cmds_supported(&ctrl_data)) { 56 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_MGMT_UNSUP_BY_DEV, 0, 57 "controller does not support namespace management")); 58 } 59 60 /* 61 * See discussion in nvme_ns_create_req_init_by_csi() down below for 62 * rationale around the single error here. 63 */ 64 if (sel != NVME_NS_ATTACH_CTRL_ATTACH && 65 sel != NVME_NS_ATTACH_CTRL_DETACH) { 66 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_ATTACH_BAD_SEL, 0, 67 "the system (and possibly device) does not support " 68 "attaching namespaces with selector 0x%x", sel)); 69 } 70 71 req = calloc(1, sizeof (nvme_ns_attach_req_t)); 72 if (req == NULL) { 73 int e = errno; 74 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 75 "allocate memory for a new nvme_ns_attach_req_t: %s", 76 strerror(e))); 77 } 78 79 req->nar_ctrl = ctrl; 80 req->nar_sel = sel; 81 for (size_t i = 0; i < nvme_ns_attach_nfields; i++) { 82 if (nvme_ns_attach_fields[i].nlfi_def_req) { 83 req->nar_need |= 1 << i; 84 } 85 } 86 nvme_ns_attach_req_clear_need(req, NVME_NS_ATTACH_REQ_FIELD_SEL); 87 88 *reqp = req; 89 return (nvme_ctrl_success(ctrl)); 90 } 91 92 static const nvme_field_check_t nvme_ns_attach_check_nsid = { 93 nvme_ns_attach_fields, NVME_NS_ATTACH_REQ_FIELD_NSID, 94 NVME_ERR_NS_RANGE, 0, 0 95 }; 96 97 bool 98 nvme_ns_attach_req_set_nsid(nvme_ns_attach_req_t *req, uint32_t nsid) 99 { 100 if (!nvme_field_check_one(req->nar_ctrl, nsid, "namespace attach", 101 &nvme_ns_attach_check_nsid, 0)) { 102 return (false); 103 } 104 105 req->nar_nsid = nsid; 106 nvme_ns_attach_req_clear_need(req, NVME_NS_ATTACH_REQ_FIELD_NSID); 107 return (nvme_ctrl_success(req->nar_ctrl)); 108 } 109 110 /* 111 * Right now we don't support setting an explicit controller list in the kernel 112 * so this is a short-hand for saying just do it for my current controller 113 * without requiring us to actually set anything here, we just need to clear 114 * that this has been explicitly set that way the target is not implicit. 115 */ 116 bool 117 nvme_ns_attach_req_set_ctrlid_self(nvme_ns_attach_req_t *req) 118 { 119 nvme_ns_attach_req_clear_need(req, NVME_NS_ATTACH_REQ_FIELD_DPTR); 120 return (nvme_ctrl_success(req->nar_ctrl)); 121 } 122 123 bool 124 nvme_ns_attach_req_exec(nvme_ns_attach_req_t *req) 125 { 126 nvme_ctrl_t *ctrl = req->nar_ctrl; 127 nvme_ioctl_common_t common; 128 int code; 129 130 if (req->nar_need != 0) { 131 return (nvme_field_miss_err(ctrl, nvme_ns_attach_fields, 132 nvme_ns_attach_nfields, 133 NVME_ERR_NS_ATTACH_REQ_MISSING_FIELDS, "namespace attach", 134 req->nar_need)); 135 } 136 137 (void) memset(&common, 0, sizeof (common)); 138 common.nioc_nsid = req->nar_nsid; 139 140 code = req->nar_sel == NVME_NS_ATTACH_CTRL_ATTACH ? 141 NVME_IOC_CTRL_ATTACH : NVME_IOC_CTRL_DETACH; 142 if (ioctl(ctrl->nc_fd, code, &common) != 0) { 143 int e = errno; 144 return (nvme_ioctl_syserror(ctrl, e, "namespace attach")); 145 } 146 147 if (common.nioc_drv_err != NVME_IOCTL_E_OK) { 148 return (nvme_ioctl_error(ctrl, &common, "namespace attach")); 149 } 150 151 return (nvme_ctrl_success(ctrl)); 152 } 153 154 void 155 nvme_ns_delete_req_fini(nvme_ns_delete_req_t *req) 156 { 157 free(req); 158 } 159 160 bool 161 nvme_ns_delete_req_init(nvme_ctrl_t *ctrl, nvme_ns_delete_req_t **reqp) 162 { 163 nvme_ns_delete_req_t *req; 164 nvme_valid_ctrl_data_t ctrl_data; 165 166 if (reqp == NULL) { 167 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 168 "encountered invalid nvme_ns_delete_req_t output pointer: " 169 "%p", reqp)); 170 } 171 172 ctrl_data.vcd_vers = &ctrl->nc_vers; 173 ctrl_data.vcd_id = &ctrl->nc_info; 174 175 if (!nvme_nsmgmt_cmds_supported(&ctrl_data)) { 176 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_MGMT_UNSUP_BY_DEV, 0, 177 "controller does not support namespace management")); 178 } 179 180 req = calloc(1, sizeof (nvme_ns_delete_req_t)); 181 if (req == NULL) { 182 int e = errno; 183 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 184 "allocate memory for a new nvme_ns_delete_req_t: %s", 185 strerror(e))); 186 } 187 188 req->ndr_ctrl = ctrl; 189 for (size_t i = 0; i < nvme_ns_delete_nfields; i++) { 190 if (nvme_ns_delete_fields[i].nlfi_def_req) { 191 req->ndr_need |= 1 << i; 192 } 193 } 194 195 *reqp = req; 196 return (nvme_ctrl_success(ctrl)); 197 } 198 199 static void 200 nvme_ns_delete_req_clear_need(nvme_ns_delete_req_t *req, 201 nvme_ns_delete_req_field_t field) 202 { 203 req->ndr_need &= ~(1 << field); 204 } 205 206 static const nvme_field_check_t nvme_ns_delete_check_nsid = { 207 nvme_ns_delete_fields, NVME_NS_DELETE_REQ_FIELD_NSID, 208 NVME_ERR_NS_RANGE, 0, 0 209 }; 210 211 bool 212 nvme_ns_delete_req_set_nsid(nvme_ns_delete_req_t *req, uint32_t nsid) 213 { 214 if (!nvme_field_check_one(req->ndr_ctrl, nsid, "namespace delete", 215 &nvme_ns_delete_check_nsid, 0)) { 216 return (false); 217 } 218 219 req->ndr_nsid = nsid; 220 nvme_ns_delete_req_clear_need(req, NVME_NS_DELETE_REQ_FIELD_NSID); 221 return (nvme_ctrl_success(req->ndr_ctrl)); 222 } 223 224 bool 225 nvme_ns_delete_req_exec(nvme_ns_delete_req_t *req) 226 { 227 nvme_ctrl_t *ctrl = req->ndr_ctrl; 228 nvme_ioctl_common_t common; 229 230 if (req->ndr_need != 0) { 231 return (nvme_field_miss_err(ctrl, nvme_ns_delete_fields, 232 nvme_ns_delete_nfields, 233 NVME_ERR_NS_DELETE_REQ_MISSING_FIELDS, "namespace delete", 234 req->ndr_need)); 235 } 236 237 (void) memset(&common, 0, sizeof (common)); 238 common.nioc_nsid = req->ndr_nsid; 239 240 if (ioctl(ctrl->nc_fd, NVME_IOC_NS_DELETE, &common) != 0) { 241 int e = errno; 242 return (nvme_ioctl_syserror(ctrl, e, "namespace delete")); 243 } 244 245 if (common.nioc_drv_err != NVME_IOCTL_E_OK) { 246 return (nvme_ioctl_error(ctrl, &common, "namespace delete")); 247 } 248 249 return (nvme_ctrl_success(ctrl)); 250 } 251 252 void 253 nvme_ns_create_req_fini(nvme_ns_create_req_t *req) 254 { 255 free(req); 256 } 257 258 bool 259 nvme_ns_create_req_init_by_csi(nvme_ctrl_t *ctrl, nvme_csi_t csi, 260 nvme_ns_create_req_t **reqp) 261 { 262 nvme_ns_create_req_t *req; 263 nvme_valid_ctrl_data_t ctrl_data; 264 265 if (reqp == NULL) { 266 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 267 "encountered invalid nvme_ns_create_req_t output pointer: " 268 "%p", reqp)); 269 } 270 271 ctrl_data.vcd_vers = &ctrl->nc_vers; 272 ctrl_data.vcd_id = &ctrl->nc_info; 273 274 if (!nvme_nsmgmt_cmds_supported(&ctrl_data)) { 275 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_MGMT_UNSUP_BY_DEV, 0, 276 "controller does not support namespace management")); 277 } 278 279 /* 280 * The CSI determines what fields are supported and required. Not all 281 * CSIs support namespace creation and in addition, we only support the 282 * NVM CSI. The notion of CSIs was added in NVMe 2.0. There are several 283 * things that could go wrong here: 284 * 285 * - We could have an NVMe controller that is pre-2.0 and therefore 286 * anything other than the NVM CSI is invalid (it's implicit pre-2.0). 287 * - We could have a CSI that's just not defined by any version of the 288 * spec. 289 * - We could have a CSI that's defined by a spec that's newer than the 290 * device. 291 * - The CSI may not support namespace creation. The device may not even 292 * support the CSI! 293 * 294 * In addition, the kernel doesn't support anything other than the NVM 295 * CSI today. Rather than break these all apart, we give a generic error 296 * message about this. We can make this higher fidelity in the future. 297 */ 298 if (csi != NVME_CSI_NVM) { 299 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_CREATE_BAD_CSI, 0, 300 "the system (and possibly device) does not support " 301 "creating namespaces with CSI 0x%x", csi)); 302 } 303 304 req = calloc(1, sizeof (nvme_ns_create_req_t)); 305 if (req == NULL) { 306 int e = errno; 307 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 308 "allocate memory for a new nvme_ns_create_req_t: %s", 309 strerror(e))); 310 } 311 312 for (size_t i = 0; i < nvme_ns_create_fields_nvm_nreq; i++) { 313 req->ncr_need |= 1 << nvme_ns_create_fields_nvm_req[i]; 314 } 315 316 for (size_t i = 0; i < nvme_ns_create_fields_nvm_nallow; i++) { 317 req->ncr_allow |= 1 << nvme_ns_create_fields_nvm_allow[i]; 318 } 319 320 req->ncr_ctrl = ctrl; 321 req->ncr_csi = csi; 322 323 *reqp = req; 324 return (nvme_ctrl_success(ctrl)); 325 } 326 327 static void 328 nvme_ns_create_req_clear_need(nvme_ns_create_req_t *req, 329 nvme_ns_create_req_field_t field) 330 { 331 req->ncr_need &= ~(1 << field); 332 } 333 334 static const nvme_field_check_t nvme_ns_create_check_flbas = { 335 nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_FLBAS, 336 NVME_ERR_NS_CREATE_FLBAS_RANGE, 0, 0 337 }; 338 339 bool 340 nvme_ns_create_req_set_flbas(nvme_ns_create_req_t *req, uint32_t flbas) 341 { 342 if (!nvme_field_check_one(req->ncr_ctrl, flbas, "namespace create", 343 &nvme_ns_create_check_flbas, req->ncr_allow)) { 344 return (false); 345 } 346 347 req->ncr_flbas = flbas; 348 nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_FLBAS); 349 return (nvme_ctrl_success(req->ncr_ctrl)); 350 } 351 352 static const nvme_field_check_t nvme_ns_create_check_nsze = { 353 nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_NSZE, 354 NVME_ERR_NS_CREATE_NSZE_RANGE, 0, 0 355 }; 356 357 bool 358 nvme_ns_create_req_set_nsze(nvme_ns_create_req_t *req, uint64_t nsze) 359 { 360 if (!nvme_field_check_one(req->ncr_ctrl, nsze, "namespace create", 361 &nvme_ns_create_check_nsze, req->ncr_allow)) { 362 return (false); 363 } 364 365 req->ncr_nsze = nsze; 366 nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_NSZE); 367 return (nvme_ctrl_success(req->ncr_ctrl)); 368 } 369 370 static const nvme_field_check_t nvme_ns_create_check_ncap = { 371 nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_NCAP, 372 NVME_ERR_NS_CREATE_NCAP_RANGE, 0, 0 373 }; 374 375 bool 376 nvme_ns_create_req_set_ncap(nvme_ns_create_req_t *req, uint64_t ncap) 377 { 378 if (!nvme_field_check_one(req->ncr_ctrl, ncap, "namespace create", 379 &nvme_ns_create_check_ncap, req->ncr_allow)) { 380 return (false); 381 } 382 383 req->ncr_ncap = ncap; 384 nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_NCAP); 385 return (nvme_ctrl_success(req->ncr_ctrl)); 386 } 387 388 static const nvme_field_check_t nvme_ns_create_check_nmic = { 389 nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_NMIC, 390 NVME_ERR_NS_CREATE_NMIC_RANGE, 0, 0 391 }; 392 393 bool 394 nvme_ns_create_req_set_nmic(nvme_ns_create_req_t *req, uint32_t nmic) 395 { 396 if (!nvme_field_check_one(req->ncr_ctrl, nmic, "namespace create", 397 &nvme_ns_create_check_nmic, req->ncr_allow)) { 398 return (false); 399 } 400 401 req->ncr_nmic = nmic; 402 nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_NMIC); 403 return (nvme_ctrl_success(req->ncr_ctrl)); 404 } 405 406 bool 407 nvme_ns_create_req_get_nsid(nvme_ns_create_req_t *req, uint32_t *nsid) 408 { 409 if (nsid == NULL) { 410 return (nvme_ctrl_error(req->ncr_ctrl, NVME_ERR_BAD_PTR, 0, 411 "encountered invalid nsid output pointer: %p", nsid)); 412 } 413 414 if (!req->ncr_results_valid) { 415 return (nvme_ctrl_error(req->ncr_ctrl, 416 NVME_ERR_NS_CREATE_NO_RESULTS, 0, "namespace create " 417 "results are not currently valid and cannot be returned")); 418 } 419 420 *nsid = req->ncr_nsid; 421 return (nvme_ctrl_success(req->ncr_ctrl)); 422 } 423 424 bool 425 nvme_ns_create_req_exec(nvme_ns_create_req_t *req) 426 { 427 nvme_ctrl_t *ctrl = req->ncr_ctrl; 428 nvme_ioctl_ns_create_t create; 429 430 /* 431 * Immediately invalidate our results if someone calls this again. 432 */ 433 req->ncr_results_valid = false; 434 req->ncr_nsid = 0; 435 436 if (req->ncr_need != 0) { 437 return (nvme_field_miss_err(ctrl, nvme_ns_create_fields, 438 nvme_ns_create_nfields, 439 NVME_ERR_NS_CREATE_REQ_MISSING_FIELDS, "namespace create", 440 req->ncr_need)); 441 } 442 443 (void) memset(&create, 0, sizeof (create)); 444 create.nnc_nsze = req->ncr_nsze; 445 create.nnc_ncap = req->ncr_ncap; 446 create.nnc_csi = req->ncr_csi; 447 create.nnc_flbas = req->ncr_flbas; 448 create.nnc_nmic = req->ncr_nmic; 449 450 if (ioctl(ctrl->nc_fd, NVME_IOC_NS_CREATE, &create) != 0) { 451 int e = errno; 452 return (nvme_ioctl_syserror(ctrl, e, "namespace create")); 453 } 454 455 if (create.nnc_common.nioc_drv_err != NVME_IOCTL_E_OK) { 456 return (nvme_ioctl_error(ctrl, &create.nnc_common, 457 "namespace create")); 458 } 459 460 req->ncr_results_valid = true; 461 req->ncr_nsid = create.nnc_nsid; 462 return (nvme_ctrl_success(ctrl)); 463 } 464