1533affcbSRobert Mustacchi /*
2533affcbSRobert Mustacchi * This file and its contents are supplied under the terms of the
3533affcbSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4533affcbSRobert Mustacchi * You may only use this file in accordance with the terms of version
5533affcbSRobert Mustacchi * 1.0 of the CDDL.
6533affcbSRobert Mustacchi *
7533affcbSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8533affcbSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9533affcbSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10533affcbSRobert Mustacchi */
11533affcbSRobert Mustacchi
12533affcbSRobert Mustacchi /*
13533affcbSRobert Mustacchi * Copyright 2024 Oxide Computer Company
14533affcbSRobert Mustacchi */
15533affcbSRobert Mustacchi
16533affcbSRobert Mustacchi /*
17533affcbSRobert Mustacchi * This implements all of the libnvme log discovery and log page execution
18533affcbSRobert Mustacchi * functions.
19533affcbSRobert Mustacchi *
20533affcbSRobert Mustacchi * In NVMe 1.0 there were just three mandatory log pages. These are the classic
21533affcbSRobert Mustacchi * Error, SMART, and firmware log pages. NVMe 1.1 added an optional log page for
22533affcbSRobert Mustacchi * NVM devices. Specifically this is the Reservation Log page. This was
23533affcbSRobert Mustacchi * indicated by the controller's ONCS field. Version 1.1 also introduced the Log
24533affcbSRobert Mustacchi * Page Attributes (LPA) field which is how additional pages were indicated as
25533affcbSRobert Mustacchi * being supported when not part of something like ONCS.
26533affcbSRobert Mustacchi *
27533affcbSRobert Mustacchi * Beginning in NVMe 1.2, many more log pages were added that were optional. In
28533affcbSRobert Mustacchi * particular, the changed namespace list and command effects log. The former
29533affcbSRobert Mustacchi * has support indicated via a bit in OAES (though this was not clarified until
30533affcbSRobert Mustacchi * NVMe 1.3) while the latter is in the LPA field. NVMe 1.2 also added the
31533affcbSRobert Mustacchi * ability for the Get Log Page to support larger amounts of data. The last
32533affcbSRobert Mustacchi * major piece of 1.2 was the addition of fabrics related log pages. Those are
33533affcbSRobert Mustacchi * not currently supported here.
34533affcbSRobert Mustacchi *
35533affcbSRobert Mustacchi * NVMe 1.3 and 1.4 continued the trend of adding log pages that are generally
36533affcbSRobert Mustacchi * optional, but may be required given a specific set of features being enabled.
37533affcbSRobert Mustacchi *
38533affcbSRobert Mustacchi * The largest change for log pages is in NVMe 2.0. It added a specific means of
39533affcbSRobert Mustacchi * indicating a command set for a given log page and also added the ability to
40533affcbSRobert Mustacchi * query all the supported log pages. This has existed previously, but only
41533affcbSRobert Mustacchi * through vendor specific means.
42533affcbSRobert Mustacchi */
43533affcbSRobert Mustacchi
44533affcbSRobert Mustacchi #include <string.h>
45533affcbSRobert Mustacchi #include <upanic.h>
46533affcbSRobert Mustacchi #include <sys/sysmacros.h>
47533affcbSRobert Mustacchi #include <sys/debug.h>
48533affcbSRobert Mustacchi #include <unistd.h>
49533affcbSRobert Mustacchi
50533affcbSRobert Mustacchi #include "libnvme_impl.h"
51533affcbSRobert Mustacchi
52533affcbSRobert Mustacchi void
nvme_log_disc_free(nvme_log_disc_t * disc)53533affcbSRobert Mustacchi nvme_log_disc_free(nvme_log_disc_t *disc)
54533affcbSRobert Mustacchi {
55533affcbSRobert Mustacchi free(disc);
56533affcbSRobert Mustacchi }
57533affcbSRobert Mustacchi
58533affcbSRobert Mustacchi const char *
nvme_log_disc_name(const nvme_log_disc_t * disc)59533affcbSRobert Mustacchi nvme_log_disc_name(const nvme_log_disc_t *disc)
60533affcbSRobert Mustacchi {
61533affcbSRobert Mustacchi return (disc->nld_short);
62533affcbSRobert Mustacchi }
63533affcbSRobert Mustacchi
64533affcbSRobert Mustacchi const char *
nvme_log_disc_desc(const nvme_log_disc_t * disc)65533affcbSRobert Mustacchi nvme_log_disc_desc(const nvme_log_disc_t *disc)
66533affcbSRobert Mustacchi {
67533affcbSRobert Mustacchi return (disc->nld_desc);
68533affcbSRobert Mustacchi }
69533affcbSRobert Mustacchi
70533affcbSRobert Mustacchi nvme_csi_t
nvme_log_disc_csi(const nvme_log_disc_t * disc)71533affcbSRobert Mustacchi nvme_log_disc_csi(const nvme_log_disc_t *disc)
72533affcbSRobert Mustacchi {
73533affcbSRobert Mustacchi return (disc->nld_csi);
74533affcbSRobert Mustacchi }
75533affcbSRobert Mustacchi
76533affcbSRobert Mustacchi uint32_t
nvme_log_disc_lid(const nvme_log_disc_t * disc)77533affcbSRobert Mustacchi nvme_log_disc_lid(const nvme_log_disc_t *disc)
78533affcbSRobert Mustacchi {
79533affcbSRobert Mustacchi return (disc->nld_lid);
80533affcbSRobert Mustacchi }
81533affcbSRobert Mustacchi
82533affcbSRobert Mustacchi nvme_log_disc_kind_t
nvme_log_disc_kind(const nvme_log_disc_t * disc)83533affcbSRobert Mustacchi nvme_log_disc_kind(const nvme_log_disc_t *disc)
84533affcbSRobert Mustacchi {
85533affcbSRobert Mustacchi return (disc->nld_kind);
86533affcbSRobert Mustacchi }
87533affcbSRobert Mustacchi
88533affcbSRobert Mustacchi nvme_log_disc_source_t
nvme_log_disc_sources(const nvme_log_disc_t * disc)89533affcbSRobert Mustacchi nvme_log_disc_sources(const nvme_log_disc_t *disc)
90533affcbSRobert Mustacchi {
91533affcbSRobert Mustacchi return (disc->nld_srcs);
92533affcbSRobert Mustacchi }
93533affcbSRobert Mustacchi
94533affcbSRobert Mustacchi nvme_log_disc_fields_t
nvme_log_disc_fields(const nvme_log_disc_t * disc)95533affcbSRobert Mustacchi nvme_log_disc_fields(const nvme_log_disc_t *disc)
96533affcbSRobert Mustacchi {
97533affcbSRobert Mustacchi return (disc->nld_fields);
98533affcbSRobert Mustacchi }
99533affcbSRobert Mustacchi
100533affcbSRobert Mustacchi nvme_log_disc_scope_t
nvme_log_disc_scopes(const nvme_log_disc_t * disc)101533affcbSRobert Mustacchi nvme_log_disc_scopes(const nvme_log_disc_t *disc)
102533affcbSRobert Mustacchi {
103533affcbSRobert Mustacchi return (disc->nld_scope);
104533affcbSRobert Mustacchi }
105533affcbSRobert Mustacchi
106533affcbSRobert Mustacchi bool
nvme_log_disc_impl(const nvme_log_disc_t * disc)107533affcbSRobert Mustacchi nvme_log_disc_impl(const nvme_log_disc_t *disc)
108533affcbSRobert Mustacchi {
109533affcbSRobert Mustacchi return ((disc->nld_flags & NVME_LOG_DISC_F_IMPL) != 0);
110533affcbSRobert Mustacchi }
111533affcbSRobert Mustacchi
112533affcbSRobert Mustacchi nvme_log_size_kind_t
nvme_log_disc_size(const nvme_log_disc_t * disc,uint64_t * sizep)113533affcbSRobert Mustacchi nvme_log_disc_size(const nvme_log_disc_t *disc, uint64_t *sizep)
114533affcbSRobert Mustacchi {
115533affcbSRobert Mustacchi *sizep = disc->nld_alloc_len;
116533affcbSRobert Mustacchi return (disc->nld_size_kind);
117533affcbSRobert Mustacchi }
118533affcbSRobert Mustacchi
119533affcbSRobert Mustacchi /*
120533affcbSRobert Mustacchi * For a variable length log page, presuming we've been given sufficient data
121533affcbSRobert Mustacchi * actually determine the overall length that should now be used to get all
122533affcbSRobert Mustacchi * data in the log.
123533affcbSRobert Mustacchi */
124533affcbSRobert Mustacchi bool
nvme_log_disc_calc_size(const nvme_log_disc_t * disc,uint64_t * act,const void * buf,size_t buflen)125533affcbSRobert Mustacchi nvme_log_disc_calc_size(const nvme_log_disc_t *disc, uint64_t *act,
126533affcbSRobert Mustacchi const void *buf, size_t buflen)
127533affcbSRobert Mustacchi {
128533affcbSRobert Mustacchi if (disc->nld_var_func == NULL) {
129533affcbSRobert Mustacchi *act = disc->nld_alloc_len;
130533affcbSRobert Mustacchi }
131533affcbSRobert Mustacchi
132533affcbSRobert Mustacchi return (disc->nld_var_func(act, buf, buflen));
133533affcbSRobert Mustacchi }
134533affcbSRobert Mustacchi
135533affcbSRobert Mustacchi bool
nvme_log_disc_dup(nvme_ctrl_t * ctrl,const nvme_log_disc_t * src,nvme_log_disc_t ** discp)136533affcbSRobert Mustacchi nvme_log_disc_dup(nvme_ctrl_t *ctrl, const nvme_log_disc_t *src,
137533affcbSRobert Mustacchi nvme_log_disc_t **discp)
138533affcbSRobert Mustacchi {
139533affcbSRobert Mustacchi nvme_log_disc_t *disc;
140533affcbSRobert Mustacchi
141533affcbSRobert Mustacchi if (src == NULL) {
142533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
143533affcbSRobert Mustacchi "encountered invalid nvme_log_disc_t pointer to duplicate: "
144533affcbSRobert Mustacchi "%p", src));
145533affcbSRobert Mustacchi }
146533affcbSRobert Mustacchi
147533affcbSRobert Mustacchi if (discp == NULL) {
148533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
149533affcbSRobert Mustacchi "encountered invalid nvme_log_disc_t output pointer: %p",
150533affcbSRobert Mustacchi discp));
151533affcbSRobert Mustacchi }
152533affcbSRobert Mustacchi
153533affcbSRobert Mustacchi disc = calloc(1, sizeof (nvme_log_disc_t));
154533affcbSRobert Mustacchi if (disc == NULL) {
155533affcbSRobert Mustacchi int e = errno;
156533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
157533affcbSRobert Mustacchi "allocate memory for a new nvme_log_disc_t: %s",
158533affcbSRobert Mustacchi strerror(e)));
159533affcbSRobert Mustacchi }
160533affcbSRobert Mustacchi
161533affcbSRobert Mustacchi (void) memcpy(disc, src, sizeof (nvme_log_disc_t));
162533affcbSRobert Mustacchi *discp = disc;
163533affcbSRobert Mustacchi return (nvme_ctrl_success(ctrl));
164533affcbSRobert Mustacchi }
165533affcbSRobert Mustacchi
166533affcbSRobert Mustacchi /*
167533affcbSRobert Mustacchi * Log Page Discovery logic
168533affcbSRobert Mustacchi */
169533affcbSRobert Mustacchi static bool
nvme_log_discover_validate(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags)170533affcbSRobert Mustacchi nvme_log_discover_validate(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes,
171533affcbSRobert Mustacchi uint32_t flags)
172533affcbSRobert Mustacchi {
173533affcbSRobert Mustacchi const nvme_log_disc_scope_t valid_scopes = NVME_LOG_SCOPE_CTRL |
174533affcbSRobert Mustacchi NVME_LOG_SCOPE_NVM | NVME_LOG_SCOPE_NS;
175533affcbSRobert Mustacchi
176533affcbSRobert Mustacchi /*
177533affcbSRobert Mustacchi * For now require an explicit scope. Perhaps 0 should be an alias for
178533affcbSRobert Mustacchi * allow all. That means if something gets added no one has to update to
179533affcbSRobert Mustacchi * get new things, but on the other hand that means they might see
180533affcbSRobert Mustacchi * unexpected scopes.
181533affcbSRobert Mustacchi */
182533affcbSRobert Mustacchi if (scopes == 0) {
183533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, "no log "
184533affcbSRobert Mustacchi "scope specified (given 0), a scope must be requested"));
185533affcbSRobert Mustacchi }
186533affcbSRobert Mustacchi
187533affcbSRobert Mustacchi if ((scopes & ~valid_scopes) != 0) {
188533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
189533affcbSRobert Mustacchi "encountered invalid scope for the nvme_log_disc_scope_t: "
190533affcbSRobert Mustacchi "0x%x", scopes & ~valid_scopes));
191533affcbSRobert Mustacchi }
192533affcbSRobert Mustacchi
193533affcbSRobert Mustacchi if (flags != 0) {
194533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
195533affcbSRobert Mustacchi "encountered invalid log discovery flags: 0x%x", flags));
196533affcbSRobert Mustacchi }
197533affcbSRobert Mustacchi
198533affcbSRobert Mustacchi return (true);
199533affcbSRobert Mustacchi }
200533affcbSRobert Mustacchi
201533affcbSRobert Mustacchi void
nvme_log_discover_fini(nvme_log_iter_t * iter)202533affcbSRobert Mustacchi nvme_log_discover_fini(nvme_log_iter_t *iter)
203533affcbSRobert Mustacchi {
204533affcbSRobert Mustacchi free(iter);
205533affcbSRobert Mustacchi }
206533affcbSRobert Mustacchi
207533affcbSRobert Mustacchi static bool
nvme_log_discover_one(nvme_log_iter_t * iter,const nvme_log_page_info_t * info)208533affcbSRobert Mustacchi nvme_log_discover_one(nvme_log_iter_t *iter, const nvme_log_page_info_t *info)
209533affcbSRobert Mustacchi {
210533affcbSRobert Mustacchi bool var;
211533affcbSRobert Mustacchi nvme_log_disc_t *disc = &iter->nli_nld;
212533affcbSRobert Mustacchi nvme_log_disc_scope_t scope;
213533affcbSRobert Mustacchi nvme_valid_ctrl_data_t data;
214533affcbSRobert Mustacchi
215533affcbSRobert Mustacchi data.vcd_vers = &iter->nli_ctrl->nc_vers;
216533affcbSRobert Mustacchi data.vcd_id = &iter->nli_ctrl->nc_info;
217533affcbSRobert Mustacchi
218533affcbSRobert Mustacchi /*
219533affcbSRobert Mustacchi * Determine the scope of the log page so we can understand if the user
220533affcbSRobert Mustacchi * cares about this or not.
221533affcbSRobert Mustacchi */
222533affcbSRobert Mustacchi scope = nvme_log_page_info_scope(info, &data);
223533affcbSRobert Mustacchi if ((iter->nli_scope & scope) == 0) {
224533affcbSRobert Mustacchi return (false);
225533affcbSRobert Mustacchi }
226533affcbSRobert Mustacchi
227533affcbSRobert Mustacchi (void) memset(disc, 0, sizeof (nvme_log_disc_t));
228533affcbSRobert Mustacchi
229533affcbSRobert Mustacchi /*
230533affcbSRobert Mustacchi * Now that we know that this applies, fill in the remaining information
231533affcbSRobert Mustacchi * that we need.
232533affcbSRobert Mustacchi */
233533affcbSRobert Mustacchi disc->nld_short = info->nlpi_short;
234533affcbSRobert Mustacchi disc->nld_desc = info->nlpi_human;
235533affcbSRobert Mustacchi disc->nld_lid = info->nlpi_lid;
236533affcbSRobert Mustacchi disc->nld_csi = info->nlpi_csi;
237533affcbSRobert Mustacchi disc->nld_kind = info->nlpi_kind;
238533affcbSRobert Mustacchi disc->nld_srcs = info->nlpi_source;
239533affcbSRobert Mustacchi disc->nld_scope = scope;
240533affcbSRobert Mustacchi disc->nld_fields = info->nlpi_disc;
241533affcbSRobert Mustacchi
242533affcbSRobert Mustacchi disc->nld_alloc_len = nvme_log_page_info_size(info, &data, &var);
243533affcbSRobert Mustacchi if (disc->nld_alloc_len != 0) {
244533affcbSRobert Mustacchi if (var) {
245533affcbSRobert Mustacchi disc->nld_var_func = info->nlpi_var_func;
246533affcbSRobert Mustacchi disc->nld_size_kind = NVME_LOG_SIZE_K_VAR;
247533affcbSRobert Mustacchi } else {
248533affcbSRobert Mustacchi disc->nld_size_kind = NVME_LOG_SIZE_K_FIXED;
249533affcbSRobert Mustacchi }
250533affcbSRobert Mustacchi } else {
251533affcbSRobert Mustacchi disc->nld_size_kind = NVME_LOG_SIZE_K_UNKNOWN;
252533affcbSRobert Mustacchi disc->nld_alloc_len = NVME_LOG_MAX_SIZE;
253533affcbSRobert Mustacchi }
254533affcbSRobert Mustacchi
255533affcbSRobert Mustacchi if (nvme_log_page_info_supported(info, &data)) {
256533affcbSRobert Mustacchi disc->nld_flags |= NVME_LOG_DISC_F_IMPL;
257533affcbSRobert Mustacchi }
258533affcbSRobert Mustacchi
259533affcbSRobert Mustacchi return (true);
260533affcbSRobert Mustacchi }
261533affcbSRobert Mustacchi
262533affcbSRobert Mustacchi nvme_iter_t
nvme_log_discover_step(nvme_log_iter_t * iter,const nvme_log_disc_t ** outp)263533affcbSRobert Mustacchi nvme_log_discover_step(nvme_log_iter_t *iter, const nvme_log_disc_t **outp)
264533affcbSRobert Mustacchi {
265533affcbSRobert Mustacchi *outp = NULL;
266533affcbSRobert Mustacchi nvme_ctrl_t *ctrl = iter->nli_ctrl;
267533affcbSRobert Mustacchi
268533affcbSRobert Mustacchi if (iter->nli_std_done && iter->nli_vs_done) {
269533affcbSRobert Mustacchi return (NVME_ITER_DONE);
270533affcbSRobert Mustacchi }
271533affcbSRobert Mustacchi
272533affcbSRobert Mustacchi /*
273533affcbSRobert Mustacchi * We start by walking the list of spec pages and then check the device
274533affcbSRobert Mustacchi * specific ones. In the NVMe 2.x era or when we have support for a
275533affcbSRobert Mustacchi * vendor-specific method for getting supported log pages then we should
276533affcbSRobert Mustacchi * prefer executing that and using that info to provide information
277533affcbSRobert Mustacchi * where possible.
278533affcbSRobert Mustacchi */
279533affcbSRobert Mustacchi if (!iter->nli_std_done) {
280533affcbSRobert Mustacchi while (iter->nli_cur_idx < nvme_std_log_npages) {
281533affcbSRobert Mustacchi const nvme_log_page_info_t *info =
282533affcbSRobert Mustacchi &nvme_std_log_pages[iter->nli_cur_idx];
283533affcbSRobert Mustacchi iter->nli_cur_idx++;
284533affcbSRobert Mustacchi if (nvme_log_discover_one(iter, info)) {
285533affcbSRobert Mustacchi *outp = &iter->nli_nld;
286533affcbSRobert Mustacchi return (NVME_ITER_VALID);
287533affcbSRobert Mustacchi }
288533affcbSRobert Mustacchi }
289533affcbSRobert Mustacchi iter->nli_std_done = true;
290533affcbSRobert Mustacchi iter->nli_cur_idx = 0;
291533affcbSRobert Mustacchi }
292533affcbSRobert Mustacchi
293533affcbSRobert Mustacchi if (ctrl->nc_vsd == NULL) {
294533affcbSRobert Mustacchi iter->nli_vs_done = true;
295533affcbSRobert Mustacchi return (NVME_ITER_DONE);
296533affcbSRobert Mustacchi }
297533affcbSRobert Mustacchi
298533affcbSRobert Mustacchi while (iter->nli_cur_idx < ctrl->nc_vsd->nvd_nlogs) {
299533affcbSRobert Mustacchi const nvme_log_page_info_t *info =
300533affcbSRobert Mustacchi &ctrl->nc_vsd->nvd_logs[iter->nli_cur_idx];
301533affcbSRobert Mustacchi iter->nli_cur_idx++;
302533affcbSRobert Mustacchi if (nvme_log_discover_one(iter, info)) {
303533affcbSRobert Mustacchi *outp = &iter->nli_nld;
304533affcbSRobert Mustacchi return (NVME_ITER_VALID);
305533affcbSRobert Mustacchi }
306533affcbSRobert Mustacchi }
307533affcbSRobert Mustacchi
308533affcbSRobert Mustacchi iter->nli_vs_done = true;
309533affcbSRobert Mustacchi iter->nli_cur_idx = 0;
310533affcbSRobert Mustacchi return (NVME_ITER_DONE);
311533affcbSRobert Mustacchi }
312533affcbSRobert Mustacchi
313533affcbSRobert Mustacchi bool
nvme_log_discover_init(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags,nvme_log_iter_t ** iterp)314533affcbSRobert Mustacchi nvme_log_discover_init(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes,
315533affcbSRobert Mustacchi uint32_t flags, nvme_log_iter_t **iterp)
316533affcbSRobert Mustacchi {
317533affcbSRobert Mustacchi nvme_log_iter_t *iter;
318533affcbSRobert Mustacchi
319533affcbSRobert Mustacchi if (!nvme_log_discover_validate(ctrl, scopes, flags)) {
320533affcbSRobert Mustacchi return (false);
321533affcbSRobert Mustacchi }
322533affcbSRobert Mustacchi
323533affcbSRobert Mustacchi if (iterp == NULL) {
324533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
325533affcbSRobert Mustacchi "encountered invalid nvme_log_iter_t output pointer: %p",
326533affcbSRobert Mustacchi iterp));
327533affcbSRobert Mustacchi }
328533affcbSRobert Mustacchi
329533affcbSRobert Mustacchi iter = calloc(1, sizeof (nvme_log_iter_t));
330533affcbSRobert Mustacchi if (iter == NULL) {
331533affcbSRobert Mustacchi int e = errno;
332533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
333533affcbSRobert Mustacchi "allocate memory for a new nvme_log_iter_t: %s",
334533affcbSRobert Mustacchi strerror(e)));
335533affcbSRobert Mustacchi }
336533affcbSRobert Mustacchi
337533affcbSRobert Mustacchi iter->nli_ctrl = ctrl;
338533affcbSRobert Mustacchi iter->nli_scope = scopes;
339533affcbSRobert Mustacchi
340533affcbSRobert Mustacchi *iterp = iter;
341533affcbSRobert Mustacchi return (nvme_ctrl_success(ctrl));
342533affcbSRobert Mustacchi }
343533affcbSRobert Mustacchi
344533affcbSRobert Mustacchi /*
345533affcbSRobert Mustacchi * Walk all of the requested log pages that match and fill out the information
346533affcbSRobert Mustacchi * for the discovery form.
347533affcbSRobert Mustacchi */
348533affcbSRobert Mustacchi bool
nvme_log_discover(nvme_ctrl_t * ctrl,nvme_log_disc_scope_t scopes,uint32_t flags,nvme_log_disc_f func,void * arg)349533affcbSRobert Mustacchi nvme_log_discover(nvme_ctrl_t *ctrl, nvme_log_disc_scope_t scopes,
350533affcbSRobert Mustacchi uint32_t flags, nvme_log_disc_f func, void *arg)
351533affcbSRobert Mustacchi {
352533affcbSRobert Mustacchi nvme_log_iter_t *iter;
353533affcbSRobert Mustacchi nvme_iter_t ret;
354533affcbSRobert Mustacchi const nvme_log_disc_t *disc;
355533affcbSRobert Mustacchi
356533affcbSRobert Mustacchi if (func == NULL) {
357533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
358533affcbSRobert Mustacchi "encountered invalid nvme_log_disc_f function pointer: %p",
359533affcbSRobert Mustacchi func));
360533affcbSRobert Mustacchi }
361533affcbSRobert Mustacchi
362533affcbSRobert Mustacchi if (!nvme_log_discover_init(ctrl, scopes, flags, &iter)) {
363533affcbSRobert Mustacchi return (false);
364533affcbSRobert Mustacchi }
365533affcbSRobert Mustacchi
366533affcbSRobert Mustacchi while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) {
367533affcbSRobert Mustacchi if (!func(ctrl, disc, arg))
368533affcbSRobert Mustacchi break;
369533affcbSRobert Mustacchi }
370533affcbSRobert Mustacchi
371533affcbSRobert Mustacchi nvme_log_discover_fini(iter);
372533affcbSRobert Mustacchi if (ret == NVME_ITER_ERROR) {
373533affcbSRobert Mustacchi return (false);
374533affcbSRobert Mustacchi }
375533affcbSRobert Mustacchi
376533affcbSRobert Mustacchi return (nvme_ctrl_success(ctrl));
377533affcbSRobert Mustacchi }
378533affcbSRobert Mustacchi
379533affcbSRobert Mustacchi
380533affcbSRobert Mustacchi void
nvme_log_req_fini(nvme_log_req_t * req)381533affcbSRobert Mustacchi nvme_log_req_fini(nvme_log_req_t *req)
382533affcbSRobert Mustacchi {
383533affcbSRobert Mustacchi free(req);
384533affcbSRobert Mustacchi }
385533affcbSRobert Mustacchi
386533affcbSRobert Mustacchi /*
387533affcbSRobert Mustacchi * This is the totally manual path that occurs. When this is used, we require
388533affcbSRobert Mustacchi * that people specify a subset of the fields here, primarily just the actual
389533affcbSRobert Mustacchi * log page, output, and CSI. We don't try to be clever here and use the
390533affcbSRobert Mustacchi * discovery information to know what to set. That's reserved for creating this
391533affcbSRobert Mustacchi * request based upon discovery information.
392533affcbSRobert Mustacchi */
393533affcbSRobert Mustacchi bool
nvme_log_req_init(nvme_ctrl_t * ctrl,nvme_log_req_t ** reqp)394533affcbSRobert Mustacchi nvme_log_req_init(nvme_ctrl_t *ctrl, nvme_log_req_t **reqp)
395533affcbSRobert Mustacchi {
396533affcbSRobert Mustacchi nvme_log_req_t *req;
397533affcbSRobert Mustacchi
398533affcbSRobert Mustacchi if (reqp == NULL) {
399533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
400533affcbSRobert Mustacchi "encountered invalid nvme_log_req_t output pointer: %p",
401533affcbSRobert Mustacchi reqp));
402533affcbSRobert Mustacchi }
403533affcbSRobert Mustacchi
404533affcbSRobert Mustacchi req = calloc(1, sizeof (nvme_log_req_t));
405533affcbSRobert Mustacchi if (req == NULL) {
406533affcbSRobert Mustacchi int e = errno;
407533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
408533affcbSRobert Mustacchi "allocate memory for a new nvme_log_req_t: %s",
409533affcbSRobert Mustacchi strerror(e)));
410533affcbSRobert Mustacchi }
411533affcbSRobert Mustacchi
412533affcbSRobert Mustacchi req->nlr_ctrl = ctrl;
413533affcbSRobert Mustacchi for (size_t i = 0; i < nvme_log_nfields; i++) {
414533affcbSRobert Mustacchi if (nvme_log_fields[i].nlfi_def_req) {
415533affcbSRobert Mustacchi req->nlr_need |= 1 << i;
416533affcbSRobert Mustacchi }
417533affcbSRobert Mustacchi
418533affcbSRobert Mustacchi if (nvme_log_fields[i].nlfi_def_allow) {
419533affcbSRobert Mustacchi req->nlr_allow |= 1 << i;
420533affcbSRobert Mustacchi }
421533affcbSRobert Mustacchi }
422533affcbSRobert Mustacchi
423533affcbSRobert Mustacchi /*
424533affcbSRobert Mustacchi * Because we don't know anything about this log request, indicate that
425533affcbSRobert Mustacchi * if we're given the all namespaces nsid that's fine. We'll still
426533affcbSRobert Mustacchi * check the controller version when this is set first.
427533affcbSRobert Mustacchi */
428533affcbSRobert Mustacchi req->nlr_flags |= NVME_LOG_REQ_F_BCAST_NS_OK;
429533affcbSRobert Mustacchi
430533affcbSRobert Mustacchi *reqp = req;
431533affcbSRobert Mustacchi return (nvme_ctrl_success(ctrl));
432533affcbSRobert Mustacchi }
433533affcbSRobert Mustacchi
434533affcbSRobert Mustacchi bool
nvme_log_req_init_by_disc(nvme_ctrl_t * ctrl,const nvme_log_disc_t * disc,nvme_log_req_t ** reqp)435533affcbSRobert Mustacchi nvme_log_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_log_disc_t *disc,
436533affcbSRobert Mustacchi nvme_log_req_t **reqp)
437533affcbSRobert Mustacchi {
438533affcbSRobert Mustacchi nvme_log_req_t *req;
439533affcbSRobert Mustacchi
440533affcbSRobert Mustacchi if (disc == NULL) {
441533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
442533affcbSRobert Mustacchi "encountered invalid nvme_log_disc_t pointer: %p", disc));
443533affcbSRobert Mustacchi }
444533affcbSRobert Mustacchi
445533affcbSRobert Mustacchi if (reqp == NULL) {
446533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
447533affcbSRobert Mustacchi "encountered invalid nvme_log_req_t output pointer: %p",
448533affcbSRobert Mustacchi reqp));
449533affcbSRobert Mustacchi }
450533affcbSRobert Mustacchi
451533affcbSRobert Mustacchi if ((disc->nld_flags & NVME_LOG_DISC_F_IMPL) == 0) {
452533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_LOG_UNSUP_BY_DEV, 0,
453533affcbSRobert Mustacchi "cannot create log request for log %s (CSI/LID 0x%x/0x%x) "
454533affcbSRobert Mustacchi "because it is not supported by the device",
455533affcbSRobert Mustacchi disc->nld_short, disc->nld_csi, disc->nld_lid));
456533affcbSRobert Mustacchi }
457533affcbSRobert Mustacchi
458533affcbSRobert Mustacchi req = calloc(1, sizeof (nvme_log_req_t));
459533affcbSRobert Mustacchi if (req == NULL) {
460533affcbSRobert Mustacchi int e = errno;
461533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
462533affcbSRobert Mustacchi "allocate memory for a new nvme_log_req_t: %s",
463533affcbSRobert Mustacchi strerror(e)));
464533affcbSRobert Mustacchi }
465533affcbSRobert Mustacchi
466533affcbSRobert Mustacchi req->nlr_ctrl = ctrl;
467533affcbSRobert Mustacchi req->nlr_lid = disc->nld_lid;
468533affcbSRobert Mustacchi req->nlr_csi = disc->nld_csi;
469533affcbSRobert Mustacchi
470533affcbSRobert Mustacchi /*
471533affcbSRobert Mustacchi * Setting the size is always required here, because this is how we
472533affcbSRobert Mustacchi * track that the output pointer is actually set. We will always allow
473533affcbSRobert Mustacchi * setting the offset though it's possible the controller won't support
474533affcbSRobert Mustacchi * that.
475533affcbSRobert Mustacchi */
476533affcbSRobert Mustacchi req->nlr_need = req->nlr_allow = 1 << NVME_LOG_REQ_FIELD_SIZE;
477533affcbSRobert Mustacchi req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_OFFSET;
478533affcbSRobert Mustacchi
479533affcbSRobert Mustacchi /*
480533affcbSRobert Mustacchi * Initialize our needed and allowed fields. Because we have the actual
481533affcbSRobert Mustacchi * lid/csi from the above, we don't allow the user to overwrite them at
482533affcbSRobert Mustacchi * all. For the LSP and LSI, right now these are all our nothing, but
483533affcbSRobert Mustacchi * this may break. RAE is a bit special and discussed below.
484533affcbSRobert Mustacchi */
485533affcbSRobert Mustacchi if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_LSP) != 0) {
486533affcbSRobert Mustacchi req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_LSP;
487533affcbSRobert Mustacchi req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_LSP;
488533affcbSRobert Mustacchi }
489533affcbSRobert Mustacchi
490533affcbSRobert Mustacchi if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_LSI) != 0) {
491533affcbSRobert Mustacchi req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_LSI;
492533affcbSRobert Mustacchi req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_LSI;
493533affcbSRobert Mustacchi }
494533affcbSRobert Mustacchi
495533affcbSRobert Mustacchi /*
496533affcbSRobert Mustacchi * Because RAE wasn't added until NVMe 1.3, we can't do much with it
497533affcbSRobert Mustacchi * before that. However, once it's here we definitely want to default to
498533affcbSRobert Mustacchi * setting it by default so that way we can minimize the chance that
499533affcbSRobert Mustacchi * we'll steal an alert that the kernel needs to read and acknowledge.
500533affcbSRobert Mustacchi */
501533affcbSRobert Mustacchi if ((disc->nld_fields & NVME_LOG_DISC_F_NEED_RAE) != 0 &&
502533affcbSRobert Mustacchi nvme_vers_ctrl_atleast(ctrl,
503533affcbSRobert Mustacchi nvme_log_fields[NVME_LOG_REQ_FIELD_RAE].nlfi_vers)) {
504533affcbSRobert Mustacchi req->nlr_flags |= NVME_LOG_REQ_F_RAE;
505533affcbSRobert Mustacchi req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_RAE;
506533affcbSRobert Mustacchi }
507533affcbSRobert Mustacchi
508533affcbSRobert Mustacchi /*
509533affcbSRobert Mustacchi * Check the log page scope setting. If the log is said to be namespace
510533affcbSRobert Mustacchi * scoped, then we'll allow the namespace to be specified. If it
511533affcbSRobert Mustacchi * supports a different scope as well, then we'll default to the
512533affcbSRobert Mustacchi * controller scope and this field is optional. Otherwise, it'll be
513533affcbSRobert Mustacchi * required and it will be a mandatory field.
514533affcbSRobert Mustacchi */
515533affcbSRobert Mustacchi if ((disc->nld_scope & NVME_LOG_SCOPE_NS) != 0) {
516533affcbSRobert Mustacchi req->nlr_allow |= 1 << NVME_LOG_REQ_FIELD_NSID;
517533affcbSRobert Mustacchi if ((disc->nld_scope & ~NVME_LOG_SCOPE_NS) != 0) {
518533affcbSRobert Mustacchi req->nlr_flags |= NVME_LOG_REQ_F_BCAST_NS_OK;
519533affcbSRobert Mustacchi req->nlr_nsid = NVME_NSID_BCAST;
520533affcbSRobert Mustacchi } else {
521533affcbSRobert Mustacchi req->nlr_need |= 1 << NVME_LOG_REQ_FIELD_NSID;
522533affcbSRobert Mustacchi }
523533affcbSRobert Mustacchi }
524533affcbSRobert Mustacchi
525533affcbSRobert Mustacchi *reqp = req;
526533affcbSRobert Mustacchi return (nvme_ctrl_success(ctrl));
527533affcbSRobert Mustacchi }
528533affcbSRobert Mustacchi
529533affcbSRobert Mustacchi typedef struct {
530533affcbSRobert Mustacchi bool nlia_found;
531533affcbSRobert Mustacchi const char *nlia_name;
532533affcbSRobert Mustacchi nvme_log_req_t *nlia_req;
533533affcbSRobert Mustacchi nvme_log_disc_t **nlia_discp;
534533affcbSRobert Mustacchi nvme_err_data_t nlia_err;
535533affcbSRobert Mustacchi } nvme_log_init_arg_t;
536533affcbSRobert Mustacchi
537533affcbSRobert Mustacchi static bool
nvme_log_req_init_by_name_cb(nvme_ctrl_t * ctrl,const nvme_log_disc_t * disc,void * arg)538533affcbSRobert Mustacchi nvme_log_req_init_by_name_cb(nvme_ctrl_t *ctrl, const nvme_log_disc_t *disc,
539533affcbSRobert Mustacchi void *arg)
540533affcbSRobert Mustacchi {
541533affcbSRobert Mustacchi nvme_log_init_arg_t *init = arg;
542533affcbSRobert Mustacchi
543533affcbSRobert Mustacchi if (strcmp(init->nlia_name, disc->nld_short) != 0) {
544533affcbSRobert Mustacchi return (true);
545533affcbSRobert Mustacchi }
546533affcbSRobert Mustacchi
547533affcbSRobert Mustacchi init->nlia_found = true;
548533affcbSRobert Mustacchi if (!nvme_log_req_init_by_disc(ctrl, disc, &init->nlia_req)) {
549533affcbSRobert Mustacchi nvme_ctrl_err_save(ctrl, &init->nlia_err);
550533affcbSRobert Mustacchi init->nlia_req = NULL;
551533affcbSRobert Mustacchi } else if (init->nlia_discp != NULL) {
552533affcbSRobert Mustacchi if (!nvme_log_disc_dup(ctrl, disc, init->nlia_discp)) {
553533affcbSRobert Mustacchi nvme_ctrl_err_save(ctrl, &init->nlia_err);
554533affcbSRobert Mustacchi nvme_log_req_fini(init->nlia_req);
555533affcbSRobert Mustacchi init->nlia_req = NULL;
556533affcbSRobert Mustacchi }
557533affcbSRobert Mustacchi }
558533affcbSRobert Mustacchi
559533affcbSRobert Mustacchi return (false);
560533affcbSRobert Mustacchi }
561533affcbSRobert Mustacchi
562533affcbSRobert Mustacchi 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)563533affcbSRobert Mustacchi nvme_log_req_init_by_name(nvme_ctrl_t *ctrl, const char *name, uint32_t flags,
564533affcbSRobert Mustacchi nvme_log_disc_t **discp, nvme_log_req_t **reqp)
565533affcbSRobert Mustacchi {
566533affcbSRobert Mustacchi nvme_log_init_arg_t init;
567533affcbSRobert Mustacchi
568533affcbSRobert Mustacchi /*
569533affcbSRobert Mustacchi * We consider discp an optional argument and therefore do not check it
570533affcbSRobert Mustacchi * unlike name and reqp.
571533affcbSRobert Mustacchi */
572533affcbSRobert Mustacchi if (reqp == NULL) {
573533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
574533affcbSRobert Mustacchi "encountered invalid nvme_log_req_t output pointer: %p",
575533affcbSRobert Mustacchi reqp));
576533affcbSRobert Mustacchi }
577533affcbSRobert Mustacchi
578533affcbSRobert Mustacchi if (name == NULL) {
579533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
580533affcbSRobert Mustacchi "encountered invalid pointer for log page name: %p", name));
581533affcbSRobert Mustacchi }
582533affcbSRobert Mustacchi
583533affcbSRobert Mustacchi (void) memset(&init, 0, sizeof (init));
584533affcbSRobert Mustacchi init.nlia_name = name;
585533affcbSRobert Mustacchi init.nlia_discp = discp;
586533affcbSRobert Mustacchi
587533affcbSRobert Mustacchi if (!nvme_log_discover(ctrl, NVME_LOG_SCOPE_CTRL |
588533affcbSRobert Mustacchi NVME_LOG_SCOPE_NVM | NVME_LOG_SCOPE_NS, flags,
589533affcbSRobert Mustacchi nvme_log_req_init_by_name_cb, &init)) {
590533affcbSRobert Mustacchi return (false);
591533affcbSRobert Mustacchi }
592533affcbSRobert Mustacchi
593533affcbSRobert Mustacchi if (!init.nlia_found) {
594533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_LOG_NAME_UNKNOWN, 0,
595533affcbSRobert Mustacchi "failed to find log page with name %s", name));
596533affcbSRobert Mustacchi }
597533affcbSRobert Mustacchi
598533affcbSRobert Mustacchi /*
599533affcbSRobert Mustacchi * If we failed to create the request, but we did find it, then that
600533affcbSRobert Mustacchi * means something went wrong and we can go ahead and already return an
601533affcbSRobert Mustacchi * error.
602533affcbSRobert Mustacchi */
603533affcbSRobert Mustacchi if (init.nlia_req == NULL) {
604533affcbSRobert Mustacchi nvme_ctrl_err_set(ctrl, &init.nlia_err);
605533affcbSRobert Mustacchi return (false);
606533affcbSRobert Mustacchi }
607533affcbSRobert Mustacchi
608533affcbSRobert Mustacchi *reqp = init.nlia_req;
609533affcbSRobert Mustacchi return (nvme_ctrl_success(ctrl));
610533affcbSRobert Mustacchi }
611533affcbSRobert Mustacchi
612533affcbSRobert Mustacchi static void
nvme_log_req_clear_need(nvme_log_req_t * req,nvme_log_req_field_t field)613533affcbSRobert Mustacchi nvme_log_req_clear_need(nvme_log_req_t *req, nvme_log_req_field_t field)
614533affcbSRobert Mustacchi {
615533affcbSRobert Mustacchi req->nlr_need &= ~(1 << field);
616533affcbSRobert Mustacchi }
617533affcbSRobert Mustacchi
618533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_lid = {
619533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_LID,
620533affcbSRobert Mustacchi NVME_ERR_LOG_LID_RANGE, 0, 0
621533affcbSRobert Mustacchi };
622533affcbSRobert Mustacchi
623533affcbSRobert Mustacchi bool
nvme_log_req_set_lid(nvme_log_req_t * req,uint32_t lid)624533affcbSRobert Mustacchi nvme_log_req_set_lid(nvme_log_req_t *req, uint32_t lid)
625533affcbSRobert Mustacchi {
626533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, lid, "get log page",
627533affcbSRobert Mustacchi &nvme_log_check_lid, req->nlr_allow)) {
628533affcbSRobert Mustacchi return (false);
629533affcbSRobert Mustacchi }
630533affcbSRobert Mustacchi
631533affcbSRobert Mustacchi req->nlr_lid = lid;
632533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LID);
633533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
634533affcbSRobert Mustacchi }
635533affcbSRobert Mustacchi
636533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_lsp = {
637533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_LSP,
638533affcbSRobert Mustacchi NVME_ERR_LOG_LSP_RANGE, NVME_ERR_LOG_LSP_UNSUP,
639533affcbSRobert Mustacchi NVME_ERR_LOG_LSP_UNUSE
640533affcbSRobert Mustacchi };
641533affcbSRobert Mustacchi
642533affcbSRobert Mustacchi bool
nvme_log_req_set_lsp(nvme_log_req_t * req,uint32_t lsp)643533affcbSRobert Mustacchi nvme_log_req_set_lsp(nvme_log_req_t *req, uint32_t lsp)
644533affcbSRobert Mustacchi {
645533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, lsp, "get log page",
646533affcbSRobert Mustacchi &nvme_log_check_lsp, req->nlr_allow)) {
647533affcbSRobert Mustacchi return (false);
648533affcbSRobert Mustacchi }
649533affcbSRobert Mustacchi
650533affcbSRobert Mustacchi req->nlr_lsp = lsp;
651533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSP);
652533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
653533affcbSRobert Mustacchi }
654533affcbSRobert Mustacchi
655533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_lsi = {
656533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_LSI,
657533affcbSRobert Mustacchi NVME_ERR_LOG_LSI_RANGE, NVME_ERR_LOG_LSI_UNSUP,
658533affcbSRobert Mustacchi NVME_ERR_LOG_LSI_UNUSE
659533affcbSRobert Mustacchi };
660533affcbSRobert Mustacchi
661533affcbSRobert Mustacchi bool
nvme_log_req_set_lsi(nvme_log_req_t * req,uint32_t lsi)662533affcbSRobert Mustacchi nvme_log_req_set_lsi(nvme_log_req_t *req, uint32_t lsi)
663533affcbSRobert Mustacchi {
664533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, lsi, "get log page",
665533affcbSRobert Mustacchi &nvme_log_check_lsi, req->nlr_allow)) {
666533affcbSRobert Mustacchi return (false);
667533affcbSRobert Mustacchi }
668533affcbSRobert Mustacchi
669533affcbSRobert Mustacchi req->nlr_lsi = lsi;
670533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_LSI);
671533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
672533affcbSRobert Mustacchi }
673533affcbSRobert Mustacchi
674533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_csi = {
675533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_CSI,
676533affcbSRobert Mustacchi NVME_ERR_LOG_CSI_RANGE, NVME_ERR_LOG_CSI_UNSUP, 0
677533affcbSRobert Mustacchi };
678533affcbSRobert Mustacchi
679533affcbSRobert Mustacchi bool
nvme_log_req_set_csi(nvme_log_req_t * req,nvme_csi_t csi)680*9d9f9432SToomas Soome nvme_log_req_set_csi(nvme_log_req_t *req, nvme_csi_t csi)
681533affcbSRobert Mustacchi {
682533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, csi, "get log page",
683533affcbSRobert Mustacchi &nvme_log_check_csi, req->nlr_allow)) {
684533affcbSRobert Mustacchi return (false);
685533affcbSRobert Mustacchi }
686533affcbSRobert Mustacchi
687533affcbSRobert Mustacchi req->nlr_csi = csi;
688533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_CSI);
689533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
690533affcbSRobert Mustacchi }
691533affcbSRobert Mustacchi
692533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_size = {
693533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_SIZE,
694533affcbSRobert Mustacchi NVME_ERR_LOG_SIZE_RANGE, 0, 0
695533affcbSRobert Mustacchi };
696533affcbSRobert Mustacchi
697533affcbSRobert Mustacchi bool
nvme_log_req_set_output(nvme_log_req_t * req,void * buf,size_t buflen)698533affcbSRobert Mustacchi nvme_log_req_set_output(nvme_log_req_t *req, void *buf, size_t buflen)
699533affcbSRobert Mustacchi {
700533affcbSRobert Mustacchi if (buf == NULL) {
701533affcbSRobert Mustacchi return (nvme_ctrl_error(req->nlr_ctrl, NVME_ERR_BAD_PTR, 0,
702533affcbSRobert Mustacchi "log request output buffer cannot be NULL"));
703533affcbSRobert Mustacchi }
704533affcbSRobert Mustacchi
705533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, buflen, "get log page",
706533affcbSRobert Mustacchi &nvme_log_check_size, req->nlr_allow)) {
707533affcbSRobert Mustacchi return (false);
708533affcbSRobert Mustacchi }
709533affcbSRobert Mustacchi
710533affcbSRobert Mustacchi req->nlr_output = buf;
711533affcbSRobert Mustacchi req->nlr_output_len = buflen;
712533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_SIZE);
713533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
714533affcbSRobert Mustacchi }
715533affcbSRobert Mustacchi
716533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_offset = {
717533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_OFFSET,
718533affcbSRobert Mustacchi NVME_ERR_LOG_OFFSET_RANGE, 0, 0
719533affcbSRobert Mustacchi };
720533affcbSRobert Mustacchi
721533affcbSRobert Mustacchi bool
nvme_log_req_set_offset(nvme_log_req_t * req,uint64_t off)722533affcbSRobert Mustacchi nvme_log_req_set_offset(nvme_log_req_t *req, uint64_t off)
723533affcbSRobert Mustacchi {
724533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, off, "get log page",
725533affcbSRobert Mustacchi &nvme_log_check_offset, req->nlr_allow)) {
726533affcbSRobert Mustacchi return (false);
727533affcbSRobert Mustacchi }
728533affcbSRobert Mustacchi
729533affcbSRobert Mustacchi req->nlr_offset = off;
730533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_OFFSET);
731533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
732533affcbSRobert Mustacchi }
733533affcbSRobert Mustacchi
734533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_nsid = {
735533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_NSID, NVME_ERR_NS_RANGE, 0, 0
736533affcbSRobert Mustacchi };
737533affcbSRobert Mustacchi
738533affcbSRobert Mustacchi bool
nvme_log_req_set_nsid(nvme_log_req_t * req,uint32_t nsid)739533affcbSRobert Mustacchi nvme_log_req_set_nsid(nvme_log_req_t *req, uint32_t nsid)
740533affcbSRobert Mustacchi {
741533affcbSRobert Mustacchi nvme_ctrl_t *ctrl = req->nlr_ctrl;
742533affcbSRobert Mustacchi
743533affcbSRobert Mustacchi if (nsid == NVME_NSID_BCAST &&
744533affcbSRobert Mustacchi (req->nlr_flags & NVME_LOG_REQ_F_BCAST_NS_OK) == 0) {
745533affcbSRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
746533affcbSRobert Mustacchi "namespaces/controller nsid (0x%x) is not allowed for this "
747533affcbSRobert Mustacchi "log page, valid namespaces are [0x%x, 0x%x]", nsid,
748533affcbSRobert Mustacchi NVME_NSID_MIN, req->nlr_ctrl->nc_info.id_nn));
749533affcbSRobert Mustacchi }
750533affcbSRobert Mustacchi
751533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, nsid, "get log page",
752533affcbSRobert Mustacchi &nvme_log_check_nsid, req->nlr_allow)) {
753533affcbSRobert Mustacchi return (false);
754533affcbSRobert Mustacchi }
755533affcbSRobert Mustacchi
756533affcbSRobert Mustacchi req->nlr_nsid = nsid;
757533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_NSID);
758533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
759533affcbSRobert Mustacchi }
760533affcbSRobert Mustacchi
761533affcbSRobert Mustacchi static const nvme_field_check_t nvme_log_check_rae = {
762533affcbSRobert Mustacchi nvme_log_fields, NVME_LOG_REQ_FIELD_RAE,
763533affcbSRobert Mustacchi NVME_ERR_LOG_RAE_RANGE, NVME_ERR_LOG_RAE_UNSUP,
764533affcbSRobert Mustacchi NVME_ERR_LOG_RAE_UNUSE
765533affcbSRobert Mustacchi };
766533affcbSRobert Mustacchi
767533affcbSRobert Mustacchi bool
nvme_log_req_set_rae(nvme_log_req_t * req,bool rae)768533affcbSRobert Mustacchi nvme_log_req_set_rae(nvme_log_req_t *req, bool rae)
769533affcbSRobert Mustacchi {
770533affcbSRobert Mustacchi if (!nvme_field_check_one(req->nlr_ctrl, rae, "get log page",
771533affcbSRobert Mustacchi &nvme_log_check_rae, req->nlr_allow)) {
772533affcbSRobert Mustacchi return (false);
773533affcbSRobert Mustacchi }
774533affcbSRobert Mustacchi
775533affcbSRobert Mustacchi if (rae) {
776533affcbSRobert Mustacchi req->nlr_flags |= NVME_LOG_REQ_F_RAE;
777533affcbSRobert Mustacchi } else {
778533affcbSRobert Mustacchi req->nlr_flags &= ~NVME_LOG_REQ_F_RAE;
779533affcbSRobert Mustacchi }
780533affcbSRobert Mustacchi nvme_log_req_clear_need(req, NVME_LOG_REQ_FIELD_RAE);
781533affcbSRobert Mustacchi return (nvme_ctrl_success(req->nlr_ctrl));
782533affcbSRobert Mustacchi }
783533affcbSRobert Mustacchi
784533affcbSRobert Mustacchi bool
nvme_log_req_exec(nvme_log_req_t * req)785533affcbSRobert Mustacchi nvme_log_req_exec(nvme_log_req_t *req)
786533affcbSRobert Mustacchi {
787533affcbSRobert Mustacchi nvme_ctrl_t *ctrl = req->nlr_ctrl;
788533affcbSRobert Mustacchi nvme_ioctl_get_logpage_t log;
789533affcbSRobert Mustacchi
790533affcbSRobert Mustacchi if (req->nlr_need != 0) {
791533affcbSRobert Mustacchi return (nvme_field_miss_err(ctrl, nvme_log_fields,
792533affcbSRobert Mustacchi nvme_log_nfields, NVME_ERR_LOG_REQ_MISSING_FIELDS,
793533affcbSRobert Mustacchi "get log page", req->nlr_need));
794533affcbSRobert Mustacchi }
795533affcbSRobert Mustacchi
796533affcbSRobert Mustacchi (void) memset(&log, 0, sizeof (nvme_ioctl_get_logpage_t));
797533affcbSRobert Mustacchi log.nigl_common.nioc_nsid = req->nlr_nsid;
798533affcbSRobert Mustacchi log.nigl_csi = req->nlr_csi;
799533affcbSRobert Mustacchi log.nigl_lid = req->nlr_lid;
800533affcbSRobert Mustacchi log.nigl_lsp = req->nlr_lsp;
801533affcbSRobert Mustacchi log.nigl_lsi = req->nlr_lsi;
802533affcbSRobert Mustacchi if ((req->nlr_flags & NVME_LOG_REQ_F_RAE) != 0) {
803533affcbSRobert Mustacchi log.nigl_rae = 1;
804533affcbSRobert Mustacchi }
805533affcbSRobert Mustacchi log.nigl_len = req->nlr_output_len;
806533affcbSRobert Mustacchi log.nigl_offset = req->nlr_offset;
807533affcbSRobert Mustacchi log.nigl_data = (uintptr_t)req->nlr_output;
808533affcbSRobert Mustacchi
809533affcbSRobert Mustacchi if (ioctl(ctrl->nc_fd, NVME_IOC_GET_LOGPAGE, &log) != 0) {
810533affcbSRobert Mustacchi int e = errno;
811533affcbSRobert Mustacchi return (nvme_ioctl_syserror(ctrl, e, "get log page"));
812533affcbSRobert Mustacchi }
813533affcbSRobert Mustacchi
814533affcbSRobert Mustacchi if (log.nigl_common.nioc_drv_err != NVME_IOCTL_E_OK) {
815533affcbSRobert Mustacchi return (nvme_ioctl_error(ctrl, &log.nigl_common,
816533affcbSRobert Mustacchi "get log page"));
817533affcbSRobert Mustacchi }
818533affcbSRobert Mustacchi
819533affcbSRobert Mustacchi return (nvme_ctrl_success(ctrl));
820533affcbSRobert Mustacchi }
821