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 2024 Oxide Computer Company 14 */ 15 16 /* 17 * Manage NVMe feature detection and feature queries. 18 */ 19 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 #include "libnvme_impl.h" 25 26 const char * 27 nvme_feat_disc_short(const nvme_feat_disc_t *disc) 28 { 29 return (disc->nfd_short); 30 } 31 32 const char * 33 nvme_feat_disc_spec(const nvme_feat_disc_t *disc) 34 { 35 return (disc->nfd_spec); 36 } 37 38 uint32_t 39 nvme_feat_disc_fid(const nvme_feat_disc_t *disc) 40 { 41 return (disc->nfd_fid); 42 } 43 44 nvme_feat_scope_t 45 nvme_feat_disc_scope(const nvme_feat_disc_t *disc) 46 { 47 return (disc->nfd_scope); 48 } 49 50 nvme_feat_kind_t 51 nvme_feat_disc_kind(const nvme_feat_disc_t *disc) 52 { 53 return (disc->nfd_kind); 54 } 55 56 nvme_feat_csi_t 57 nvme_feat_disc_csi(const nvme_feat_disc_t *disc) 58 { 59 return (disc->nfd_csi); 60 } 61 62 nvme_feat_flags_t 63 nvme_feat_disc_flags(const nvme_feat_disc_t *disc) 64 { 65 return (disc->nfd_flags); 66 } 67 68 nvme_get_feat_fields_t 69 nvme_feat_disc_fields_get(const nvme_feat_disc_t *disc) 70 { 71 return (disc->nfd_in_get); 72 } 73 74 nvme_set_feat_fields_t 75 nvme_feat_disc_fields_set(const nvme_feat_disc_t *disc) 76 { 77 return (disc->nfd_in_set); 78 } 79 80 nvme_feat_output_t 81 nvme_feat_disc_output_get(const nvme_feat_disc_t *disc) 82 { 83 return (disc->nfd_out_get); 84 } 85 86 nvme_feat_output_t 87 nvme_feat_disc_output_set(const nvme_feat_disc_t *disc) 88 { 89 return (disc->nfd_out_set); 90 } 91 92 uint64_t 93 nvme_feat_disc_data_size(const nvme_feat_disc_t *disc) 94 { 95 return (disc->nfd_len); 96 } 97 98 static bool 99 nvme_feat_discover_validate(nvme_ctrl_t *ctrl, nvme_feat_scope_t scopes, 100 uint32_t flags) 101 { 102 const nvme_feat_scope_t valid_scopes = NVME_FEAT_SCOPE_CTRL | 103 NVME_FEAT_SCOPE_NS; 104 105 /* 106 * See the note in nvme_log_discover_validate() on why we don't support 107 * a zeroed scope. 108 */ 109 if (scopes == 0) { 110 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, "no " 111 "feature scope specified (given 0), a scope must be " 112 "requested")); 113 } 114 115 if ((scopes & ~valid_scopes) != 0) { 116 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 117 "encountered invalid scope for the nvme_feat_disc_scope_t: " 118 "0x%x", scopes & ~valid_scopes)); 119 } 120 121 /* 122 * The flags are meant for future expansion here, hence the all zero 123 * requirement. 124 */ 125 if (flags != 0) { 126 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 127 "encountered invalid feature discovery flags: 0x%x", 128 flags)); 129 } 130 131 return (true); 132 } 133 134 nvme_feat_impl_t 135 nvme_feat_disc_impl(const nvme_feat_disc_t *disc) 136 { 137 return (disc->nfd_impl); 138 } 139 140 void 141 nvme_feat_disc_free(nvme_feat_disc_t *disc) 142 { 143 free(disc); 144 } 145 146 bool 147 nvme_feat_disc_dup(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *src, 148 nvme_feat_disc_t **discp) 149 { 150 nvme_feat_disc_t *disc; 151 152 if (src == NULL) { 153 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 154 "encountered invalid nvme_feat_disc_t pointer to " 155 "duplicate: %p", src)); 156 } 157 158 if (discp == NULL) { 159 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 160 "encountered invalid nvme_feat_disc_t output pointer: %p", 161 discp)); 162 } 163 164 disc = calloc(1, sizeof (nvme_feat_disc_t)); 165 if (disc == NULL) { 166 int e = errno; 167 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 168 "allocate memory for a new nvme_feat_disc_t: %s", 169 strerror(e))); 170 } 171 172 (void) memcpy(disc, src, sizeof (nvme_feat_disc_t)); 173 *discp = disc; 174 return (nvme_ctrl_success(ctrl)); 175 } 176 177 void 178 nvme_feat_discover_fini(nvme_feat_iter_t *iter) 179 { 180 free(iter); 181 } 182 183 static bool 184 nvme_feat_discover_one(nvme_feat_iter_t *iter, const nvme_feat_info_t *info) 185 { 186 nvme_feat_disc_t *disc = &iter->nfi_disc; 187 nvme_valid_ctrl_data_t data; 188 189 data.vcd_vers = &iter->nfi_ctrl->nc_vers; 190 data.vcd_id = &iter->nfi_ctrl->nc_info; 191 192 /* 193 * The user is not interested in this feature. Do not include it. 194 */ 195 if ((iter->nfi_scope & info->nfeat_scope) == 0) { 196 return (false); 197 } 198 199 (void) memset(disc, 0, sizeof (nvme_feat_disc_t)); 200 201 disc->nfd_short = info->nfeat_short; 202 disc->nfd_spec = info->nfeat_spec; 203 disc->nfd_fid = info->nfeat_fid; 204 disc->nfd_kind = info->nfeat_kind; 205 disc->nfd_scope = info->nfeat_scope; 206 disc->nfd_flags = info->nfeat_flags; 207 disc->nfd_csi = info->nfeat_csi; 208 disc->nfd_in_get = info->nfeat_in_get; 209 disc->nfd_in_set = info->nfeat_in_set; 210 disc->nfd_out_get = info->nfeat_out_get; 211 disc->nfd_out_set = info->nfeat_out_set; 212 disc->nfd_len = info->nfeat_len; 213 disc->nfd_impl = nvme_feat_supported(info, &data); 214 215 return (true); 216 } 217 218 nvme_iter_t 219 nvme_feat_discover_step(nvme_feat_iter_t *iter, const nvme_feat_disc_t **outp) 220 { 221 *outp = NULL; 222 223 if (iter->nfi_cur_idx == nvme_std_nfeats) { 224 return (NVME_ITER_DONE); 225 } 226 227 while (iter->nfi_cur_idx < nvme_std_nfeats) { 228 const nvme_feat_info_t *feat = 229 &nvme_std_feats[iter->nfi_cur_idx]; 230 iter->nfi_cur_idx++; 231 232 if (nvme_feat_discover_one(iter, feat)) { 233 *outp = &iter->nfi_disc; 234 return (NVME_ITER_VALID); 235 } 236 } 237 238 /* 239 * When we add support for vendor-specific features, then we will want 240 * to check NVME_LOG_DISC_F_NO_DB here and if it is not set, proceed to 241 * move past the standard features onto the vendor-specific features 242 * like we do in the log page work right now. 243 */ 244 ASSERT3U(iter->nfi_cur_idx, ==, nvme_std_nfeats); 245 return (NVME_ITER_DONE); 246 } 247 248 bool 249 nvme_feat_discover_init(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope, 250 uint32_t flags, nvme_feat_iter_t **iterp) 251 { 252 nvme_feat_iter_t *iter; 253 254 if (!nvme_feat_discover_validate(ctrl, scope, flags)) { 255 return (false); 256 } 257 258 if (iterp == NULL) { 259 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 260 "encountered invalid nvme_feat_iter_t output pointer: %p", 261 iterp)); 262 } 263 264 iter = calloc(1, sizeof (nvme_feat_iter_t)); 265 if (iter == NULL) { 266 int e = errno; 267 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 268 "allocate memory for a new nvme_feat_iter_t: %s", 269 strerror(e))); 270 } 271 272 iter->nfi_ctrl = ctrl; 273 iter->nfi_scope = scope; 274 275 *iterp = iter; 276 return (nvme_ctrl_success(ctrl)); 277 } 278 279 bool 280 nvme_feat_discover(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope, uint32_t flags, 281 nvme_feat_disc_f func, void *arg) 282 { 283 nvme_feat_iter_t *iter; 284 nvme_iter_t ret; 285 const nvme_feat_disc_t *disc; 286 287 288 if (func == NULL) { 289 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 290 "encountered invalid nvme_feat_disc_f function pointer: %p", 291 func)); 292 } 293 294 if (!nvme_feat_discover_init(ctrl, scope, flags, &iter)) { 295 return (false); 296 } 297 298 while ((ret = nvme_feat_discover_step(iter, &disc)) == 299 NVME_ITER_VALID) { 300 if (!func(ctrl, disc, arg)) 301 break; 302 } 303 304 nvme_feat_discover_fini(iter); 305 if (ret == NVME_ITER_ERROR) { 306 return (false); 307 } 308 309 return (nvme_ctrl_success(ctrl)); 310 } 311 312 void 313 nvme_get_feat_req_fini(nvme_get_feat_req_t *req) 314 { 315 free(req); 316 } 317 318 /* 319 * This instantiates a simple get features request that allows most fields to be 320 * specified. 321 */ 322 bool 323 nvme_get_feat_req_init(nvme_ctrl_t *ctrl, nvme_get_feat_req_t **reqp) 324 { 325 nvme_get_feat_req_t *req; 326 327 if (reqp == NULL) { 328 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 329 "encountered invalid nvme_get_feat_req_t output pointer: " 330 "%p", reqp)); 331 } 332 333 req = calloc(1, sizeof (nvme_get_feat_req_t)); 334 if (req == NULL) { 335 int e = errno; 336 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 337 "allocate memory for a new nvme_get_feat_req_t: %s", 338 strerror(e))); 339 } 340 341 req->gfr_ctrl = ctrl; 342 for (size_t i = 0; i < nvme_get_feat_nfields; i++) { 343 if (nvme_get_feat_fields[i].nlfi_def_req) { 344 req->gfr_need |= 1 << i; 345 } 346 347 if (nvme_get_feat_fields[i].nlfi_def_allow) { 348 req->gfr_allow |= 1 << i; 349 } 350 } 351 352 /* 353 * For a generic get feature request, we don't know if this is true or 354 * not. Even though none of the features the kernel supports at this 355 * moment support this, we still want to enable this for the future for 356 * when they do. The kernel will enforce this. 357 */ 358 req->gfr_flags = NVME_FEAT_F_GET_BCAST_NSID; 359 360 *reqp = req; 361 return (nvme_ctrl_success(ctrl)); 362 } 363 364 bool 365 nvme_get_feat_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *disc, 366 nvme_get_feat_req_t **reqp) 367 { 368 nvme_get_feat_req_t *req; 369 370 if (disc == NULL) { 371 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 372 "encountered invalid nvme_feat_disc_t pointer: %p", disc)); 373 } 374 375 if (reqp == NULL) { 376 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 377 "encountered invalid nvme_get_feat_req_t output pointer: " 378 "%p", reqp)); 379 } 380 381 if (disc->nfd_impl == NVME_FEAT_IMPL_UNSUPPORTED) { 382 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_UNSUP_BY_DEV, 0, 383 "cannot create get feature request for feature %s " 384 "(FID 0x%x) because it is not supported by the device", 385 disc->nfd_short, disc->nfd_fid)); 386 387 } 388 389 req = calloc(1, sizeof (nvme_get_feat_req_t)); 390 if (req == NULL) { 391 int e = errno; 392 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 393 "allocate memory for a new nvme_get_feat_req_t: %s", 394 strerror(e))); 395 } 396 397 req->gfr_ctrl = ctrl; 398 399 /* 400 * Unlike the more generic get feature initialization, we only allow 401 * items to be set if they're actually required. We do not require a 402 * selector to be set and will default to just getting the current 403 * value. Otherwise every other field that is settable is required. We 404 * don't let someone change the fid on us, because that would be quite 405 * confusing. 406 */ 407 req->gfr_fid = disc->nfd_fid; 408 req->gfr_flags = disc->nfd_flags; 409 req->gfr_targ_len = disc->nfd_len; 410 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_SEL; 411 412 if ((disc->nfd_in_get & NVME_GET_FEAT_F_CDW11) != 0) { 413 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11; 414 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11; 415 } 416 417 if ((disc->nfd_in_get & NVME_GET_FEAT_F_DATA) != 0) { 418 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR; 419 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR; 420 } 421 422 if ((disc->nfd_in_get & NVME_GET_FEAT_F_NSID) != 0) { 423 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID; 424 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID; 425 } 426 427 *reqp = req; 428 return (nvme_ctrl_success(ctrl)); 429 } 430 431 typedef struct { 432 bool ngfi_found; 433 const char *ngfi_name; 434 nvme_get_feat_req_t *ngfi_req; 435 nvme_feat_disc_t **ngfi_discp; 436 nvme_err_data_t ngfi_err; 437 } nvme_get_feat_init_arg_t; 438 439 static bool 440 nvme_get_feat_req_init_by_name_cb(nvme_ctrl_t *ctrl, 441 const nvme_feat_disc_t *disc, void *arg) 442 { 443 nvme_get_feat_init_arg_t *init = arg; 444 445 if (strcmp(init->ngfi_name, disc->nfd_short) != 0 && 446 strcmp(init->ngfi_name, disc->nfd_spec) != 0) { 447 return (true); 448 } 449 450 init->ngfi_found = true; 451 if (!nvme_get_feat_req_init_by_disc(ctrl, disc, &init->ngfi_req)) { 452 nvme_ctrl_err_save(ctrl, &init->ngfi_err); 453 init->ngfi_req = NULL; 454 } else if (init->ngfi_discp != NULL) { 455 if (!nvme_feat_disc_dup(ctrl, disc, init->ngfi_discp)) { 456 nvme_ctrl_err_save(ctrl, &init->ngfi_err); 457 nvme_get_feat_req_fini(init->ngfi_req); 458 init->ngfi_req = NULL; 459 } 460 } 461 462 return (false); 463 } 464 465 bool 466 nvme_get_feat_req_init_by_name(nvme_ctrl_t *ctrl, const char *name, 467 uint32_t df, nvme_feat_disc_t **discp, nvme_get_feat_req_t **reqp) 468 { 469 nvme_get_feat_init_arg_t init; 470 471 /* 472 * Note, we consider discp optional unlikely the name and reqp. The 473 * discover flags, df, will be validated by the discover functions we 474 * call. 475 */ 476 if (name == NULL) { 477 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 478 "encountered invalid pointer for log page name: %p", name)); 479 } 480 481 if (reqp == NULL) { 482 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 483 "encountered invalid nvme_get_feat_req_t output pointer: " 484 "%p", reqp)); 485 } 486 487 (void) memset(&init, 0, sizeof (init)); 488 init.ngfi_name = name; 489 init.ngfi_discp = discp; 490 491 if (!nvme_feat_discover(ctrl, NVME_FEAT_SCOPE_CTRL | NVME_FEAT_SCOPE_NS, 492 df, nvme_get_feat_req_init_by_name_cb, &init)) { 493 return (false); 494 } 495 496 if (!init.ngfi_found) { 497 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_NAME_UNKNOWN, 0, 498 "failed to find feature with name %s", name)); 499 } 500 501 if (init.ngfi_req == NULL) { 502 nvme_ctrl_err_set(ctrl, &init.ngfi_err); 503 return (false); 504 } 505 506 *reqp = init.ngfi_req; 507 return (nvme_ctrl_success(ctrl)); 508 } 509 510 static void 511 nvme_get_feat_req_set_need(nvme_get_feat_req_t *req, 512 nvme_get_feat_req_field_t field) 513 { 514 req->gfr_need |= 1 << field; 515 } 516 517 static void 518 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req, 519 nvme_get_feat_req_field_t field) 520 { 521 req->gfr_need &= ~(1 << field); 522 } 523 524 static const nvme_field_check_t nvme_get_feat_check_fid = { 525 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID, 526 NVME_ERR_FEAT_FID_RANGE, 0, 0 527 }; 528 529 bool 530 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid) 531 { 532 if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature", 533 &nvme_get_feat_check_fid, req->gfr_allow)) { 534 return (false); 535 } 536 537 req->gfr_fid = fid; 538 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID); 539 return (nvme_ctrl_success(req->gfr_ctrl)); 540 } 541 542 static const nvme_field_check_t nvme_get_feat_check_sel = { 543 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL, 544 NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0 545 }; 546 547 bool 548 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel) 549 { 550 if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature", 551 &nvme_get_feat_check_sel, req->gfr_allow)) { 552 return (false); 553 } 554 555 if (sel == NVME_FEATURE_SEL_SUPPORTED) { 556 return (nvme_ctrl_error(req->gfr_ctrl, 557 NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do " 558 "not support supported capabilities selector")); 559 } 560 561 req->gfr_sel = sel; 562 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL); 563 return (nvme_ctrl_success(req->gfr_ctrl)); 564 } 565 566 static const nvme_field_check_t nvme_get_feat_check_cdw11 = { 567 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11, 568 NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE 569 }; 570 571 bool 572 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11) 573 { 574 if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature", 575 &nvme_get_feat_check_cdw11, req->gfr_allow)) { 576 return (false); 577 } 578 579 req->gfr_cdw11 = cdw11; 580 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11); 581 return (nvme_ctrl_success(req->gfr_ctrl)); 582 } 583 584 static const nvme_field_check_t nvme_get_feat_check_nsid = { 585 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID, 586 NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE 587 }; 588 589 bool 590 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid) 591 { 592 nvme_ctrl_t *ctrl = req->gfr_ctrl; 593 594 /* 595 * Check the NSID first before we go into the generic validation code so 596 * we can get a better error message. 597 */ 598 if (nsid == NVME_NSID_BCAST && 599 (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) { 600 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all " 601 "namespaces/controller nsid (0x%x) is not allowed for this " 602 "feature, valid namespaces are [0x%x, 0x%x]", nsid, 603 NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn)); 604 } 605 606 if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature", 607 &nvme_get_feat_check_nsid, req->gfr_allow)) { 608 return (false); 609 } 610 611 req->gfr_nsid = nsid; 612 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID); 613 return (nvme_ctrl_success(ctrl)); 614 } 615 616 bool 617 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len) 618 { 619 if (buf == NULL) { 620 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0, 621 "get feature output buffer cannot be NULL")); 622 } 623 624 if (len == 0) { 625 return (nvme_ctrl_error(req->gfr_ctrl, 626 NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length " 627 "cannot be zero")); 628 } 629 630 /* 631 * Something to consider for the future here is that we know the fixed 632 * size data that we're expecting for the feature. It would be nice if 633 * we validated that we have that size now versus later. Related, 634 * because there is no field check logic for this, we must manually 635 * check that it is allowed and cannot use nvme_field_check_one(). 636 */ 637 if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) { 638 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE, 639 0, "field output (dptr) cannot be set in this get feature " 640 "request")); 641 } 642 643 req->gfr_buf = buf; 644 req->gfr_len = len; 645 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR); 646 return (nvme_ctrl_success(req->gfr_ctrl)); 647 } 648 649 bool 650 nvme_get_feat_req_clear_output(nvme_get_feat_req_t *req) 651 { 652 if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) { 653 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE, 654 0, "field output (dptr) cannot be cleared in this get " 655 "feature request")); 656 } 657 658 req->gfr_buf = NULL; 659 req->gfr_len = 0; 660 nvme_get_feat_req_set_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR); 661 return (nvme_ctrl_success(req->gfr_ctrl)); 662 } 663 664 bool 665 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0) 666 { 667 if (cdw0 == NULL) { 668 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0, 669 "encountered invalid cdw0 output pointer: %p", cdw0)); 670 } 671 672 if (!req->gfr_results_valid) { 673 return (nvme_ctrl_error(req->gfr_ctrl, 674 NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not " 675 "currently valid and cannot be returned")); 676 } 677 678 *cdw0 = req->gfr_cdw0; 679 return (nvme_ctrl_success(req->gfr_ctrl)); 680 } 681 682 bool 683 nvme_get_feat_req_exec(nvme_get_feat_req_t *req) 684 { 685 nvme_ctrl_t *ctrl = req->gfr_ctrl; 686 nvme_ioctl_get_feature_t feat; 687 688 /* 689 * Because this has been called, we need to immediately invalidate our 690 * stored cdw0 results. We do this as a precaution regardless of whether 691 * or not it is valid. 692 */ 693 req->gfr_results_valid = false; 694 req->gfr_cdw0 = 0; 695 696 if (req->gfr_need != 0) { 697 return (nvme_field_miss_err(ctrl, nvme_get_feat_fields, 698 nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS, 699 "get feature", req->gfr_need)); 700 } 701 702 (void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t)); 703 feat.nigf_common.nioc_nsid = req->gfr_nsid; 704 feat.nigf_fid = req->gfr_fid; 705 feat.nigf_sel = req->gfr_sel; 706 feat.nigf_cdw11 = req->gfr_cdw11; 707 if (req->gfr_buf != NULL) { 708 feat.nigf_data = (uintptr_t)req->gfr_buf; 709 feat.nigf_len = req->gfr_len; 710 } 711 712 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) { 713 int e = errno; 714 return (nvme_ioctl_syserror(ctrl, e, "get feature")); 715 } 716 717 if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) { 718 return (nvme_ioctl_error(ctrl, &feat.nigf_common, 719 "get feature")); 720 } 721 722 req->gfr_results_valid = true; 723 req->gfr_cdw0 = feat.nigf_cdw0; 724 725 return (nvme_ctrl_success(ctrl)); 726 } 727