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
nvme_log_disc_free(nvme_log_disc_t * disc)53 nvme_log_disc_free(nvme_log_disc_t *disc)
54 {
55 free(disc);
56 }
57
58 const char *
nvme_log_disc_name(const nvme_log_disc_t * disc)59 nvme_log_disc_name(const nvme_log_disc_t *disc)
60 {
61 return (disc->nld_short);
62 }
63
64 const char *
nvme_log_disc_desc(const nvme_log_disc_t * disc)65 nvme_log_disc_desc(const nvme_log_disc_t *disc)
66 {
67 return (disc->nld_desc);
68 }
69
70 nvme_csi_t
nvme_log_disc_csi(const nvme_log_disc_t * disc)71 nvme_log_disc_csi(const nvme_log_disc_t *disc)
72 {
73 return (disc->nld_csi);
74 }
75
76 uint32_t
nvme_log_disc_lid(const nvme_log_disc_t * disc)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
nvme_log_disc_kind(const nvme_log_disc_t * disc)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
nvme_log_disc_sources(const nvme_log_disc_t * disc)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
nvme_log_disc_fields(const nvme_log_disc_t * disc)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
nvme_log_disc_scopes(const nvme_log_disc_t * disc)101 nvme_log_disc_scopes(const nvme_log_disc_t *disc)
102 {
103 return (disc->nld_scope);
104 }
105
106 bool
nvme_log_disc_impl(const nvme_log_disc_t * disc)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
nvme_log_disc_size(const nvme_log_disc_t * disc,uint64_t * sizep)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
nvme_log_disc_calc_size(const nvme_log_disc_t * disc,uint64_t * act,const void * buf,size_t buflen)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
nvme_log_disc_dup(nvme_ctrl_t * ctrl,const nvme_log_disc_t * src,nvme_log_disc_t ** discp)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
nvme_log_discover_validate(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags)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
nvme_log_discover_fini(nvme_log_iter_t * iter)202 nvme_log_discover_fini(nvme_log_iter_t *iter)
203 {
204 free(iter);
205 }
206
207 static bool
nvme_log_discover_one(nvme_log_iter_t * iter,const nvme_log_page_info_t * info)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
nvme_log_discover_step(nvme_log_iter_t * iter,const nvme_log_disc_t ** outp)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
nvme_log_discover_init(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags,nvme_log_iter_t ** iterp)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
nvme_log_discover(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags,nvme_log_disc_f func,void * arg)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
nvme_log_req_fini(nvme_log_req_t * req)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
nvme_log_req_init(nvme_ctrl_t * ctrl,nvme_log_req_t ** reqp)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
nvme_log_req_init_by_disc(nvme_ctrl_t * ctrl,const nvme_log_disc_t * disc,nvme_log_req_t ** reqp)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
nvme_log_req_init_by_name_cb(nvme_ctrl_t * ctrl,const nvme_log_disc_t * disc,void * arg)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
nvme_log_req_init_by_name(nvme_ctrl_t * ctrl,const char * name,uint32_t flags,nvme_log_disc_t ** discp,nvme_log_req_t ** reqp)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
nvme_log_req_set_need(nvme_log_req_t * req,nvme_log_req_field_t field)613 nvme_log_req_set_need(nvme_log_req_t *req, nvme_log_req_field_t field)
614 {
615 req->nlr_need |= 1 << field;
616 }
617
618 static void
nvme_log_req_clear_need(nvme_log_req_t * req,nvme_log_req_field_t field)619 nvme_log_req_clear_need(nvme_log_req_t *req, nvme_log_req_field_t field)
620 {
621 req->nlr_need &= ~(1 << field);
622 }
623
624 static const nvme_field_check_t nvme_log_check_lid = {
625 nvme_log_fields, NVME_LOG_REQ_FIELD_LID,
626 NVME_ERR_LOG_LID_RANGE, 0, 0
627 };
628
629 bool
nvme_log_req_set_lid(nvme_log_req_t * req,uint32_t lid)630 nvme_log_req_set_lid(nvme_log_req_t *req, uint32_t lid)
631 {
632 if (!nvme_field_check_one(req->nlr_ctrl, lid, "get log page",
633 &nvme_log_check_lid, req->nlr_allow)) {
634 return (false);
635 }
636
637 req->nlr_lid = lid;
638 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LID);
639 return (nvme_ctrl_success(req->nlr_ctrl));
640 }
641
642 static const nvme_field_check_t nvme_log_check_lsp = {
643 nvme_log_fields, NVME_LOG_REQ_FIELD_LSP,
644 NVME_ERR_LOG_LSP_RANGE, NVME_ERR_LOG_LSP_UNSUP,
645 NVME_ERR_LOG_LSP_UNUSE
646 };
647
648 bool
nvme_log_req_set_lsp(nvme_log_req_t * req,uint32_t lsp)649 nvme_log_req_set_lsp(nvme_log_req_t *req, uint32_t lsp)
650 {
651 if (!nvme_field_check_one(req->nlr_ctrl, lsp, "get log page",
652 &nvme_log_check_lsp, req->nlr_allow)) {
653 return (false);
654 }
655
656 req->nlr_lsp = lsp;
657 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSP);
658 return (nvme_ctrl_success(req->nlr_ctrl));
659 }
660
661 static const nvme_field_check_t nvme_log_check_lsi = {
662 nvme_log_fields, NVME_LOG_REQ_FIELD_LSI,
663 NVME_ERR_LOG_LSI_RANGE, NVME_ERR_LOG_LSI_UNSUP,
664 NVME_ERR_LOG_LSI_UNUSE
665 };
666
667 bool
nvme_log_req_set_lsi(nvme_log_req_t * req,uint32_t lsi)668 nvme_log_req_set_lsi(nvme_log_req_t *req, uint32_t lsi)
669 {
670 if (!nvme_field_check_one(req->nlr_ctrl, lsi, "get log page",
671 &nvme_log_check_lsi, req->nlr_allow)) {
672 return (false);
673 }
674
675 req->nlr_lsi = lsi;
676 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSI);
677 return (nvme_ctrl_success(req->nlr_ctrl));
678 }
679
680 static const nvme_field_check_t nvme_log_check_csi = {
681 nvme_log_fields, NVME_LOG_REQ_FIELD_CSI,
682 NVME_ERR_LOG_CSI_RANGE, NVME_ERR_LOG_CSI_UNSUP, 0
683 };
684
685 bool
nvme_log_req_set_csi(nvme_log_req_t * req,nvme_csi_t csi)686 nvme_log_req_set_csi(nvme_log_req_t *req, nvme_csi_t csi)
687 {
688 if (!nvme_field_check_one(req->nlr_ctrl, csi, "get log page",
689 &nvme_log_check_csi, req->nlr_allow)) {
690 return (false);
691 }
692
693 req->nlr_csi = csi;
694 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_CSI);
695 return (nvme_ctrl_success(req->nlr_ctrl));
696 }
697
698 static const nvme_field_check_t nvme_log_check_size = {
699 nvme_log_fields, NVME_LOG_REQ_FIELD_SIZE,
700 NVME_ERR_LOG_SIZE_RANGE, 0, 0
701 };
702
703 bool
nvme_log_req_set_output(nvme_log_req_t * req,void * buf,size_t buflen)704 nvme_log_req_set_output(nvme_log_req_t *req, void *buf, size_t buflen)
705 {
706 if (buf == NULL) {
707 return (nvme_ctrl_error(req->nlr_ctrl, NVME_ERR_BAD_PTR, 0,
708 "log request output buffer cannot be NULL"));
709 }
710
711 if (!nvme_field_check_one(req->nlr_ctrl, buflen, "get log page",
712 &nvme_log_check_size, req->nlr_allow)) {
713 return (false);
714 }
715
716 req->nlr_output = buf;
717 req->nlr_output_len = buflen;
718 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_SIZE);
719 return (nvme_ctrl_success(req->nlr_ctrl));
720 }
721
722 bool
nvme_log_req_clear_output(nvme_log_req_t * req)723 nvme_log_req_clear_output(nvme_log_req_t *req)
724 {
725 req->nlr_output = NULL;
726 req->nlr_output_len = 0;
727
728 /*
729 * We can always set that we need this again as every log page requires
730 * a size being set. See the default allow settings for the field in
731 * nvme_log_fields[] and nvme_log_req_init_by_disc().
732 */
733 nvme_log_req_set_need(req, NVME_LOG_REQ_FIELD_SIZE);
734 return (nvme_ctrl_success(req->nlr_ctrl));
735 }
736
737 static const nvme_field_check_t nvme_log_check_offset = {
738 nvme_log_fields, NVME_LOG_REQ_FIELD_OFFSET,
739 NVME_ERR_LOG_OFFSET_RANGE, 0, 0
740 };
741
742 bool
nvme_log_req_set_offset(nvme_log_req_t * req,uint64_t off)743 nvme_log_req_set_offset(nvme_log_req_t *req, uint64_t off)
744 {
745 if (!nvme_field_check_one(req->nlr_ctrl, off, "get log page",
746 &nvme_log_check_offset, req->nlr_allow)) {
747 return (false);
748 }
749
750 req->nlr_offset = off;
751 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_OFFSET);
752 return (nvme_ctrl_success(req->nlr_ctrl));
753 }
754
755 static const nvme_field_check_t nvme_log_check_nsid = {
756 nvme_log_fields, NVME_LOG_REQ_FIELD_NSID, NVME_ERR_NS_RANGE, 0, 0
757 };
758
759 bool
nvme_log_req_set_nsid(nvme_log_req_t * req,uint32_t nsid)760 nvme_log_req_set_nsid(nvme_log_req_t *req, uint32_t nsid)
761 {
762 nvme_ctrl_t *ctrl = req->nlr_ctrl;
763
764 if (nsid == NVME_NSID_BCAST &&
765 (req->nlr_flags & NVME_LOG_REQ_F_BCAST_NS_OK) == 0) {
766 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
767 "namespaces/controller nsid (0x%x) is not allowed for this "
768 "log page, valid namespaces are [0x%x, 0x%x]", nsid,
769 NVME_NSID_MIN, req->nlr_ctrl->nc_info.id_nn));
770 }
771
772 if (!nvme_field_check_one(req->nlr_ctrl, nsid, "get log page",
773 &nvme_log_check_nsid, req->nlr_allow)) {
774 return (false);
775 }
776
777 req->nlr_nsid = nsid;
778 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_NSID);
779 return (nvme_ctrl_success(req->nlr_ctrl));
780 }
781
782 static const nvme_field_check_t nvme_log_check_rae = {
783 nvme_log_fields, NVME_LOG_REQ_FIELD_RAE,
784 NVME_ERR_LOG_RAE_RANGE, NVME_ERR_LOG_RAE_UNSUP,
785 NVME_ERR_LOG_RAE_UNUSE
786 };
787
788 bool
nvme_log_req_set_rae(nvme_log_req_t * req,bool rae)789 nvme_log_req_set_rae(nvme_log_req_t *req, bool rae)
790 {
791 if (!nvme_field_check_one(req->nlr_ctrl, rae, "get log page",
792 &nvme_log_check_rae, req->nlr_allow)) {
793 return (false);
794 }
795
796 if (rae) {
797 req->nlr_flags |= NVME_LOG_REQ_F_RAE;
798 } else {
799 req->nlr_flags &= ~NVME_LOG_REQ_F_RAE;
800 }
801 nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_RAE);
802 return (nvme_ctrl_success(req->nlr_ctrl));
803 }
804
805 bool
nvme_log_req_exec(nvme_log_req_t * req)806 nvme_log_req_exec(nvme_log_req_t *req)
807 {
808 nvme_ctrl_t *ctrl = req->nlr_ctrl;
809 nvme_ioctl_get_logpage_t log;
810
811 if (req->nlr_need != 0) {
812 return (nvme_field_miss_err(ctrl, nvme_log_fields,
813 nvme_log_nfields, NVME_ERR_LOG_REQ_MISSING_FIELDS,
814 "get log page", req->nlr_need));
815 }
816
817 (void) memset(&log, 0, sizeof (nvme_ioctl_get_logpage_t));
818 log.nigl_common.nioc_nsid = req->nlr_nsid;
819 log.nigl_csi = req->nlr_csi;
820 log.nigl_lid = req->nlr_lid;
821 log.nigl_lsp = req->nlr_lsp;
822 log.nigl_lsi = req->nlr_lsi;
823 if ((req->nlr_flags & NVME_LOG_REQ_F_RAE) != 0) {
824 log.nigl_rae = 1;
825 }
826 log.nigl_len = req->nlr_output_len;
827 log.nigl_offset = req->nlr_offset;
828 log.nigl_data = (uintptr_t)req->nlr_output;
829
830 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_LOGPAGE, &log) != 0) {
831 int e = errno;
832 return (nvme_ioctl_syserror(ctrl, e, "get log page"));
833 }
834
835 if (log.nigl_common.nioc_drv_err != NVME_IOCTL_E_OK) {
836 return (nvme_ioctl_error(ctrl, &log.nigl_common,
837 "get log page"));
838 }
839
840 return (nvme_ctrl_success(ctrl));
841 }
842