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