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 * This implements all of the libnvme log discovery and log page execution 18 * functions. 19 * 20 * In NVMe 1.0 there were just three mandatory log pages. These are the classic 21 * Error, SMART, and firmware log pages. NVMe 1.1 added an optional log page for 22 * NVM devices. Specifically this is the Reservation Log page. This was 23 * indicated by the controller's ONCS field. Version 1.1 also introduced the Log 24 * Page Attributes (LPA) field which is how additional pages were indicated as 25 * being supported when not part of something like ONCS. 26 * 27 * Beginning in NVMe 1.2, many more log pages were added that were optional. In 28 * particular, the changed namespace list and command effects log. The former 29 * has support indicated via a bit in OAES (though this was not clarified until 30 * NVMe 1.3) while the latter is in the LPA field. NVMe 1.2 also added the 31 * ability for the Get Log Page to support larger amounts of data. The last 32 * major piece of 1.2 was the addition of fabrics related log pages. Those are 33 * not currently supported here. 34 * 35 * NVMe 1.3 and 1.4 continued the trend of adding log pages that are generally 36 * optional, but may be required given a specific set of features being enabled. 37 * 38 * The largest change for log pages is in NVMe 2.0. It added a specific means of 39 * indicating a command set for a given log page and also added the ability to 40 * query all the supported log pages. This has existed previously, but only 41 * through vendor specific means. 42 */ 43 44 #include <string.h> 45 #include <upanic.h> 46 #include <sys/sysmacros.h> 47 #include <sys/debug.h> 48 #include <unistd.h> 49 50 #include "libnvme_impl.h" 51 52 void 53 nvme_log_disc_free(nvme_log_disc_t *disc) 54 { 55 free(disc); 56 } 57 58 const char * 59 nvme_log_disc_name(const nvme_log_disc_t *disc) 60 { 61 return (disc->nld_short); 62 } 63 64 const char * 65 nvme_log_disc_desc(const nvme_log_disc_t *disc) 66 { 67 return (disc->nld_desc); 68 } 69 70 nvme_csi_t 71 nvme_log_disc_csi(const nvme_log_disc_t *disc) 72 { 73 return (disc->nld_csi); 74 } 75 76 uint32_t 77 nvme_log_disc_lid(const nvme_log_disc_t *disc) 78 { 79 return (disc->nld_lid); 80 } 81 82 nvme_log_disc_kind_t 83 nvme_log_disc_kind(const nvme_log_disc_t *disc) 84 { 85 return (disc->nld_kind); 86 } 87 88 nvme_log_disc_source_t 89 nvme_log_disc_sources(const nvme_log_disc_t *disc) 90 { 91 return (disc->nld_srcs); 92 } 93 94 nvme_log_disc_fields_t 95 nvme_log_disc_fields(const nvme_log_disc_t *disc) 96 { 97 return (disc->nld_fields); 98 } 99 100 nvme_log_disc_scope_t 101 nvme_log_disc_scopes(const nvme_log_disc_t *disc) 102 { 103 return (disc->nld_scope); 104 } 105 106 bool 107 nvme_log_disc_impl(const nvme_log_disc_t *disc) 108 { 109 return ((disc->nld_flags & NVME_LOG_DISC_F_IMPL) != 0); 110 } 111 112 nvme_log_size_kind_t 113 nvme_log_disc_size(const nvme_log_disc_t *disc, uint64_t *sizep) 114 { 115 *sizep = disc->nld_alloc_len; 116 return (disc->nld_size_kind); 117 } 118 119 /* 120 * For a variable length log page, presuming we've been given sufficient data 121 * actually determine the overall length that should now be used to get all 122 * data in the log. 123 */ 124 bool 125 nvme_log_disc_calc_size(const nvme_log_disc_t *disc, uint64_t *act, 126 const void *buf, size_t buflen) 127 { 128 if (disc->nld_var_func == NULL) { 129 *act = disc->nld_alloc_len; 130 } 131 132 return (disc->nld_var_func(act, buf, buflen)); 133 } 134 135 bool 136 nvme_log_disc_dup(nvme_ctrl_t *ctrl, const nvme_log_disc_t *src, 137 nvme_log_disc_t **discp) 138 { 139 nvme_log_disc_t *disc; 140 141 if (src == NULL) { 142 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 143 "encountered invalid nvme_log_disc_t pointer to duplicate: " 144 "%p", src)); 145 } 146 147 if (discp == NULL) { 148 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 149 "encountered invalid nvme_log_disc_t output pointer: %p", 150 discp)); 151 } 152 153 disc = calloc(1, sizeof (nvme_log_disc_t)); 154 if (disc == NULL) { 155 int e = errno; 156 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 157 "allocate memory for a new nvme_log_disc_t: %s", 158 strerror(e))); 159 } 160 161 (void) memcpy(disc, src, sizeof (nvme_log_disc_t)); 162 *discp = disc; 163 return (nvme_ctrl_success(ctrl)); 164 } 165 166 /* 167 * Log Page Discovery logic 168 */ 169 static bool 170 nvme_log_discover_validate(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes, 171 uint32_t flags) 172 { 173 const nvme_log_disc_scope_t valid_scopes = NVME_LOG_SCOPE_CTRL | 174 NVME_LOG_SCOPE_NVM | NVME_LOG_SCOPE_NS; 175 176 /* 177 * For now require an explicit scope. Perhaps 0 should be an alias for 178 * allow all. That means if something gets added no one has to update to 179 * get new things, but on the other hand that means they might see 180 * unexpected scopes. 181 */ 182 if (scopes == 0) { 183 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, "no log " 184 "scope specified (given 0), a scope must be requested")); 185 } 186 187 if ((scopes & ~valid_scopes) != 0) { 188 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 189 "encountered invalid scope for the nvme_log_disc_scope_t: " 190 "0x%x", scopes & ~valid_scopes)); 191 } 192 193 if (flags != 0) { 194 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 195 "encountered invalid log discovery flags: 0x%x", flags)); 196 } 197 198 return (true); 199 } 200 201 void 202 nvme_log_discover_fini(nvme_log_iter_t *iter) 203 { 204 free(iter); 205 } 206 207 static bool 208 nvme_log_discover_one(nvme_log_iter_t *iter, const nvme_log_page_info_t *info) 209 { 210 bool var; 211 nvme_log_disc_t *disc = &iter->nli_nld; 212 nvme_log_disc_scope_t scope; 213 nvme_valid_ctrl_data_t data; 214 215 data.vcd_vers = &iter->nli_ctrl->nc_vers; 216 data.vcd_id = &iter->nli_ctrl->nc_info; 217 218 /* 219 * Determine the scope of the log page so we can understand if the user 220 * cares about this or not. 221 */ 222 scope = nvme_log_page_info_scope(info, &data); 223 if ((iter->nli_scope & scope) == 0) { 224 return (false); 225 } 226 227 (void) memset(disc, 0, sizeof (nvme_log_disc_t)); 228 229 /* 230 * Now that we know that this applies, fill in the remaining information 231 * that we need. 232 */ 233 disc->nld_short = info->nlpi_short; 234 disc->nld_desc = info->nlpi_human; 235 disc->nld_lid = info->nlpi_lid; 236 disc->nld_csi = info->nlpi_csi; 237 disc->nld_kind = info->nlpi_kind; 238 disc->nld_srcs = info->nlpi_source; 239 disc->nld_scope = scope; 240 disc->nld_fields = info->nlpi_disc; 241 242 disc->nld_alloc_len = nvme_log_page_info_size(info, &data, &var); 243 if (disc->nld_alloc_len != 0) { 244 if (var) { 245 disc->nld_var_func = info->nlpi_var_func; 246 disc->nld_size_kind = NVME_LOG_SIZE_K_VAR; 247 } else { 248 disc->nld_size_kind = NVME_LOG_SIZE_K_FIXED; 249 } 250 } else { 251 disc->nld_size_kind = NVME_LOG_SIZE_K_UNKNOWN; 252 disc->nld_alloc_len = NVME_LOG_MAX_SIZE; 253 } 254 255 if (nvme_log_page_info_supported(info, &data)) { 256 disc->nld_flags |= NVME_LOG_DISC_F_IMPL; 257 } 258 259 return (true); 260 } 261 262 nvme_iter_t 263 nvme_log_discover_step(nvme_log_iter_t *iter, const nvme_log_disc_t **outp) 264 { 265 *outp = NULL; 266 nvme_ctrl_t *ctrl = iter->nli_ctrl; 267 268 if (iter->nli_std_done && iter->nli_vs_done) { 269 return (NVME_ITER_DONE); 270 } 271 272 /* 273 * We start by walking the list of spec pages and then check the device 274 * specific ones. In the NVMe 2.x era or when we have support for a 275 * vendor-specific method for getting supported log pages then we should 276 * prefer executing that and using that info to provide information 277 * where possible. 278 */ 279 if (!iter->nli_std_done) { 280 while (iter->nli_cur_idx < nvme_std_log_npages) { 281 const nvme_log_page_info_t *info = 282 &nvme_std_log_pages[iter->nli_cur_idx]; 283 iter->nli_cur_idx++; 284 if (nvme_log_discover_one(iter, info)) { 285 *outp = &iter->nli_nld; 286 return (NVME_ITER_VALID); 287 } 288 } 289 iter->nli_std_done = true; 290 iter->nli_cur_idx = 0; 291 } 292 293 if (ctrl->nc_vsd == NULL) { 294 iter->nli_vs_done = true; 295 return (NVME_ITER_DONE); 296 } 297 298 while (iter->nli_cur_idx < ctrl->nc_vsd->nvd_nlogs) { 299 const nvme_log_page_info_t *info = 300 &ctrl->nc_vsd->nvd_logs[iter->nli_cur_idx]; 301 iter->nli_cur_idx++; 302 if (nvme_log_discover_one(iter, info)) { 303 *outp = &iter->nli_nld; 304 return (NVME_ITER_VALID); 305 } 306 } 307 308 iter->nli_vs_done = true; 309 iter->nli_cur_idx = 0; 310 return (NVME_ITER_DONE); 311 } 312 313 bool 314 nvme_log_discover_init(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes, 315 uint32_t flags, nvme_log_iter_t **iterp) 316 { 317 nvme_log_iter_t *iter; 318 319 if (!nvme_log_discover_validate(ctrl, scopes, flags)) { 320 return (false); 321 } 322 323 if (iterp == NULL) { 324 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 325 "encountered invalid nvme_log_iter_t output pointer: %p", 326 iterp)); 327 } 328 329 iter = calloc(1, sizeof (nvme_log_iter_t)); 330 if (iter == NULL) { 331 int e = errno; 332 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 333 "allocate memory for a new nvme_log_iter_t: %s", 334 strerror(e))); 335 } 336 337 iter->nli_ctrl = ctrl; 338 iter->nli_scope = scopes; 339 340 *iterp = iter; 341 return (nvme_ctrl_success(ctrl)); 342 } 343 344 /* 345 * Walk all of the requested log pages that match and fill out the information 346 * for the discovery form. 347 */ 348 bool 349 nvme_log_discover(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes, 350 uint32_t flags, nvme_log_disc_f func, void *arg) 351 { 352 nvme_log_iter_t *iter; 353 nvme_iter_t ret; 354 const nvme_log_disc_t *disc; 355 356 if (func == NULL) { 357 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 358 "encountered invalid nvme_log_disc_f function pointer: %p", 359 func)); 360 } 361 362 if (!nvme_log_discover_init(ctrl, scopes, flags, &iter)) { 363 return (false); 364 } 365 366 while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) { 367 if (!func(ctrl, disc, arg)) 368 break; 369 } 370 371 nvme_log_discover_fini(iter); 372 if (ret == NVME_ITER_ERROR) { 373 return (false); 374 } 375 376 return (nvme_ctrl_success(ctrl)); 377 } 378 379 380 void 381 nvme_log_req_fini(nvme_log_req_t *req) 382 { 383 free(req); 384 } 385 386 /* 387 * This is the totally manual path that occurs. When this is used, we require 388 * that people specify a subset of the fields here, primarily just the actual 389 * log page, output, and CSI. We don't try to be clever here and use the 390 * discovery information to know what to set. That's reserved for creating this 391 * request based upon discovery information. 392 */ 393 bool 394 nvme_log_req_init(nvme_ctrl_t *ctrl, nvme_log_req_t **reqp) 395 { 396 nvme_log_req_t *req; 397 398 if (reqp == NULL) { 399 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 400 "encountered invalid nvme_log_req_t output pointer: %p", 401 reqp)); 402 } 403 404 req = calloc(1, sizeof (nvme_log_req_t)); 405 if (req == NULL) { 406 int e = errno; 407 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 408 "allocate memory for a new nvme_log_req_t: %s", 409 strerror(e))); 410 } 411 412 req->nlr_ctrl = ctrl; 413 for (size_t i = 0; i < nvme_log_nfields; i++) { 414 if (nvme_log_fields[i].nlfi_def_req) { 415 req->nlr_need |= 1 << i; 416 } 417 418 if (nvme_log_fields[i].nlfi_def_allow) { 419 req->nlr_allow |= 1 << i; 420 } 421 } 422 423 /* 424 * Because we don't know anything about this log request, indicate that 425 * if we're given the all namespaces nsid that's fine. We'll still 426 * check the controller version when this is set first. 427 */ 428 req->nlr_flags |= NVME_LOG_REQ_F_BCAST_NS_OK; 429 430 *reqp = req; 431 return (nvme_ctrl_success(ctrl)); 432 } 433 434 bool 435 nvme_log_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_log_disc_t *disc, 436 nvme_log_req_t **reqp) 437 { 438 nvme_log_req_t *req; 439 440 if (disc == NULL) { 441 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 442 "encountered invalid nvme_log_disc_t pointer: %p", disc)); 443 } 444 445 if (reqp == NULL) { 446 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 447 "encountered invalid nvme_log_req_t output pointer: %p", 448 reqp)); 449 } 450 451 if ((disc->nld_flags & NVME_LOG_DISC_F_IMPL) == 0) { 452 return (nvme_ctrl_error(ctrl, NVME_ERR_LOG_UNSUP_BY_DEV, 0, 453 "cannot create log request for log %s (CSI/LID 0x%x/0x%x) " 454 "because it is not supported by the device", 455 disc->nld_short, disc->nld_csi, disc->nld_lid)); 456 } 457 458 req = calloc(1, sizeof (nvme_log_req_t)); 459 if (req == NULL) { 460 int e = errno; 461 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 462 "allocate memory for a new nvme_log_req_t: %s", 463 strerror(e))); 464 } 465 466 req->nlr_ctrl = ctrl; 467 req->nlr_lid = disc->nld_lid; 468 req->nlr_csi = disc->nld_csi; 469 470 /* 471 * Setting the size is always required here, because this is how we 472 * track that the output pointer is actually set. We will always allow 473 * setting the offset though it's possible the controller won't support 474 * that. 475 */ 476 req->nlr_need = req->nlr_allow = 1 << NVME_LOG_REQ_FIELD_SIZE; 477 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_OFFSET; 478 479 /* 480 * Initialize our needed and allowed fields. Because we have the actual 481 * lid/csi from the above, we don't allow the user to overwrite them at 482 * all. For the LSP and LSI, right now these are all our nothing, but 483 * this may break. RAE is a bit special and discussed below. 484 */ 485 if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_LSP) != 0) { 486 req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_LSP; 487 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_LSP; 488 } 489 490 if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_LSI) != 0) { 491 req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_LSI; 492 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_LSI; 493 } 494 495 /* 496 * Because RAE wasn't added until NVMe 1.3, we can't do much with it 497 * before that. However, once it's here we definitely want to default to 498 * setting it by default so that way we can minimize the chance that 499 * we'll steal an alert that the kernel needs to read and acknowledge. 500 */ 501 if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_RAE) != 0 && 502 nvme_vers_ctrl_atleast(ctrl, 503 nvme_log_fields[NVME_LOG_REQ_FIELD_RAE].nlfi_vers)) { 504 req->nlr_flags |= NVME_LOG_REQ_F_RAE; 505 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_RAE; 506 } 507 508 /* 509 * Check the log page scope setting. If the log is said to be namespace 510 * scoped, then we'll allow the namespace to be specified. If it 511 * supports a different scope as well, then we'll default to the 512 * controller scope and this field is optional. Otherwise, it'll be 513 * required and it will be a mandatory field. 514 */ 515 if ((disc->nld_scope & NVME_LOG_SCOPE_NS) != 0) { 516 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_NSID; 517 if ((disc->nld_scope & ~NVME_LOG_SCOPE_NS) != 0) { 518 req->nlr_flags |= NVME_LOG_REQ_F_BCAST_NS_OK; 519 req->nlr_nsid = NVME_NSID_BCAST; 520 } else { 521 req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_NSID; 522 } 523 } 524 525 *reqp = req; 526 return (nvme_ctrl_success(ctrl)); 527 } 528 529 typedef struct { 530 bool nlia_found; 531 const char *nlia_name; 532 nvme_log_req_t *nlia_req; 533 nvme_log_disc_t **nlia_discp; 534 nvme_err_data_t nlia_err; 535 } nvme_log_init_arg_t; 536 537 static bool 538 nvme_log_req_init_by_name_cb(nvme_ctrl_t *ctrl, const nvme_log_disc_t *disc, 539 void *arg) 540 { 541 nvme_log_init_arg_t *init = arg; 542 543 if (strcmp(init->nlia_name, disc->nld_short) != 0) { 544 return (true); 545 } 546 547 init->nlia_found = true; 548 if (!nvme_log_req_init_by_disc(ctrl, disc, &init->nlia_req)) { 549 nvme_ctrl_err_save(ctrl, &init->nlia_err); 550 init->nlia_req = NULL; 551 } else if (init->nlia_discp != NULL) { 552 if (!nvme_log_disc_dup(ctrl, disc, init->nlia_discp)) { 553 nvme_ctrl_err_save(ctrl, &init->nlia_err); 554 nvme_log_req_fini(init->nlia_req); 555 init->nlia_req = NULL; 556 } 557 } 558 559 return (false); 560 } 561 562 bool 563 nvme_log_req_init_by_name(nvme_ctrl_t *ctrl, const char *name, uint32_t flags, 564 nvme_log_disc_t **discp, nvme_log_req_t **reqp) 565 { 566 nvme_log_init_arg_t init; 567 568 /* 569 * We consider discp an optional argument and therefore do not check it 570 * unlike name and reqp. 571 */ 572 if (reqp == NULL) { 573 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 574 "encountered invalid nvme_log_req_t output pointer: %p", 575 reqp)); 576 } 577 578 if (name == NULL) { 579 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 580 "encountered invalid pointer for log page name: %p", name)); 581 } 582 583 (void) memset(&init, 0, sizeof (init)); 584 init.nlia_name = name; 585 init.nlia_discp = discp; 586 587 if (!nvme_log_discover(ctrl, NVME_LOG_SCOPE_CTRL | 588 NVME_LOG_SCOPE_NVM | NVME_LOG_SCOPE_NS, flags, 589 nvme_log_req_init_by_name_cb, &init)) { 590 return (false); 591 } 592 593 if (!init.nlia_found) { 594 return (nvme_ctrl_error(ctrl, NVME_ERR_LOG_NAME_UNKNOWN, 0, 595 "failed to find log page with name %s", name)); 596 } 597 598 /* 599 * If we failed to create the request, but we did find it, then that 600 * means something went wrong and we can go ahead and already return an 601 * error. 602 */ 603 if (init.nlia_req == NULL) { 604 nvme_ctrl_err_set(ctrl, &init.nlia_err); 605 return (false); 606 } 607 608 *reqp = init.nlia_req; 609 return (nvme_ctrl_success(ctrl)); 610 } 611 612 static void 613 nvme_log_req_clear_need(nvme_log_req_t *req, nvme_log_req_field_t field) 614 { 615 req->nlr_need &= ~(1 << field); 616 } 617 618 static const nvme_field_check_t nvme_log_check_lid = { 619 nvme_log_fields, NVME_LOG_REQ_FIELD_LID, 620 NVME_ERR_LOG_LID_RANGE, 0, 0 621 }; 622 623 bool 624 nvme_log_req_set_lid(nvme_log_req_t *req, uint32_t lid) 625 { 626 if (!nvme_field_check_one(req->nlr_ctrl, lid, "get log page", 627 &nvme_log_check_lid, req->nlr_allow)) { 628 return (false); 629 } 630 631 req->nlr_lid = lid; 632 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LID); 633 return (nvme_ctrl_success(req->nlr_ctrl)); 634 } 635 636 static const nvme_field_check_t nvme_log_check_lsp = { 637 nvme_log_fields, NVME_LOG_REQ_FIELD_LSP, 638 NVME_ERR_LOG_LSP_RANGE, NVME_ERR_LOG_LSP_UNSUP, 639 NVME_ERR_LOG_LSP_UNUSE 640 }; 641 642 bool 643 nvme_log_req_set_lsp(nvme_log_req_t *req, uint32_t lsp) 644 { 645 if (!nvme_field_check_one(req->nlr_ctrl, lsp, "get log page", 646 &nvme_log_check_lsp, req->nlr_allow)) { 647 return (false); 648 } 649 650 req->nlr_lsp = lsp; 651 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSP); 652 return (nvme_ctrl_success(req->nlr_ctrl)); 653 } 654 655 static const nvme_field_check_t nvme_log_check_lsi = { 656 nvme_log_fields, NVME_LOG_REQ_FIELD_LSI, 657 NVME_ERR_LOG_LSI_RANGE, NVME_ERR_LOG_LSI_UNSUP, 658 NVME_ERR_LOG_LSI_UNUSE 659 }; 660 661 bool 662 nvme_log_req_set_lsi(nvme_log_req_t *req, uint32_t lsi) 663 { 664 if (!nvme_field_check_one(req->nlr_ctrl, lsi, "get log page", 665 &nvme_log_check_lsi, req->nlr_allow)) { 666 return (false); 667 } 668 669 req->nlr_lsi = lsi; 670 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSI); 671 return (nvme_ctrl_success(req->nlr_ctrl)); 672 } 673 674 static const nvme_field_check_t nvme_log_check_csi = { 675 nvme_log_fields, NVME_LOG_REQ_FIELD_CSI, 676 NVME_ERR_LOG_CSI_RANGE, NVME_ERR_LOG_CSI_UNSUP, 0 677 }; 678 679 bool 680 nvme_log_req_set_csi(nvme_log_req_t *req, nvme_csi_t csi) 681 { 682 if (!nvme_field_check_one(req->nlr_ctrl, csi, "get log page", 683 &nvme_log_check_csi, req->nlr_allow)) { 684 return (false); 685 } 686 687 req->nlr_csi = csi; 688 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_CSI); 689 return (nvme_ctrl_success(req->nlr_ctrl)); 690 } 691 692 static const nvme_field_check_t nvme_log_check_size = { 693 nvme_log_fields, NVME_LOG_REQ_FIELD_SIZE, 694 NVME_ERR_LOG_SIZE_RANGE, 0, 0 695 }; 696 697 bool 698 nvme_log_req_set_output(nvme_log_req_t *req, void *buf, size_t buflen) 699 { 700 if (buf == NULL) { 701 return (nvme_ctrl_error(req->nlr_ctrl, NVME_ERR_BAD_PTR, 0, 702 "log request output buffer cannot be NULL")); 703 } 704 705 if (!nvme_field_check_one(req->nlr_ctrl, buflen, "get log page", 706 &nvme_log_check_size, req->nlr_allow)) { 707 return (false); 708 } 709 710 req->nlr_output = buf; 711 req->nlr_output_len = buflen; 712 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_SIZE); 713 return (nvme_ctrl_success(req->nlr_ctrl)); 714 } 715 716 static const nvme_field_check_t nvme_log_check_offset = { 717 nvme_log_fields, NVME_LOG_REQ_FIELD_OFFSET, 718 NVME_ERR_LOG_OFFSET_RANGE, 0, 0 719 }; 720 721 bool 722 nvme_log_req_set_offset(nvme_log_req_t *req, uint64_t off) 723 { 724 if (!nvme_field_check_one(req->nlr_ctrl, off, "get log page", 725 &nvme_log_check_offset, req->nlr_allow)) { 726 return (false); 727 } 728 729 req->nlr_offset = off; 730 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_OFFSET); 731 return (nvme_ctrl_success(req->nlr_ctrl)); 732 } 733 734 static const nvme_field_check_t nvme_log_check_nsid = { 735 nvme_log_fields, NVME_LOG_REQ_FIELD_NSID, NVME_ERR_NS_RANGE, 0, 0 736 }; 737 738 bool 739 nvme_log_req_set_nsid(nvme_log_req_t *req, uint32_t nsid) 740 { 741 nvme_ctrl_t *ctrl = req->nlr_ctrl; 742 743 if (nsid == NVME_NSID_BCAST && 744 (req->nlr_flags & NVME_LOG_REQ_F_BCAST_NS_OK) == 0) { 745 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all " 746 "namespaces/controller nsid (0x%x) is not allowed for this " 747 "log page, valid namespaces are [0x%x, 0x%x]", nsid, 748 NVME_NSID_MIN, req->nlr_ctrl->nc_info.id_nn)); 749 } 750 751 if (!nvme_field_check_one(req->nlr_ctrl, nsid, "get log page", 752 &nvme_log_check_nsid, req->nlr_allow)) { 753 return (false); 754 } 755 756 req->nlr_nsid = nsid; 757 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_NSID); 758 return (nvme_ctrl_success(req->nlr_ctrl)); 759 } 760 761 static const nvme_field_check_t nvme_log_check_rae = { 762 nvme_log_fields, NVME_LOG_REQ_FIELD_RAE, 763 NVME_ERR_LOG_RAE_RANGE, NVME_ERR_LOG_RAE_UNSUP, 764 NVME_ERR_LOG_RAE_UNUSE 765 }; 766 767 bool 768 nvme_log_req_set_rae(nvme_log_req_t *req, bool rae) 769 { 770 if (!nvme_field_check_one(req->nlr_ctrl, rae, "get log page", 771 &nvme_log_check_rae, req->nlr_allow)) { 772 return (false); 773 } 774 775 if (rae) { 776 req->nlr_flags |= NVME_LOG_REQ_F_RAE; 777 } else { 778 req->nlr_flags &= ~NVME_LOG_REQ_F_RAE; 779 } 780 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_RAE); 781 return (nvme_ctrl_success(req->nlr_ctrl)); 782 } 783 784 bool 785 nvme_log_req_exec(nvme_log_req_t *req) 786 { 787 nvme_ctrl_t *ctrl = req->nlr_ctrl; 788 nvme_ioctl_get_logpage_t log; 789 790 if (req->nlr_need != 0) { 791 return (nvme_field_miss_err(ctrl, nvme_log_fields, 792 nvme_log_nfields, NVME_ERR_LOG_REQ_MISSING_FIELDS, 793 "get log page", req->nlr_need)); 794 } 795 796 (void) memset(&log, 0, sizeof (nvme_ioctl_get_logpage_t)); 797 log.nigl_common.nioc_nsid = req->nlr_nsid; 798 log.nigl_csi = req->nlr_csi; 799 log.nigl_lid = req->nlr_lid; 800 log.nigl_lsp = req->nlr_lsp; 801 log.nigl_lsi = req->nlr_lsi; 802 if ((req->nlr_flags & NVME_LOG_REQ_F_RAE) != 0) { 803 log.nigl_rae = 1; 804 } 805 log.nigl_len = req->nlr_output_len; 806 log.nigl_offset = req->nlr_offset; 807 log.nigl_data = (uintptr_t)req->nlr_output; 808 809 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_LOGPAGE, &log) != 0) { 810 int e = errno; 811 return (nvme_ioctl_syserror(ctrl, e, "get log page")); 812 } 813 814 if (log.nigl_common.nioc_drv_err != NVME_IOCTL_E_OK) { 815 return (nvme_ioctl_error(ctrl, &log.nigl_common, 816 "get log page")); 817 } 818 819 return (nvme_ctrl_success(ctrl)); 820 } 821