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 * 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 const char *const * 71 nvme_log_disc_aliases(const nvme_log_disc_t *disc) 72 { 73 return (disc->nld_aliases); 74 } 75 76 size_t 77 nvme_log_disc_naliases(const nvme_log_disc_t *disc) 78 { 79 return (disc->nld_naliases); 80 } 81 82 nvme_csi_t 83 nvme_log_disc_csi(const nvme_log_disc_t *disc) 84 { 85 return (disc->nld_csi); 86 } 87 88 uint32_t 89 nvme_log_disc_lid(const nvme_log_disc_t *disc) 90 { 91 return (disc->nld_lid); 92 } 93 94 nvme_log_disc_kind_t 95 nvme_log_disc_kind(const nvme_log_disc_t *disc) 96 { 97 return (disc->nld_kind); 98 } 99 100 nvme_log_disc_source_t 101 nvme_log_disc_sources(const nvme_log_disc_t *disc) 102 { 103 return (disc->nld_srcs); 104 } 105 106 nvme_log_disc_fields_t 107 nvme_log_disc_fields(const nvme_log_disc_t *disc) 108 { 109 return (disc->nld_fields); 110 } 111 112 nvme_log_disc_scope_t 113 nvme_log_disc_scopes(const nvme_log_disc_t *disc) 114 { 115 return (disc->nld_scope); 116 } 117 118 bool 119 nvme_log_disc_impl(const nvme_log_disc_t *disc) 120 { 121 return ((disc->nld_flags & NVME_LOG_DISC_F_IMPL) != 0); 122 } 123 124 nvme_log_size_kind_t 125 nvme_log_disc_size(const nvme_log_disc_t *disc, uint64_t *sizep) 126 { 127 *sizep = disc->nld_alloc_len; 128 return (disc->nld_size_kind); 129 } 130 131 /* 132 * For a variable length log page, presuming we've been given sufficient data 133 * actually determine the overall length that should now be used to get all 134 * data in the log. 135 */ 136 bool 137 nvme_log_disc_calc_size(const nvme_log_disc_t *disc, uint64_t *act, 138 const void *buf, size_t buflen) 139 { 140 if (disc->nld_var_func == NULL) { 141 *act = disc->nld_alloc_len; 142 } else if (!disc->nld_var_func(act, buf, buflen)) { 143 return (false); 144 } 145 146 uint64_t orig = *act; 147 148 /* 149 * See nvme_log_page_info_size() in common/nvme/nvme_log.c for 150 * more context. Effectively NVMe log pages are required to be 151 * 4-byte aligned and we choose to round up. 152 */ 153 *act = P2ROUNDUP(orig, NVME_DWORD_SIZE); 154 VERIFY3U(*act, >=, orig); 155 return (true); 156 } 157 158 bool 159 nvme_log_disc_dup(nvme_ctrl_t *ctrl, const nvme_log_disc_t *src, 160 nvme_log_disc_t **discp) 161 { 162 nvme_log_disc_t *disc; 163 164 if (src == NULL) { 165 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 166 "encountered invalid nvme_log_disc_t pointer to duplicate: " 167 "%p", src)); 168 } 169 170 if (discp == NULL) { 171 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 172 "encountered invalid nvme_log_disc_t output pointer: %p", 173 discp)); 174 } 175 176 disc = calloc(1, sizeof (nvme_log_disc_t)); 177 if (disc == NULL) { 178 int e = errno; 179 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 180 "allocate memory for a new nvme_log_disc_t: %s", 181 strerror(e))); 182 } 183 184 (void) memcpy(disc, src, sizeof (nvme_log_disc_t)); 185 *discp = disc; 186 return (nvme_ctrl_success(ctrl)); 187 } 188 189 /* 190 * Log Page Discovery logic 191 */ 192 static bool 193 nvme_log_discover_validate(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes, 194 uint32_t flags) 195 { 196 const nvme_log_disc_scope_t valid_scopes = NVME_LOG_SCOPE_CTRL | 197 NVME_LOG_SCOPE_NVM | NVME_LOG_SCOPE_NS; 198 199 /* 200 * For now require an explicit scope. Perhaps 0 should be an alias for 201 * allow all. That means if something gets added no one has to update to 202 * get new things, but on the other hand that means they might see 203 * unexpected scopes. 204 */ 205 if (scopes == 0) { 206 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, "no log " 207 "scope specified (given 0), a scope must be requested")); 208 } 209 210 if ((scopes & ~valid_scopes) != 0) { 211 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 212 "encountered invalid scope for the nvme_log_disc_scope_t: " 213 "0x%x", scopes & ~valid_scopes)); 214 } 215 216 if (flags != 0) { 217 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, 218 "encountered invalid log discovery flags: 0x%x", flags)); 219 } 220 221 return (true); 222 } 223 224 /* 225 * The NVMe 2.0 specification adds a mandatory log page that describes which log 226 * pages are actually implemented, though not as much information as we use in 227 * discovery. We will attempt once per controller handle to get this log page 228 * and use it to augment the supported information in the discovery process. 229 * This log page is the first entry of the nvme_std_log_pages array. 230 */ 231 static void 232 nvme_log_discover_fetch_sup_logs(nvme_ctrl_t *ctrl) 233 { 234 const nvme_log_page_info_t *sup_info = &nvme_std_log_pages[0]; 235 nvme_suplog_log_t *sup = NULL; 236 nvme_log_req_t *req = NULL; 237 nvme_valid_ctrl_data_t data; 238 239 VERIFY3U(sup_info->nlpi_lid, ==, NVME_LOGPAGE_SUP); 240 241 /* 242 * Mark the data in the nvme_ctrl_t as valid at this point. If this 243 * fails, we swallow this error and just will have slightly less 244 * information available on certain log pages. In general, this only 245 * impacts the detection of vendor defined log pages where our built-in 246 * information is not as accurate due to the lack of firmware 247 * information. 248 */ 249 ctrl->nc_sup_logs = NULL; 250 ctrl->nc_flags |= NVME_CTRL_F_SUP_LOGS_VALID; 251 252 data.vcd_vers = &ctrl->nc_vers; 253 data.vcd_id = &ctrl->nc_info; 254 255 if (!nvme_log_page_info_supported(sup_info, &data)) { 256 return; 257 } 258 259 sup = calloc(1, sizeof (nvme_suplog_log_t)); 260 if (sup == NULL) { 261 int e = errno; 262 (void) nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 263 "allocate memory for internal log page info: %s", 264 strerror(e)); 265 goto err; 266 } 267 268 if (!nvme_log_req_init(ctrl, &req)) { 269 goto err; 270 } 271 272 if (!nvme_log_req_set_lid(req, sup_info->nlpi_lid) || 273 !nvme_log_req_set_csi(req, sup_info->nlpi_csi) || 274 !nvme_log_req_set_output(req, sup, sizeof (nvme_suplog_log_t)) || 275 !nvme_log_req_exec(req)) { 276 goto err; 277 } 278 279 ctrl->nc_sup_logs = sup; 280 nvme_log_req_fini(req); 281 return; 282 283 err: 284 /* 285 * Flag this as failed and attempt to save the error that got us here. 286 * If a memory allocation failure occurs here, then there's not much 287 * more we can do, but at least this flag set and no error information 288 * does indicate a memory problem. Regardless, we clear out the error 289 * from the nvme_ctrl_t. 290 */ 291 ctrl->nc_flags |= NVME_CTRL_F_SUP_LOGS_FAILED; 292 if ((ctrl->nc_sup_logs_err = calloc(1, sizeof (nvme_err_data_t))) != 293 NULL) { 294 nvme_ctrl_err_save(ctrl, ctrl->nc_sup_logs_err); 295 } 296 (void) nvme_ctrl_success(ctrl); 297 nvme_log_req_fini(req); 298 free(sup); 299 } 300 301 void 302 nvme_log_discover_fini(nvme_log_iter_t *iter) 303 { 304 free(iter); 305 } 306 307 static bool 308 nvme_log_discover_one(nvme_log_iter_t *iter, const nvme_log_page_info_t *info) 309 { 310 bool var; 311 nvme_log_disc_t *disc = &iter->nli_nld; 312 nvme_ctrl_t *ctrl = iter->nli_ctrl; 313 nvme_log_disc_scope_t scope; 314 nvme_valid_ctrl_data_t data; 315 316 data.vcd_vers = &iter->nli_ctrl->nc_vers; 317 data.vcd_id = &iter->nli_ctrl->nc_info; 318 319 /* 320 * Determine the scope of the log page so we can understand if the user 321 * cares about this or not. 322 */ 323 scope = nvme_log_page_info_scope(info, &data); 324 if ((iter->nli_scope & scope) == 0) { 325 return (false); 326 } 327 328 (void) memset(disc, 0, sizeof (nvme_log_disc_t)); 329 330 /* 331 * Now that we know that this applies, fill in the remaining information 332 * that we need. 333 */ 334 disc->nld_short = info->nlpi_short; 335 disc->nld_desc = info->nlpi_human; 336 disc->nld_aliases = info->nlpi_aliases; 337 disc->nld_naliases = info->nlpi_naliases; 338 disc->nld_lid = info->nlpi_lid; 339 disc->nld_csi = info->nlpi_csi; 340 disc->nld_kind = info->nlpi_kind; 341 disc->nld_srcs = info->nlpi_source; 342 disc->nld_scope = scope; 343 disc->nld_fields = info->nlpi_disc; 344 345 disc->nld_alloc_len = nvme_log_page_info_size(info, &data, &var); 346 if (disc->nld_alloc_len != 0) { 347 if (var) { 348 disc->nld_var_func = info->nlpi_var_func; 349 disc->nld_size_kind = NVME_LOG_SIZE_K_VAR; 350 } else { 351 disc->nld_size_kind = NVME_LOG_SIZE_K_FIXED; 352 } 353 } else { 354 disc->nld_size_kind = NVME_LOG_SIZE_K_UNKNOWN; 355 disc->nld_alloc_len = NVME_LOG_MAX_SIZE; 356 } 357 358 /* 359 * Determine if a log page is supported. This uses the per-log knowledge 360 * built into the nvme_log_page_info_t structures by default. When we 361 * have the NVMe 2.0 Supported Log Pages log, then we require both that 362 * and the internal bits fire. We've encountered some cases where the 363 * datasheet indicates something is supported, but firmware does not for 364 * some surprising reason. We haven't yet found cases where our logic 365 * says something is implemented but the log page information is wrong. 366 * That will likely come some day and we'll need a quirks list. 367 */ 368 if (nvme_log_page_info_supported(info, &data) && 369 (ctrl->nc_sup_logs == NULL || 370 ctrl->nc_sup_logs->nl_logs[info->nlpi_lid].ns_lsupp) != 0) { 371 disc->nld_flags |= NVME_LOG_DISC_F_IMPL; 372 } 373 374 return (true); 375 } 376 377 nvme_iter_t 378 nvme_log_discover_step(nvme_log_iter_t *iter, const nvme_log_disc_t **outp) 379 { 380 *outp = NULL; 381 nvme_ctrl_t *ctrl = iter->nli_ctrl; 382 383 if (iter->nli_std_done && iter->nli_vs_done) { 384 return (NVME_ITER_DONE); 385 } 386 387 /* 388 * We start by walking the list of spec pages and then check the device 389 * specific ones. While we may have the NVMe 2.0 Supported Log Page 390 * information, we don't really use that in discovery right now as it's 391 * rather hard to communicate useful discovery information with that. We 392 * mostly use this as a check on whether or not the log page is actually 393 * implemented based on our knowledge. 394 */ 395 if (!iter->nli_std_done) { 396 while (iter->nli_cur_idx < nvme_std_log_npages) { 397 const nvme_log_page_info_t *info = 398 &nvme_std_log_pages[iter->nli_cur_idx]; 399 iter->nli_cur_idx++; 400 if (nvme_log_discover_one(iter, info)) { 401 *outp = &iter->nli_nld; 402 return (NVME_ITER_VALID); 403 } 404 } 405 iter->nli_std_done = true; 406 iter->nli_cur_idx = 0; 407 } 408 409 if (ctrl->nc_vsd == NULL) { 410 iter->nli_vs_done = true; 411 return (NVME_ITER_DONE); 412 } 413 414 while (iter->nli_cur_idx < ctrl->nc_vsd->nvd_nlogs) { 415 const nvme_log_page_info_t *info = 416 ctrl->nc_vsd->nvd_logs[iter->nli_cur_idx]; 417 iter->nli_cur_idx++; 418 if (nvme_log_discover_one(iter, info)) { 419 *outp = &iter->nli_nld; 420 return (NVME_ITER_VALID); 421 } 422 } 423 424 iter->nli_vs_done = true; 425 iter->nli_cur_idx = 0; 426 return (NVME_ITER_DONE); 427 } 428 429 bool 430 nvme_log_discover_init(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes, 431 uint32_t flags, nvme_log_iter_t **iterp) 432 { 433 nvme_log_iter_t *iter; 434 435 if (!nvme_log_discover_validate(ctrl, scopes, flags)) { 436 return (false); 437 } 438 439 if (iterp == NULL) { 440 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 441 "encountered invalid nvme_log_iter_t output pointer: %p", 442 iterp)); 443 } 444 445 iter = calloc(1, sizeof (nvme_log_iter_t)); 446 if (iter == NULL) { 447 int e = errno; 448 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 449 "allocate memory for a new nvme_log_iter_t: %s", 450 strerror(e))); 451 } 452 453 if ((ctrl->nc_flags & NVME_CTRL_F_SUP_LOGS_VALID) == 0) { 454 nvme_log_discover_fetch_sup_logs(ctrl); 455 } 456 457 iter->nli_ctrl = ctrl; 458 iter->nli_scope = scopes; 459 460 *iterp = iter; 461 return (nvme_ctrl_success(ctrl)); 462 } 463 464 /* 465 * Walk all of the requested log pages that match and fill out the information 466 * for the discovery form. 467 */ 468 bool 469 nvme_log_discover(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes, 470 uint32_t flags, nvme_log_disc_f func, void *arg) 471 { 472 nvme_log_iter_t *iter; 473 nvme_iter_t ret; 474 const nvme_log_disc_t *disc; 475 476 if (func == NULL) { 477 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 478 "encountered invalid nvme_log_disc_f function pointer: %p", 479 func)); 480 } 481 482 if (!nvme_log_discover_init(ctrl, scopes, flags, &iter)) { 483 return (false); 484 } 485 486 while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) { 487 if (!func(ctrl, disc, arg)) 488 break; 489 } 490 491 nvme_log_discover_fini(iter); 492 if (ret == NVME_ITER_ERROR) { 493 return (false); 494 } 495 496 return (nvme_ctrl_success(ctrl)); 497 } 498 499 500 void 501 nvme_log_req_fini(nvme_log_req_t *req) 502 { 503 free(req); 504 } 505 506 /* 507 * This is the totally manual path that occurs. When this is used, we require 508 * that people specify a subset of the fields here, primarily just the actual 509 * log page, output, and CSI. We don't try to be clever here and use the 510 * discovery information to know what to set. That's reserved for creating this 511 * request based upon discovery information. 512 */ 513 bool 514 nvme_log_req_init(nvme_ctrl_t *ctrl, nvme_log_req_t **reqp) 515 { 516 nvme_log_req_t *req; 517 518 if (reqp == NULL) { 519 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 520 "encountered invalid nvme_log_req_t output pointer: %p", 521 reqp)); 522 } 523 524 req = calloc(1, sizeof (nvme_log_req_t)); 525 if (req == NULL) { 526 int e = errno; 527 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 528 "allocate memory for a new nvme_log_req_t: %s", 529 strerror(e))); 530 } 531 532 req->nlr_ctrl = ctrl; 533 for (size_t i = 0; i < nvme_log_nfields; i++) { 534 if (nvme_log_fields[i].nlfi_def_req) { 535 req->nlr_need |= 1 << i; 536 } 537 538 if (nvme_log_fields[i].nlfi_def_allow) { 539 req->nlr_allow |= 1 << i; 540 } 541 } 542 543 /* 544 * Because we don't know anything about this log request, indicate that 545 * if we're given the all namespaces nsid that's fine. We'll still 546 * check the controller version when this is set first. 547 */ 548 req->nlr_flags |= NVME_LOG_REQ_F_BCAST_NS_OK; 549 550 *reqp = req; 551 return (nvme_ctrl_success(ctrl)); 552 } 553 554 bool 555 nvme_log_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_log_disc_t *disc, 556 nvme_log_req_t **reqp) 557 { 558 nvme_log_req_t *req; 559 560 if (disc == NULL) { 561 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 562 "encountered invalid nvme_log_disc_t pointer: %p", disc)); 563 } 564 565 if (reqp == NULL) { 566 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 567 "encountered invalid nvme_log_req_t output pointer: %p", 568 reqp)); 569 } 570 571 if ((disc->nld_flags & NVME_LOG_DISC_F_IMPL) == 0) { 572 return (nvme_ctrl_error(ctrl, NVME_ERR_LOG_UNSUP_BY_DEV, 0, 573 "cannot create log request for log %s (CSI/LID 0x%x/0x%x) " 574 "because it is not supported by the device", 575 disc->nld_short, disc->nld_csi, disc->nld_lid)); 576 } 577 578 req = calloc(1, sizeof (nvme_log_req_t)); 579 if (req == NULL) { 580 int e = errno; 581 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 582 "allocate memory for a new nvme_log_req_t: %s", 583 strerror(e))); 584 } 585 586 req->nlr_ctrl = ctrl; 587 req->nlr_lid = disc->nld_lid; 588 req->nlr_csi = disc->nld_csi; 589 590 /* 591 * Setting the size is always required here, because this is how we 592 * track that the output pointer is actually set. We will always allow 593 * setting the offset though it's possible the controller won't support 594 * that. 595 */ 596 req->nlr_need = req->nlr_allow = 1 << NVME_LOG_REQ_FIELD_SIZE; 597 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_OFFSET; 598 599 /* 600 * Initialize our needed and allowed fields. Because we have the actual 601 * lid/csi from the above, we don't allow the user to overwrite them at 602 * all. For the LSP and LSI, right now these are all our nothing, but 603 * this may break. RAE is a bit special and discussed below. 604 */ 605 if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_LSP) != 0) { 606 req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_LSP; 607 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_LSP; 608 } 609 610 if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_LSI) != 0) { 611 req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_LSI; 612 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_LSI; 613 } 614 615 /* 616 * Because RAE wasn't added until NVMe 1.3, we can't do much with it 617 * before that. However, once it's here we definitely want to default to 618 * setting it by default so that way we can minimize the chance that 619 * we'll steal an alert that the kernel needs to read and acknowledge. 620 */ 621 if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_RAE) != 0 && 622 nvme_vers_ctrl_atleast(ctrl, 623 nvme_log_fields[NVME_LOG_REQ_FIELD_RAE].nlfi_vers)) { 624 req->nlr_flags |= NVME_LOG_REQ_F_RAE; 625 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_RAE; 626 } 627 628 /* 629 * Check the log page scope setting. If the log is said to be namespace 630 * scoped, then we'll allow the namespace to be specified. If it 631 * supports a different scope as well, then we'll default to the 632 * controller scope and this field is optional. Otherwise, it'll be 633 * required and it will be a mandatory field. 634 */ 635 if ((disc->nld_scope & NVME_LOG_SCOPE_NS) != 0) { 636 req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_NSID; 637 if ((disc->nld_scope & ~NVME_LOG_SCOPE_NS) != 0) { 638 req->nlr_flags |= NVME_LOG_REQ_F_BCAST_NS_OK; 639 req->nlr_nsid = NVME_NSID_BCAST; 640 } else { 641 req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_NSID; 642 } 643 } 644 645 *reqp = req; 646 return (nvme_ctrl_success(ctrl)); 647 } 648 649 typedef struct { 650 bool nlia_found; 651 const char *nlia_name; 652 nvme_log_req_t *nlia_req; 653 nvme_log_disc_t **nlia_discp; 654 nvme_err_data_t nlia_err; 655 } nvme_log_init_arg_t; 656 657 static bool 658 nvme_log_req_match_disc(const char *name, const nvme_log_disc_t *disc) 659 { 660 if (strcmp(name, disc->nld_short) == 0) { 661 return (true); 662 } 663 664 for (size_t i = 0; i < disc->nld_naliases; i++) { 665 if (strcmp(name, disc->nld_aliases[i]) == 0) { 666 return (true); 667 } 668 } 669 670 return (false); 671 } 672 673 static bool 674 nvme_log_req_init_by_name_cb(nvme_ctrl_t *ctrl, const nvme_log_disc_t *disc, 675 void *arg) 676 { 677 nvme_log_init_arg_t *init = arg; 678 679 if (!nvme_log_req_match_disc(init->nlia_name, disc)) { 680 return (true); 681 } 682 683 init->nlia_found = true; 684 if (!nvme_log_req_init_by_disc(ctrl, disc, &init->nlia_req)) { 685 nvme_ctrl_err_save(ctrl, &init->nlia_err); 686 init->nlia_req = NULL; 687 } else if (init->nlia_discp != NULL) { 688 if (!nvme_log_disc_dup(ctrl, disc, init->nlia_discp)) { 689 nvme_ctrl_err_save(ctrl, &init->nlia_err); 690 nvme_log_req_fini(init->nlia_req); 691 init->nlia_req = NULL; 692 } 693 } 694 695 return (false); 696 } 697 698 bool 699 nvme_log_req_init_by_name(nvme_ctrl_t *ctrl, const char *name, uint32_t flags, 700 nvme_log_disc_t **discp, nvme_log_req_t **reqp) 701 { 702 nvme_log_init_arg_t init; 703 704 /* 705 * We consider discp an optional argument and therefore do not check it 706 * unlike name and reqp. 707 */ 708 if (reqp == NULL) { 709 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 710 "encountered invalid nvme_log_req_t output pointer: %p", 711 reqp)); 712 } 713 714 if (name == NULL) { 715 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 716 "encountered invalid pointer for log page name: %p", name)); 717 } 718 719 (void) memset(&init, 0, sizeof (init)); 720 init.nlia_name = name; 721 init.nlia_discp = discp; 722 723 if (!nvme_log_discover(ctrl, NVME_LOG_SCOPE_CTRL | 724 NVME_LOG_SCOPE_NVM | NVME_LOG_SCOPE_NS, flags, 725 nvme_log_req_init_by_name_cb, &init)) { 726 return (false); 727 } 728 729 if (!init.nlia_found) { 730 return (nvme_ctrl_error(ctrl, NVME_ERR_LOG_NAME_UNKNOWN, 0, 731 "failed to find log page with name %s", name)); 732 } 733 734 /* 735 * If we failed to create the request, but we did find it, then that 736 * means something went wrong and we can go ahead and already return an 737 * error. 738 */ 739 if (init.nlia_req == NULL) { 740 nvme_ctrl_err_set(ctrl, &init.nlia_err); 741 return (false); 742 } 743 744 *reqp = init.nlia_req; 745 return (nvme_ctrl_success(ctrl)); 746 } 747 748 static void 749 nvme_log_req_set_need(nvme_log_req_t *req, nvme_log_req_field_t field) 750 { 751 req->nlr_need |= 1 << field; 752 } 753 754 static void 755 nvme_log_req_clear_need(nvme_log_req_t *req, nvme_log_req_field_t field) 756 { 757 req->nlr_need &= ~(1 << field); 758 } 759 760 static const nvme_field_check_t nvme_log_check_lid = { 761 nvme_log_fields, NVME_LOG_REQ_FIELD_LID, 762 NVME_ERR_LOG_LID_RANGE, 0, 0 763 }; 764 765 bool 766 nvme_log_req_set_lid(nvme_log_req_t *req, uint32_t lid) 767 { 768 if (!nvme_field_check_one(req->nlr_ctrl, lid, "get log page", 769 &nvme_log_check_lid, req->nlr_allow)) { 770 return (false); 771 } 772 773 req->nlr_lid = lid; 774 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LID); 775 return (nvme_ctrl_success(req->nlr_ctrl)); 776 } 777 778 static const nvme_field_check_t nvme_log_check_lsp = { 779 nvme_log_fields, NVME_LOG_REQ_FIELD_LSP, 780 NVME_ERR_LOG_LSP_RANGE, NVME_ERR_LOG_LSP_UNSUP, 781 NVME_ERR_LOG_LSP_UNUSE 782 }; 783 784 bool 785 nvme_log_req_set_lsp(nvme_log_req_t *req, uint32_t lsp) 786 { 787 if (!nvme_field_check_one(req->nlr_ctrl, lsp, "get log page", 788 &nvme_log_check_lsp, req->nlr_allow)) { 789 return (false); 790 } 791 792 req->nlr_lsp = lsp; 793 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSP); 794 return (nvme_ctrl_success(req->nlr_ctrl)); 795 } 796 797 static const nvme_field_check_t nvme_log_check_lsi = { 798 nvme_log_fields, NVME_LOG_REQ_FIELD_LSI, 799 NVME_ERR_LOG_LSI_RANGE, NVME_ERR_LOG_LSI_UNSUP, 800 NVME_ERR_LOG_LSI_UNUSE 801 }; 802 803 bool 804 nvme_log_req_set_lsi(nvme_log_req_t *req, uint32_t lsi) 805 { 806 if (!nvme_field_check_one(req->nlr_ctrl, lsi, "get log page", 807 &nvme_log_check_lsi, req->nlr_allow)) { 808 return (false); 809 } 810 811 req->nlr_lsi = lsi; 812 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSI); 813 return (nvme_ctrl_success(req->nlr_ctrl)); 814 } 815 816 static const nvme_field_check_t nvme_log_check_csi = { 817 nvme_log_fields, NVME_LOG_REQ_FIELD_CSI, 818 NVME_ERR_LOG_CSI_RANGE, NVME_ERR_LOG_CSI_UNSUP, 0 819 }; 820 821 bool 822 nvme_log_req_set_csi(nvme_log_req_t *req, nvme_csi_t csi) 823 { 824 if (!nvme_field_check_one(req->nlr_ctrl, csi, "get log page", 825 &nvme_log_check_csi, req->nlr_allow)) { 826 return (false); 827 } 828 829 req->nlr_csi = csi; 830 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_CSI); 831 return (nvme_ctrl_success(req->nlr_ctrl)); 832 } 833 834 static const nvme_field_check_t nvme_log_check_size = { 835 nvme_log_fields, NVME_LOG_REQ_FIELD_SIZE, 836 NVME_ERR_LOG_SIZE_RANGE, 0, 0 837 }; 838 839 bool 840 nvme_log_req_set_output(nvme_log_req_t *req, void *buf, size_t buflen) 841 { 842 if (buf == NULL) { 843 return (nvme_ctrl_error(req->nlr_ctrl, NVME_ERR_BAD_PTR, 0, 844 "log request output buffer cannot be NULL")); 845 } 846 847 if (!nvme_field_check_one(req->nlr_ctrl, buflen, "get log page", 848 &nvme_log_check_size, req->nlr_allow)) { 849 return (false); 850 } 851 852 req->nlr_output = buf; 853 req->nlr_output_len = buflen; 854 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_SIZE); 855 return (nvme_ctrl_success(req->nlr_ctrl)); 856 } 857 858 bool 859 nvme_log_req_clear_output(nvme_log_req_t *req) 860 { 861 req->nlr_output = NULL; 862 req->nlr_output_len = 0; 863 864 /* 865 * We can always set that we need this again as every log page requires 866 * a size being set. See the default allow settings for the field in 867 * nvme_log_fields[] and nvme_log_req_init_by_disc(). 868 */ 869 nvme_log_req_set_need(req, NVME_LOG_REQ_FIELD_SIZE); 870 return (nvme_ctrl_success(req->nlr_ctrl)); 871 } 872 873 static const nvme_field_check_t nvme_log_check_offset = { 874 nvme_log_fields, NVME_LOG_REQ_FIELD_OFFSET, 875 NVME_ERR_LOG_OFFSET_RANGE, 0, 0 876 }; 877 878 bool 879 nvme_log_req_set_offset(nvme_log_req_t *req, uint64_t off) 880 { 881 if (!nvme_field_check_one(req->nlr_ctrl, off, "get log page", 882 &nvme_log_check_offset, req->nlr_allow)) { 883 return (false); 884 } 885 886 req->nlr_offset = off; 887 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_OFFSET); 888 return (nvme_ctrl_success(req->nlr_ctrl)); 889 } 890 891 static const nvme_field_check_t nvme_log_check_nsid = { 892 nvme_log_fields, NVME_LOG_REQ_FIELD_NSID, NVME_ERR_NS_RANGE, 0, 0 893 }; 894 895 bool 896 nvme_log_req_set_nsid(nvme_log_req_t *req, uint32_t nsid) 897 { 898 nvme_ctrl_t *ctrl = req->nlr_ctrl; 899 900 if (nsid == NVME_NSID_BCAST && 901 (req->nlr_flags & NVME_LOG_REQ_F_BCAST_NS_OK) == 0) { 902 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all " 903 "namespaces/controller nsid (0x%x) is not allowed for this " 904 "log page, valid namespaces are [0x%x, 0x%x]", nsid, 905 NVME_NSID_MIN, req->nlr_ctrl->nc_info.id_nn)); 906 } 907 908 if (!nvme_field_check_one(req->nlr_ctrl, nsid, "get log page", 909 &nvme_log_check_nsid, req->nlr_allow)) { 910 return (false); 911 } 912 913 req->nlr_nsid = nsid; 914 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_NSID); 915 return (nvme_ctrl_success(req->nlr_ctrl)); 916 } 917 918 static const nvme_field_check_t nvme_log_check_rae = { 919 nvme_log_fields, NVME_LOG_REQ_FIELD_RAE, 920 NVME_ERR_LOG_RAE_RANGE, NVME_ERR_LOG_RAE_UNSUP, 921 NVME_ERR_LOG_RAE_UNUSE 922 }; 923 924 bool 925 nvme_log_req_set_rae(nvme_log_req_t *req, bool rae) 926 { 927 if (!nvme_field_check_one(req->nlr_ctrl, rae, "get log page", 928 &nvme_log_check_rae, req->nlr_allow)) { 929 return (false); 930 } 931 932 if (rae) { 933 req->nlr_flags |= NVME_LOG_REQ_F_RAE; 934 } else { 935 req->nlr_flags &= ~NVME_LOG_REQ_F_RAE; 936 } 937 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_RAE); 938 return (nvme_ctrl_success(req->nlr_ctrl)); 939 } 940 941 bool 942 nvme_log_req_exec(nvme_log_req_t *req) 943 { 944 nvme_ctrl_t *ctrl = req->nlr_ctrl; 945 nvme_ioctl_get_logpage_t log; 946 947 if (req->nlr_need != 0) { 948 return (nvme_field_miss_err(ctrl, nvme_log_fields, 949 nvme_log_nfields, NVME_ERR_LOG_REQ_MISSING_FIELDS, 950 "get log page", req->nlr_need)); 951 } 952 953 (void) memset(&log, 0, sizeof (nvme_ioctl_get_logpage_t)); 954 log.nigl_common.nioc_nsid = req->nlr_nsid; 955 log.nigl_csi = req->nlr_csi; 956 log.nigl_lid = req->nlr_lid; 957 log.nigl_lsp = req->nlr_lsp; 958 log.nigl_lsi = req->nlr_lsi; 959 if ((req->nlr_flags & NVME_LOG_REQ_F_RAE) != 0) { 960 log.nigl_rae = 1; 961 } 962 log.nigl_len = req->nlr_output_len; 963 log.nigl_offset = req->nlr_offset; 964 log.nigl_data = (uintptr_t)req->nlr_output; 965 966 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_LOGPAGE, &log) != 0) { 967 int e = errno; 968 return (nvme_ioctl_syserror(ctrl, e, "get log page")); 969 } 970 971 if (log.nigl_common.nioc_drv_err != NVME_IOCTL_E_OK) { 972 return (nvme_ioctl_error(ctrl, &log.nigl_common, 973 "get log page")); 974 } 975 976 return (nvme_ctrl_success(ctrl)); 977 } 978