xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_log.c (revision fbd5b8684156f1cae0891d3e690e5bdf7644c840)
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
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 /*
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
nvme_log_discover_fetch_sup_logs(nvme_ctrl_t * ctrl)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
nvme_log_discover_fini(nvme_log_iter_t * iter)279 nvme_log_discover_fini(nvme_log_iter_t *iter)
280 {
281 	free(iter);
282 }
283 
284 static bool
nvme_log_discover_one(nvme_log_iter_t * iter,const nvme_log_page_info_t * info)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
nvme_log_discover_step(nvme_log_iter_t * iter,const nvme_log_disc_t ** outp)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
nvme_log_discover_init(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags,nvme_log_iter_t ** iterp)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
nvme_log_discover(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags,nvme_log_disc_f func,void * arg)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
nvme_log_req_fini(nvme_log_req_t * req)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
nvme_log_req_init(nvme_ctrl_t * ctrl,nvme_log_req_t ** reqp)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
nvme_log_req_init_by_disc(nvme_ctrl_t * ctrl,const nvme_log_disc_t * disc,nvme_log_req_t ** reqp)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
nvme_log_req_init_by_name_cb(nvme_ctrl_t * ctrl,const nvme_log_disc_t * disc,void * arg)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
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)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
nvme_log_req_set_need(nvme_log_req_t * req,nvme_log_req_field_t field)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
nvme_log_req_clear_need(nvme_log_req_t * req,nvme_log_req_field_t field)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
nvme_log_req_set_lid(nvme_log_req_t * req,uint32_t lid)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
nvme_log_req_set_lsp(nvme_log_req_t * req,uint32_t lsp)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
nvme_log_req_set_lsi(nvme_log_req_t * req,uint32_t lsi)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
nvme_log_req_set_csi(nvme_log_req_t * req,nvme_csi_t csi)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
nvme_log_req_set_output(nvme_log_req_t * req,void * buf,size_t buflen)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
nvme_log_req_clear_output(nvme_log_req_t * req)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
nvme_log_req_set_offset(nvme_log_req_t * req,uint64_t off)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
nvme_log_req_set_nsid(nvme_log_req_t * req,uint32_t nsid)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
nvme_log_req_set_rae(nvme_log_req_t * req,bool rae)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
nvme_log_req_exec(nvme_log_req_t * req)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