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 511 static void 512 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req, 513 nvme_get_feat_req_field_t field) 514 { 515 req->gfr_need &= ~(1 << field); 516 } 517 518 static const nvme_field_check_t nvme_get_feat_check_fid = { 519 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID, 520 NVME_ERR_FEAT_FID_RANGE, 0, 0 521 }; 522 523 bool 524 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid) 525 { 526 if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature", 527 &nvme_get_feat_check_fid, req->gfr_allow)) { 528 return (false); 529 } 530 531 req->gfr_fid = fid; 532 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID); 533 return (nvme_ctrl_success(req->gfr_ctrl)); 534 } 535 536 static const nvme_field_check_t nvme_get_feat_check_sel = { 537 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL, 538 NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0 539 }; 540 541 bool 542 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel) 543 { 544 if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature", 545 &nvme_get_feat_check_sel, req->gfr_allow)) { 546 return (false); 547 } 548 549 if (sel == NVME_FEATURE_SEL_SUPPORTED) { 550 return (nvme_ctrl_error(req->gfr_ctrl, 551 NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do " 552 "not support supported capabilities selector")); 553 } 554 555 req->gfr_sel = sel; 556 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL); 557 return (nvme_ctrl_success(req->gfr_ctrl)); 558 } 559 560 static const nvme_field_check_t nvme_get_feat_check_cdw11 = { 561 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11, 562 NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE 563 }; 564 565 bool 566 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11) 567 { 568 if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature", 569 &nvme_get_feat_check_cdw11, req->gfr_allow)) { 570 return (false); 571 } 572 573 req->gfr_cdw11 = cdw11; 574 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11); 575 return (nvme_ctrl_success(req->gfr_ctrl)); 576 } 577 578 static const nvme_field_check_t nvme_get_feat_check_nsid = { 579 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID, 580 NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE 581 }; 582 583 bool 584 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid) 585 { 586 nvme_ctrl_t *ctrl = req->gfr_ctrl; 587 588 /* 589 * Check the NSID first before we go into the generic validation code so 590 * we can get a better error message. 591 */ 592 if (nsid == NVME_NSID_BCAST && 593 (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) { 594 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all " 595 "namespaces/controller nsid (0x%x) is not allowed for this " 596 "feature, valid namespaces are [0x%x, 0x%x]", nsid, 597 NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn)); 598 } 599 600 if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature", 601 &nvme_get_feat_check_nsid, req->gfr_allow)) { 602 return (false); 603 } 604 605 req->gfr_nsid = nsid; 606 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID); 607 return (nvme_ctrl_success(ctrl)); 608 } 609 610 bool 611 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len) 612 { 613 if (buf == NULL) { 614 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0, 615 "get feature output buffer cannot be NULL")); 616 } 617 618 if (len == 0) { 619 return (nvme_ctrl_error(req->gfr_ctrl, 620 NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length " 621 "cannot be zero")); 622 } 623 624 /* 625 * Something to consider for the future here is that we know the fixed 626 * size data that we're expecting for the feature. It would be nice if 627 * we validated that we have that size now versus later. 628 */ 629 630 req->gfr_buf = buf; 631 req->gfr_len = len; 632 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR); 633 return (nvme_ctrl_success(req->gfr_ctrl)); 634 } 635 636 bool 637 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0) 638 { 639 if (cdw0 == NULL) { 640 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0, 641 "encountered invalid cdw0 output pointer: %p", cdw0)); 642 } 643 644 if (!req->gfr_results_valid) { 645 return (nvme_ctrl_error(req->gfr_ctrl, 646 NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not " 647 "currently valid and cannot be returned")); 648 } 649 650 *cdw0 = req->gfr_cdw0; 651 return (nvme_ctrl_success(req->gfr_ctrl)); 652 } 653 654 bool 655 nvme_get_feat_req_exec(nvme_get_feat_req_t *req) 656 { 657 nvme_ctrl_t *ctrl = req->gfr_ctrl; 658 nvme_ioctl_get_feature_t feat; 659 660 /* 661 * Because this has been called, we need to immediately invalidate our 662 * stored cdw0 results. We do this as a precaution regardless of whether 663 * or not it is valid. 664 */ 665 req->gfr_results_valid = false; 666 req->gfr_cdw0 = 0; 667 668 if (req->gfr_need != 0) { 669 return (nvme_field_miss_err(ctrl, nvme_get_feat_fields, 670 nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS, 671 "get feature", req->gfr_need)); 672 } 673 674 (void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t)); 675 feat.nigf_common.nioc_nsid = req->gfr_nsid; 676 feat.nigf_fid = req->gfr_fid; 677 feat.nigf_sel = req->gfr_sel; 678 feat.nigf_cdw11 = req->gfr_cdw11; 679 if (req->gfr_buf != NULL) { 680 feat.nigf_data = (uintptr_t)req->gfr_buf; 681 feat.nigf_len = req->gfr_len; 682 } 683 684 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) { 685 int e = errno; 686 return (nvme_ioctl_syserror(ctrl, e, "get feature")); 687 } 688 689 if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) { 690 return (nvme_ioctl_error(ctrl, &feat.nigf_common, 691 "get feature")); 692 } 693 694 req->gfr_results_valid = true; 695 req->gfr_cdw0 = feat.nigf_cdw0; 696 697 return (nvme_ctrl_success(ctrl)); 698 } 699