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 2026 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 nvme_disc_impact_t 93 nvme_feat_disc_impact_set(const nvme_feat_disc_t *disc) 94 { 95 return (disc->nfd_impact); 96 } 97 98 uint64_t 99 nvme_feat_disc_data_size(const nvme_feat_disc_t *disc) 100 { 101 return (disc->nfd_len); 102 } 103 104 static bool 105 nvme_feat_discover_validate(nvme_ctrl_t *ctrl, nvme_feat_scope_t scopes, 106 uint32_t flags) 107 { 108 const nvme_feat_scope_t valid_scopes = NVME_FEAT_SCOPE_CTRL | 109 NVME_FEAT_SCOPE_NS | NVME_FEAT_SCOPE_NVM; 110 111 /* 112 * See the note in nvme_log_discover_validate() on why we don't support 113 * a zeroed scope. 114 */ 115 if (scopes == 0) { 116 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, "no " 117 "feature scope specified (given 0), a scope must be " 118 "requested")); 119 } 120 121 if ((scopes & ~valid_scopes) != 0) { 122 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 123 "encountered invalid scope for the nvme_feat_disc_scope_t: " 124 "0x%x", scopes & ~valid_scopes)); 125 } 126 127 /* 128 * The flags are meant for future expansion here, hence the all zero 129 * requirement. 130 */ 131 if (flags != 0) { 132 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 133 "encountered invalid feature discovery flags: 0x%x", 134 flags)); 135 } 136 137 return (true); 138 } 139 140 nvme_feat_impl_t 141 nvme_feat_disc_impl(const nvme_feat_disc_t *disc) 142 { 143 return (disc->nfd_impl); 144 } 145 146 void 147 nvme_feat_disc_free(nvme_feat_disc_t *disc) 148 { 149 free(disc); 150 } 151 152 bool 153 nvme_feat_disc_dup(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *src, 154 nvme_feat_disc_t **discp) 155 { 156 nvme_feat_disc_t *disc; 157 158 if (src == NULL) { 159 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 160 "encountered invalid nvme_feat_disc_t pointer to " 161 "duplicate: %p", src)); 162 } 163 164 if (discp == NULL) { 165 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 166 "encountered invalid nvme_feat_disc_t output pointer: %p", 167 discp)); 168 } 169 170 disc = calloc(1, sizeof (nvme_feat_disc_t)); 171 if (disc == NULL) { 172 int e = errno; 173 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 174 "allocate memory for a new nvme_feat_disc_t: %s", 175 strerror(e))); 176 } 177 178 (void) memcpy(disc, src, sizeof (nvme_feat_disc_t)); 179 *discp = disc; 180 return (nvme_ctrl_success(ctrl)); 181 } 182 183 void 184 nvme_feat_discover_fini(nvme_feat_iter_t *iter) 185 { 186 free(iter); 187 } 188 189 static bool 190 nvme_feat_discover_one(nvme_feat_iter_t *iter, const nvme_feat_info_t *info) 191 { 192 nvme_feat_disc_t *disc = &iter->nfi_disc; 193 nvme_valid_ctrl_data_t data; 194 195 data.vcd_vers = &iter->nfi_ctrl->nc_vers; 196 data.vcd_id = &iter->nfi_ctrl->nc_info; 197 198 /* 199 * The user is not interested in this feature. Do not include it. 200 */ 201 if ((iter->nfi_scope & info->nfeat_scope) == 0) { 202 return (false); 203 } 204 205 (void) memset(disc, 0, sizeof (nvme_feat_disc_t)); 206 207 disc->nfd_short = info->nfeat_short; 208 disc->nfd_spec = info->nfeat_spec; 209 disc->nfd_fid = info->nfeat_fid; 210 disc->nfd_kind = info->nfeat_kind; 211 disc->nfd_scope = info->nfeat_scope; 212 disc->nfd_flags = info->nfeat_flags; 213 disc->nfd_impact = info->nfeat_impact; 214 disc->nfd_csi = info->nfeat_csi; 215 disc->nfd_in_get = info->nfeat_in_get; 216 disc->nfd_in_set = info->nfeat_in_set; 217 disc->nfd_out_get = info->nfeat_out_get; 218 disc->nfd_out_set = info->nfeat_out_set; 219 disc->nfd_len = info->nfeat_len; 220 disc->nfd_impl = nvme_feat_supported(info, &data); 221 222 return (true); 223 } 224 225 nvme_iter_t 226 nvme_feat_discover_step(nvme_feat_iter_t *iter, const nvme_feat_disc_t **outp) 227 { 228 *outp = NULL; 229 nvme_ctrl_t *ctrl = iter->nfi_ctrl; 230 231 if (iter->nfi_std_done && iter->nfi_vs_done) { 232 return (NVME_ITER_DONE); 233 } 234 235 if (!iter->nfi_std_done) { 236 while (iter->nfi_cur_idx < nvme_std_nfeats) { 237 const nvme_feat_info_t *feat = 238 &nvme_std_feats[iter->nfi_cur_idx]; 239 iter->nfi_cur_idx++; 240 241 if (nvme_feat_discover_one(iter, feat)) { 242 *outp = &iter->nfi_disc; 243 return (NVME_ITER_VALID); 244 } 245 } 246 247 iter->nfi_std_done = true; 248 iter->nfi_cur_idx = 0; 249 } 250 251 if (ctrl->nc_vsd == NULL) { 252 iter->nfi_vs_done = true; 253 return (NVME_ITER_DONE); 254 } 255 256 while (iter->nfi_cur_idx < ctrl->nc_vsd->nvd_nfeats) { 257 const nvme_feat_info_t *feat = 258 ctrl->nc_vsd->nvd_feats[iter->nfi_cur_idx]; 259 iter->nfi_cur_idx++; 260 261 if (nvme_feat_discover_one(iter, feat)) { 262 *outp = &iter->nfi_disc; 263 return (NVME_ITER_VALID); 264 } 265 } 266 267 iter->nfi_vs_done = true; 268 iter->nfi_cur_idx = 0; 269 270 return (NVME_ITER_DONE); 271 } 272 273 bool 274 nvme_feat_discover_init(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope, 275 uint32_t flags, nvme_feat_iter_t **iterp) 276 { 277 nvme_feat_iter_t *iter; 278 279 if (!nvme_feat_discover_validate(ctrl, scope, flags)) { 280 return (false); 281 } 282 283 if (iterp == NULL) { 284 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 285 "encountered invalid nvme_feat_iter_t output pointer: %p", 286 iterp)); 287 } 288 289 iter = calloc(1, sizeof (nvme_feat_iter_t)); 290 if (iter == NULL) { 291 int e = errno; 292 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 293 "allocate memory for a new nvme_feat_iter_t: %s", 294 strerror(e))); 295 } 296 297 iter->nfi_ctrl = ctrl; 298 iter->nfi_scope = scope; 299 300 *iterp = iter; 301 return (nvme_ctrl_success(ctrl)); 302 } 303 304 bool 305 nvme_feat_discover(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope, uint32_t flags, 306 nvme_feat_disc_f func, void *arg) 307 { 308 nvme_feat_iter_t *iter; 309 nvme_iter_t ret; 310 const nvme_feat_disc_t *disc; 311 312 313 if (func == NULL) { 314 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 315 "encountered invalid nvme_feat_disc_f function pointer: %p", 316 func)); 317 } 318 319 if (!nvme_feat_discover_init(ctrl, scope, flags, &iter)) { 320 return (false); 321 } 322 323 while ((ret = nvme_feat_discover_step(iter, &disc)) == 324 NVME_ITER_VALID) { 325 if (!func(ctrl, disc, arg)) 326 break; 327 } 328 329 nvme_feat_discover_fini(iter); 330 if (ret == NVME_ITER_ERROR) { 331 return (false); 332 } 333 334 return (nvme_ctrl_success(ctrl)); 335 } 336 337 void 338 nvme_get_feat_req_fini(nvme_get_feat_req_t *req) 339 { 340 free(req); 341 } 342 343 /* 344 * This instantiates a simple get features request that allows most fields to be 345 * specified. 346 */ 347 bool 348 nvme_get_feat_req_init(nvme_ctrl_t *ctrl, nvme_get_feat_req_t **reqp) 349 { 350 nvme_get_feat_req_t *req; 351 352 if (reqp == NULL) { 353 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 354 "encountered invalid nvme_get_feat_req_t output pointer: " 355 "%p", reqp)); 356 } 357 358 req = calloc(1, sizeof (nvme_get_feat_req_t)); 359 if (req == NULL) { 360 int e = errno; 361 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 362 "allocate memory for a new nvme_get_feat_req_t: %s", 363 strerror(e))); 364 } 365 366 req->gfr_ctrl = ctrl; 367 for (size_t i = 0; i < nvme_get_feat_nfields; i++) { 368 if (nvme_get_feat_fields[i].nlfi_def_req) { 369 req->gfr_need |= 1 << i; 370 } 371 372 if (nvme_get_feat_fields[i].nlfi_def_allow) { 373 req->gfr_allow |= 1 << i; 374 } 375 } 376 377 /* 378 * For a generic get feature request, we don't know if this is true or 379 * not. Even though none of the features the kernel supports at this 380 * moment support this, we still want to enable this for the future for 381 * when they do. The kernel will enforce this. 382 */ 383 req->gfr_flags = NVME_FEAT_F_GET_BCAST_NSID; 384 385 *reqp = req; 386 return (nvme_ctrl_success(ctrl)); 387 } 388 389 bool 390 nvme_get_feat_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *disc, 391 nvme_get_feat_req_t **reqp) 392 { 393 nvme_get_feat_req_t *req; 394 395 if (disc == NULL) { 396 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 397 "encountered invalid nvme_feat_disc_t pointer: %p", disc)); 398 } 399 400 if (reqp == NULL) { 401 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 402 "encountered invalid nvme_get_feat_req_t output pointer: " 403 "%p", reqp)); 404 } 405 406 if (disc->nfd_impl == NVME_FEAT_IMPL_UNSUPPORTED) { 407 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_UNSUP_BY_DEV, 0, 408 "cannot create get feature request for feature %s " 409 "(FID 0x%x) because it is not supported by the device", 410 disc->nfd_short, disc->nfd_fid)); 411 412 } 413 414 req = calloc(1, sizeof (nvme_get_feat_req_t)); 415 if (req == NULL) { 416 int e = errno; 417 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 418 "allocate memory for a new nvme_get_feat_req_t: %s", 419 strerror(e))); 420 } 421 422 req->gfr_ctrl = ctrl; 423 424 /* 425 * Unlike the more generic get feature initialization, we only allow 426 * items to be set if they're actually required. We do not require a 427 * selector to be set and will default to just getting the current 428 * value. Otherwise every other field that is settable is required. We 429 * don't let someone change the fid on us, because that would be quite 430 * confusing. 431 */ 432 req->gfr_fid = disc->nfd_fid; 433 req->gfr_flags = disc->nfd_flags; 434 req->gfr_targ_len = disc->nfd_len; 435 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_SEL; 436 437 if ((disc->nfd_in_get & NVME_GET_FEAT_F_CDW11) != 0) { 438 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11; 439 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11; 440 } 441 442 if ((disc->nfd_in_get & NVME_GET_FEAT_F_DATA) != 0) { 443 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR; 444 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR; 445 } 446 447 if ((disc->nfd_in_get & NVME_GET_FEAT_F_NSID) != 0) { 448 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID; 449 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID; 450 } 451 452 *reqp = req; 453 return (nvme_ctrl_success(ctrl)); 454 } 455 456 typedef struct { 457 bool ngfi_found; 458 const char *ngfi_name; 459 nvme_get_feat_req_t *ngfi_req; 460 nvme_feat_disc_t **ngfi_discp; 461 nvme_err_data_t ngfi_err; 462 } nvme_get_feat_init_arg_t; 463 464 static bool 465 nvme_get_feat_req_init_by_name_cb(nvme_ctrl_t *ctrl, 466 const nvme_feat_disc_t *disc, void *arg) 467 { 468 nvme_get_feat_init_arg_t *init = arg; 469 470 if (strcmp(init->ngfi_name, disc->nfd_short) != 0 && 471 strcmp(init->ngfi_name, disc->nfd_spec) != 0) { 472 return (true); 473 } 474 475 init->ngfi_found = true; 476 if (!nvme_get_feat_req_init_by_disc(ctrl, disc, &init->ngfi_req)) { 477 nvme_ctrl_err_save(ctrl, &init->ngfi_err); 478 init->ngfi_req = NULL; 479 } else if (init->ngfi_discp != NULL) { 480 if (!nvme_feat_disc_dup(ctrl, disc, init->ngfi_discp)) { 481 nvme_ctrl_err_save(ctrl, &init->ngfi_err); 482 nvme_get_feat_req_fini(init->ngfi_req); 483 init->ngfi_req = NULL; 484 } 485 } 486 487 return (false); 488 } 489 490 bool 491 nvme_get_feat_req_init_by_name(nvme_ctrl_t *ctrl, const char *name, 492 uint32_t df, nvme_feat_disc_t **discp, nvme_get_feat_req_t **reqp) 493 { 494 nvme_get_feat_init_arg_t init; 495 496 /* 497 * Note, we consider discp optional unlike the name and reqp. The 498 * discover flags, df, will be validated by the discover functions we 499 * call. 500 */ 501 if (name == NULL) { 502 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 503 "encountered invalid pointer for log page name: %p", name)); 504 } 505 506 if (reqp == NULL) { 507 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 508 "encountered invalid nvme_get_feat_req_t output pointer: " 509 "%p", reqp)); 510 } 511 512 (void) memset(&init, 0, sizeof (init)); 513 init.ngfi_name = name; 514 init.ngfi_discp = discp; 515 516 if (!nvme_feat_discover(ctrl, NVME_FEAT_SCOPE_CTRL | 517 NVME_FEAT_SCOPE_NS | NVME_FEAT_SCOPE_NVM, df, 518 nvme_get_feat_req_init_by_name_cb, &init)) { 519 return (false); 520 } 521 522 if (!init.ngfi_found) { 523 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_NAME_UNKNOWN, 0, 524 "failed to find feature with name %s", name)); 525 } 526 527 if (init.ngfi_req == NULL) { 528 nvme_ctrl_err_set(ctrl, &init.ngfi_err); 529 return (false); 530 } 531 532 *reqp = init.ngfi_req; 533 return (nvme_ctrl_success(ctrl)); 534 } 535 536 static void 537 nvme_get_feat_req_set_need(nvme_get_feat_req_t *req, 538 nvme_get_feat_req_field_t field) 539 { 540 req->gfr_need |= 1 << field; 541 } 542 543 static void 544 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req, 545 nvme_get_feat_req_field_t field) 546 { 547 req->gfr_need &= ~(1 << field); 548 } 549 550 static const nvme_field_check_t nvme_get_feat_check_fid = { 551 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID, 552 NVME_ERR_FEAT_FID_RANGE, 0, 0 553 }; 554 555 bool 556 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid) 557 { 558 if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature", 559 &nvme_get_feat_check_fid, req->gfr_allow)) { 560 return (false); 561 } 562 563 req->gfr_fid = fid; 564 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID); 565 return (nvme_ctrl_success(req->gfr_ctrl)); 566 } 567 568 static const nvme_field_check_t nvme_get_feat_check_sel = { 569 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL, 570 NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0 571 }; 572 573 bool 574 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel) 575 { 576 if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature", 577 &nvme_get_feat_check_sel, req->gfr_allow)) { 578 return (false); 579 } 580 581 if (sel == NVME_FEATURE_SEL_SUPPORTED) { 582 return (nvme_ctrl_error(req->gfr_ctrl, 583 NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do " 584 "not support supported capabilities selector")); 585 } 586 587 req->gfr_sel = sel; 588 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL); 589 return (nvme_ctrl_success(req->gfr_ctrl)); 590 } 591 592 static const nvme_field_check_t nvme_get_feat_check_cdw11 = { 593 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11, 594 NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE 595 }; 596 597 bool 598 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11) 599 { 600 if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature", 601 &nvme_get_feat_check_cdw11, req->gfr_allow)) { 602 return (false); 603 } 604 605 req->gfr_cdw11 = cdw11; 606 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11); 607 return (nvme_ctrl_success(req->gfr_ctrl)); 608 } 609 610 static const nvme_field_check_t nvme_get_feat_check_nsid = { 611 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID, 612 NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE 613 }; 614 615 bool 616 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid) 617 { 618 nvme_ctrl_t *ctrl = req->gfr_ctrl; 619 620 /* 621 * Check the NSID first before we go into the generic validation code so 622 * we can get a better error message. 623 */ 624 if (nsid == NVME_NSID_BCAST && 625 (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) { 626 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all " 627 "namespaces/controller nsid (0x%x) is not allowed for this " 628 "feature, valid namespaces are [0x%x, 0x%x]", nsid, 629 NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn)); 630 } 631 632 if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature", 633 &nvme_get_feat_check_nsid, req->gfr_allow)) { 634 return (false); 635 } 636 637 req->gfr_nsid = nsid; 638 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID); 639 return (nvme_ctrl_success(ctrl)); 640 } 641 642 bool 643 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len) 644 { 645 if (buf == NULL) { 646 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0, 647 "get feature output buffer cannot be NULL")); 648 } 649 650 if (len == 0) { 651 return (nvme_ctrl_error(req->gfr_ctrl, 652 NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length " 653 "cannot be zero")); 654 } 655 656 if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) { 657 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE, 658 0, "field output (dptr) cannot be set in this get feature " 659 "request")); 660 } 661 662 if (req->gfr_targ_len != 0 && len < req->gfr_targ_len) { 663 return (nvme_ctrl_error(req->gfr_ctrl, 664 NVME_ERR_FEAT_DATA_RANGE, 0, "requested data length 0x%zx " 665 "is less than the feature's target size of 0x%" PRIx64, len, 666 req->gfr_targ_len)); 667 } 668 669 req->gfr_buf = buf; 670 req->gfr_len = len; 671 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR); 672 return (nvme_ctrl_success(req->gfr_ctrl)); 673 } 674 675 bool 676 nvme_get_feat_req_clear_output(nvme_get_feat_req_t *req) 677 { 678 if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) { 679 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE, 680 0, "field output (dptr) cannot be cleared in this get " 681 "feature request")); 682 } 683 684 req->gfr_buf = NULL; 685 req->gfr_len = 0; 686 nvme_get_feat_req_set_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR); 687 return (nvme_ctrl_success(req->gfr_ctrl)); 688 } 689 690 bool 691 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0) 692 { 693 if (cdw0 == NULL) { 694 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0, 695 "encountered invalid cdw0 output pointer: %p", cdw0)); 696 } 697 698 if (!req->gfr_results_valid) { 699 return (nvme_ctrl_error(req->gfr_ctrl, 700 NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not " 701 "currently valid and cannot be returned")); 702 } 703 704 *cdw0 = req->gfr_cdw0; 705 return (nvme_ctrl_success(req->gfr_ctrl)); 706 } 707 708 bool 709 nvme_get_feat_req_exec(nvme_get_feat_req_t *req) 710 { 711 nvme_ctrl_t *ctrl = req->gfr_ctrl; 712 nvme_ioctl_get_feature_t feat; 713 714 /* 715 * Because this has been called, we need to immediately invalidate our 716 * stored cdw0 results. We do this as a precaution regardless of whether 717 * or not it is valid. 718 */ 719 req->gfr_results_valid = false; 720 req->gfr_cdw0 = 0; 721 722 if (req->gfr_need != 0) { 723 return (nvme_field_miss_err(ctrl, nvme_get_feat_fields, 724 nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS, 725 "get feature", req->gfr_need)); 726 } 727 728 (void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t)); 729 feat.nigf_common.nioc_nsid = req->gfr_nsid; 730 feat.nigf_fid = req->gfr_fid; 731 feat.nigf_sel = req->gfr_sel; 732 feat.nigf_cdw11 = req->gfr_cdw11; 733 if (req->gfr_buf != NULL) { 734 feat.nigf_data = (uintptr_t)req->gfr_buf; 735 feat.nigf_len = req->gfr_len; 736 } 737 738 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) { 739 int e = errno; 740 return (nvme_ioctl_syserror(ctrl, e, "get feature")); 741 } 742 743 if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) { 744 return (nvme_ioctl_error(ctrl, &feat.nigf_common, 745 "get feature")); 746 } 747 748 req->gfr_results_valid = true; 749 req->gfr_cdw0 = feat.nigf_cdw0; 750 751 return (nvme_ctrl_success(ctrl)); 752 } 753 754 void 755 nvme_set_feat_req_fini(nvme_set_feat_req_t *req) 756 { 757 free(req); 758 } 759 760 bool 761 nvme_set_feat_req_init(nvme_ctrl_t *ctrl, nvme_set_feat_req_t **reqp) 762 { 763 nvme_set_feat_req_t *req; 764 765 if (reqp == NULL) { 766 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 767 "encountered invalid nvme_set_feat_req_t output pointer: " 768 "%p", reqp)); 769 } 770 771 req = calloc(1, sizeof (nvme_set_feat_req_t)); 772 if (req == NULL) { 773 int e = errno; 774 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 775 "allocate memory for a new nvme_set_feat_req_t: %s", 776 strerror(e))); 777 } 778 779 req->sfr_ctrl = ctrl; 780 for (size_t i = 0; i < nvme_set_feat_nfields; i++) { 781 if (nvme_set_feat_fields[i].nlfi_def_req) { 782 req->sfr_need |= 1 << i; 783 } 784 785 if (nvme_set_feat_fields[i].nlfi_def_allow) { 786 req->sfr_allow |= 1 << i; 787 } 788 } 789 790 /* 791 * We set this here for the same reason that we do in 792 * nvme_get_feat_req_init(). 793 */ 794 req->sfr_flags = NVME_FEAT_F_GET_BCAST_NSID; 795 796 *reqp = req; 797 return (nvme_ctrl_success(ctrl)); 798 } 799 800 bool nvme_set_feat_req_init_by_disc(nvme_ctrl_t *ctrl, 801 const nvme_feat_disc_t *disc, nvme_set_feat_req_t **reqp) 802 { 803 nvme_set_feat_req_t *req; 804 805 if (disc == NULL) { 806 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 807 "encountered invalid nvme_feat_disc_t pointer: %p", disc)); 808 } 809 810 if (reqp == NULL) { 811 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 812 "encountered invalid nvme_set_feat_req_t output pointer: " 813 "%p", reqp)); 814 } 815 816 if (disc->nfd_impl == NVME_FEAT_IMPL_UNSUPPORTED) { 817 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_UNSUP_BY_DEV, 0, 818 "cannot create set feature request for feature %s " 819 "(FID 0x%x) because it is not supported by the device", 820 disc->nfd_short, disc->nfd_fid)); 821 822 } 823 824 req = calloc(1, sizeof (nvme_set_feat_req_t)); 825 if (req == NULL) { 826 int e = errno; 827 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 828 "allocate memory for a new nvme_set_feat_req_t: %s", 829 strerror(e))); 830 } 831 832 req->sfr_ctrl = ctrl; 833 834 /* 835 * Using the feature discovery information set a number of fields from 836 * this including the feature id, the target size, the allowed flags, 837 * and the impact. We also use this to seed the set of allowed and 838 * needed fields. 839 */ 840 req->sfr_fid = disc->nfd_fid; 841 req->sfr_flags = disc->nfd_flags; 842 req->sfr_targ_len = disc->nfd_len; 843 req->sfr_impact = disc->nfd_impact; 844 req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_SAVE; 845 846 if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW11) != 0) { 847 req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW11; 848 req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW11; 849 } 850 851 if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW12) != 0) { 852 req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW12; 853 req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW12; 854 } 855 856 if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW13) != 0) { 857 req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW13; 858 req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW13; 859 } 860 861 if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW15) != 0) { 862 req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW15; 863 req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW15; 864 } 865 866 if ((disc->nfd_in_set & NVME_SET_FEAT_F_DATA) != 0) { 867 req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_DPTR; 868 req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_DPTR; 869 } 870 871 if ((disc->nfd_in_set & NVME_SET_FEAT_F_NSID) != 0) { 872 req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_NSID; 873 req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_NSID; 874 } 875 876 *reqp = req; 877 return (nvme_ctrl_success(ctrl)); 878 } 879 880 typedef struct { 881 bool nsfi_found; 882 const char *nsfi_name; 883 nvme_set_feat_req_t *nsfi_req; 884 nvme_feat_disc_t **nsfi_discp; 885 nvme_err_data_t nsfi_err; 886 } nvme_set_feat_init_arg_t; 887 888 static bool 889 nvme_set_feat_req_init_by_name_cb(nvme_ctrl_t *ctrl, 890 const nvme_feat_disc_t *disc, void *arg) 891 { 892 nvme_set_feat_init_arg_t *init = arg; 893 894 if (strcmp(init->nsfi_name, disc->nfd_short) != 0 && 895 strcmp(init->nsfi_name, disc->nfd_spec) != 0) { 896 return (true); 897 } 898 899 init->nsfi_found = true; 900 if (!nvme_set_feat_req_init_by_disc(ctrl, disc, &init->nsfi_req)) { 901 nvme_ctrl_err_save(ctrl, &init->nsfi_err); 902 init->nsfi_req = NULL; 903 } else if (init->nsfi_discp != NULL) { 904 if (!nvme_feat_disc_dup(ctrl, disc, init->nsfi_discp)) { 905 nvme_ctrl_err_save(ctrl, &init->nsfi_err); 906 nvme_set_feat_req_fini(init->nsfi_req); 907 init->nsfi_req = NULL; 908 } 909 } 910 911 return (false); 912 } 913 914 bool 915 nvme_set_feat_req_init_by_name(nvme_ctrl_t *ctrl, const char *name, 916 uint32_t df, nvme_feat_disc_t **discp, nvme_set_feat_req_t **reqp) 917 { 918 nvme_set_feat_init_arg_t init; 919 920 /* 921 * Note, we consider discp optional unlike the name and reqp. The 922 * discover flags, df, will be validated by the discover functions we 923 * call. 924 */ 925 if (name == NULL) { 926 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 927 "encountered invalid pointer for log page name: %p", name)); 928 } 929 930 if (reqp == NULL) { 931 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 932 "encountered invalid nvme_set_feat_req_t output pointer: " 933 "%p", reqp)); 934 } 935 936 (void) memset(&init, 0, sizeof (init)); 937 init.nsfi_name = name; 938 init.nsfi_discp = discp; 939 940 if (!nvme_feat_discover(ctrl, NVME_FEAT_SCOPE_CTRL | 941 NVME_FEAT_SCOPE_NS | NVME_FEAT_SCOPE_NVM, df, 942 nvme_set_feat_req_init_by_name_cb, &init)) { 943 return (false); 944 } 945 946 if (!init.nsfi_found) { 947 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_NAME_UNKNOWN, 0, 948 "failed to find feature with name %s", name)); 949 } 950 951 if (init.nsfi_req == NULL) { 952 nvme_ctrl_err_set(ctrl, &init.nsfi_err); 953 return (false); 954 } 955 956 *reqp = init.nsfi_req; 957 return (nvme_ctrl_success(ctrl)); 958 } 959 960 static void 961 nvme_set_feat_req_set_need(nvme_set_feat_req_t *req, 962 nvme_set_feat_req_field_t field) 963 { 964 req->sfr_need |= 1 << field; 965 } 966 967 static void 968 nvme_set_feat_req_clear_need(nvme_set_feat_req_t *req, 969 nvme_set_feat_req_field_t field) 970 { 971 req->sfr_need &= ~(1 << field); 972 } 973 974 static const nvme_field_check_t nvme_set_feat_check_fid = { 975 nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_FID, 976 NVME_ERR_FEAT_FID_RANGE, 0, 0 977 }; 978 979 bool 980 nvme_set_feat_req_set_fid(nvme_set_feat_req_t *req, uint32_t fid) 981 { 982 if (!nvme_field_check_one(req->sfr_ctrl, fid, "set feature", 983 &nvme_set_feat_check_fid, req->sfr_allow)) { 984 return (false); 985 } 986 987 req->sfr_fid = fid; 988 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_FID); 989 return (nvme_ctrl_success(req->sfr_ctrl)); 990 } 991 992 static const nvme_field_check_t nvme_set_feat_check_save = { 993 nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_SAVE, 994 NVME_ERR_FEAT_SAVE_RANGE, NVME_ERR_FEAT_SAVE_UNSUP, 0 995 }; 996 997 bool 998 nvme_set_feat_req_set_save(nvme_set_feat_req_t *req, bool save) 999 { 1000 if (!nvme_field_check_one(req->sfr_ctrl, save, "set feature", 1001 &nvme_set_feat_check_save, req->sfr_allow)) { 1002 return (false); 1003 } 1004 1005 req->sfr_save = save; 1006 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_SAVE); 1007 return (nvme_ctrl_success(req->sfr_ctrl)); 1008 } 1009 1010 static const nvme_field_check_t nvme_set_feat_check_nsid = { 1011 nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_NSID, 1012 NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE 1013 }; 1014 1015 bool 1016 nvme_set_feat_req_set_nsid(nvme_set_feat_req_t *req, uint32_t nsid) 1017 { 1018 nvme_ctrl_t *ctrl = req->sfr_ctrl; 1019 1020 /* 1021 * Check the NSID first before we go into the generic validation code so 1022 * we can get a better error message. 1023 */ 1024 if (nsid == NVME_NSID_BCAST && 1025 (req->sfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) { 1026 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all " 1027 "namespaces/controller nsid (0x%x) is not allowed for this " 1028 "feature, valid namespaces are [0x%x, 0x%x]", nsid, 1029 NVME_NSID_MIN, req->sfr_ctrl->nc_info.id_nn)); 1030 } 1031 1032 if (!nvme_field_check_one(req->sfr_ctrl, nsid, "set feature", 1033 &nvme_set_feat_check_nsid, req->sfr_allow)) { 1034 return (false); 1035 } 1036 1037 req->sfr_nsid = nsid; 1038 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_NSID); 1039 return (nvme_ctrl_success(ctrl)); 1040 } 1041 1042 static const nvme_field_check_t nvme_set_feat_check_cdw11 = { 1043 nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW11, 1044 NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE 1045 }; 1046 1047 bool 1048 nvme_set_feat_req_set_cdw11(nvme_set_feat_req_t *req, uint32_t cdw11) 1049 { 1050 if (!nvme_field_check_one(req->sfr_ctrl, cdw11, "set feature", 1051 &nvme_set_feat_check_cdw11, req->sfr_allow)) { 1052 return (false); 1053 } 1054 1055 req->sfr_cdw11 = cdw11; 1056 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW11); 1057 return (nvme_ctrl_success(req->sfr_ctrl)); 1058 } 1059 1060 static const nvme_field_check_t nvme_set_feat_check_cdw12 = { 1061 nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW12, 1062 NVME_ERR_FEAT_CDW12_RANGE, NVME_ERR_FEAT_CDW12_UNSUP, 1063 NVME_ERR_FEAT_CDW12_UNUSE 1064 }; 1065 1066 bool 1067 nvme_set_feat_req_set_cdw12(nvme_set_feat_req_t *req, uint32_t cdw12) 1068 { 1069 if (!nvme_field_check_one(req->sfr_ctrl, cdw12, "set feature", 1070 &nvme_set_feat_check_cdw12, req->sfr_allow)) { 1071 return (false); 1072 } 1073 1074 req->sfr_cdw12 = cdw12; 1075 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW12); 1076 return (nvme_ctrl_success(req->sfr_ctrl)); 1077 } 1078 1079 static const nvme_field_check_t nvme_set_feat_check_cdw13 = { 1080 nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW13, 1081 NVME_ERR_FEAT_CDW13_RANGE, NVME_ERR_FEAT_CDW13_UNSUP, 1082 NVME_ERR_FEAT_CDW13_UNUSE 1083 }; 1084 1085 bool 1086 nvme_set_feat_req_set_cdw13(nvme_set_feat_req_t *req, uint32_t cdw13) 1087 { 1088 if (!nvme_field_check_one(req->sfr_ctrl, cdw13, "set feature", 1089 &nvme_set_feat_check_cdw13, req->sfr_allow)) { 1090 return (false); 1091 } 1092 1093 req->sfr_cdw13 = cdw13; 1094 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW13); 1095 return (nvme_ctrl_success(req->sfr_ctrl)); 1096 } 1097 1098 static const nvme_field_check_t nvme_set_feat_check_cdw15 = { 1099 nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW15, 1100 NVME_ERR_FEAT_CDW15_RANGE, NVME_ERR_FEAT_CDW15_UNSUP, 1101 NVME_ERR_FEAT_CDW15_UNUSE 1102 }; 1103 1104 bool 1105 nvme_set_feat_req_set_cdw15(nvme_set_feat_req_t *req, uint32_t cdw15) 1106 { 1107 if (!nvme_field_check_one(req->sfr_ctrl, cdw15, "set feature", 1108 &nvme_set_feat_check_cdw15, req->sfr_allow)) { 1109 return (false); 1110 } 1111 1112 req->sfr_cdw15 = cdw15; 1113 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW15); 1114 return (nvme_ctrl_success(req->sfr_ctrl)); 1115 } 1116 1117 bool 1118 nvme_set_feat_req_set_input(nvme_set_feat_req_t *req, const void *buf, 1119 size_t len) 1120 { 1121 if (buf == NULL) { 1122 return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_BAD_PTR, 0, 1123 "set feature output buffer cannot be NULL")); 1124 } 1125 1126 if (len == 0) { 1127 return (nvme_ctrl_error(req->sfr_ctrl, 1128 NVME_ERR_FEAT_DATA_RANGE, 0, "set feature output length " 1129 "cannot be zero")); 1130 } 1131 1132 if ((req->sfr_allow & (1 << NVME_SET_FEAT_REQ_FIELD_DPTR)) == 0) { 1133 return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE, 1134 0, "field input (dptr) cannot be set in this set feature " 1135 "request")); 1136 } 1137 1138 if (req->sfr_targ_len != 0 && len < req->sfr_targ_len) { 1139 return (nvme_ctrl_error(req->sfr_ctrl, 1140 NVME_ERR_FEAT_DATA_RANGE, 0, "requested data length 0x%zx " 1141 "is less than the feature's target size of 0x%" PRIx64, len, 1142 req->sfr_targ_len)); 1143 } 1144 1145 req->sfr_buf = buf; 1146 req->sfr_len = len; 1147 nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_DPTR); 1148 return (nvme_ctrl_success(req->sfr_ctrl)); 1149 } 1150 1151 bool 1152 nvme_set_feat_req_clear_input(nvme_set_feat_req_t *req) 1153 { 1154 if ((req->sfr_allow & (1 << NVME_SET_FEAT_REQ_FIELD_DPTR)) == 0) { 1155 return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE, 1156 0, "field input (dptr) cannot be cleared in this set " 1157 "feature request")); 1158 } 1159 1160 req->sfr_buf = NULL; 1161 req->sfr_len = 0; 1162 nvme_set_feat_req_set_need(req, NVME_SET_FEAT_REQ_FIELD_DPTR); 1163 return (nvme_ctrl_success(req->sfr_ctrl)); 1164 } 1165 1166 /* 1167 * As impact values are a libnvme specific item, we don't use common parsing 1168 * routines. 1169 */ 1170 bool 1171 nvme_set_feat_req_set_impact(nvme_set_feat_req_t *req, 1172 nvme_disc_impact_t impact) 1173 { 1174 if (!nvme_field_valid_impact(req->sfr_ctrl, impact, 1175 NVME_ERR_FEAT_IMPACT_RANGE)) { 1176 return (false); 1177 } 1178 1179 req->sfr_impact = impact; 1180 return (nvme_ctrl_success(req->sfr_ctrl)); 1181 } 1182 1183 bool 1184 nvme_set_feat_req_get_cdw0(nvme_set_feat_req_t *req, uint32_t *cdw0) 1185 { 1186 if (cdw0 == NULL) { 1187 return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_BAD_PTR, 0, 1188 "encountered invalid cdw0 output pointer: %p", cdw0)); 1189 } 1190 1191 if (!req->sfr_results_valid) { 1192 return (nvme_ctrl_error(req->sfr_ctrl, 1193 NVME_ERR_FEAT_NO_RESULTS, 0, "set feature results are not " 1194 "currently valid and cannot be returned")); 1195 } 1196 1197 *cdw0 = req->sfr_cdw0; 1198 return (nvme_ctrl_success(req->sfr_ctrl)); 1199 } 1200 1201 bool 1202 nvme_set_feat_req_exec(nvme_set_feat_req_t *req) 1203 { 1204 nvme_ctrl_t *ctrl = req->sfr_ctrl; 1205 nvme_ioctl_set_feature_t feat; 1206 1207 /* 1208 * Immediately invalidate cdw0 so regardless of the results of this, 1209 * callers cannot be confused by whether or not a call accidentally 1210 * succeeded. 1211 */ 1212 req->sfr_results_valid = false; 1213 req->sfr_cdw0 = 0; 1214 1215 if (req->sfr_need != 0) { 1216 return (nvme_field_miss_err(ctrl, nvme_set_feat_fields, 1217 nvme_set_feat_nfields, NVME_ERR_SET_FEAT_REQ_MISSING_FIELDS, 1218 "set feature", req->sfr_need)); 1219 } 1220 1221 (void) memset(&feat, 0, sizeof (nvme_ioctl_set_feature_t)); 1222 feat.nisf_common.nioc_nsid = req->sfr_nsid; 1223 feat.nisf_fid = req->sfr_fid; 1224 feat.nisf_save = req->sfr_save; 1225 feat.nisf_cdw11 = req->sfr_cdw11; 1226 feat.nisf_cdw12 = req->sfr_cdw12; 1227 feat.nisf_cdw13 = req->sfr_cdw13; 1228 feat.nisf_cdw15 = req->sfr_cdw15; 1229 if (req->sfr_impact != 0) { 1230 feat.nisf_impact |= NVME_IMPACT_NS; 1231 } 1232 if (req->sfr_buf != NULL) { 1233 feat.nisf_data = (uintptr_t)req->sfr_buf; 1234 feat.nisf_len = req->sfr_len; 1235 } 1236 1237 if (ioctl(ctrl->nc_fd, NVME_IOC_SET_FEATURE, &feat) != 0) { 1238 int e = errno; 1239 return (nvme_ioctl_syserror(ctrl, e, "set feature")); 1240 } 1241 1242 if (feat.nisf_common.nioc_drv_err != NVME_IOCTL_E_OK) { 1243 return (nvme_ioctl_error(ctrl, &feat.nisf_common, 1244 "set feature")); 1245 } 1246 1247 req->sfr_results_valid = true; 1248 req->sfr_cdw0 = feat.nisf_cdw0; 1249 1250 return (nvme_ctrl_success(ctrl)); 1251 } 1252