xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_feature.c (revision 1c02c6c85edfeb48df1fe18511a8779bf9d6c6ed)
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  * Manage NVMe feature detection and feature queries.
18  */
19 
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 #include "libnvme_impl.h"
25 
26 const char *
nvme_feat_disc_short(const nvme_feat_disc_t * disc)27 nvme_feat_disc_short(const nvme_feat_disc_t *disc)
28 {
29 	return (disc->nfd_short);
30 }
31 
32 const char *
nvme_feat_disc_spec(const nvme_feat_disc_t * disc)33 nvme_feat_disc_spec(const nvme_feat_disc_t *disc)
34 {
35 	return (disc->nfd_spec);
36 }
37 
38 uint32_t
nvme_feat_disc_fid(const nvme_feat_disc_t * disc)39 nvme_feat_disc_fid(const nvme_feat_disc_t *disc)
40 {
41 	return (disc->nfd_fid);
42 }
43 
44 nvme_feat_scope_t
nvme_feat_disc_scope(const nvme_feat_disc_t * disc)45 nvme_feat_disc_scope(const nvme_feat_disc_t *disc)
46 {
47 	return (disc->nfd_scope);
48 }
49 
50 nvme_feat_kind_t
nvme_feat_disc_kind(const nvme_feat_disc_t * disc)51 nvme_feat_disc_kind(const nvme_feat_disc_t *disc)
52 {
53 	return (disc->nfd_kind);
54 }
55 
56 nvme_feat_csi_t
nvme_feat_disc_csi(const nvme_feat_disc_t * disc)57 nvme_feat_disc_csi(const nvme_feat_disc_t *disc)
58 {
59 	return (disc->nfd_csi);
60 }
61 
62 nvme_feat_flags_t
nvme_feat_disc_flags(const nvme_feat_disc_t * disc)63 nvme_feat_disc_flags(const nvme_feat_disc_t *disc)
64 {
65 	return (disc->nfd_flags);
66 }
67 
68 nvme_get_feat_fields_t
nvme_feat_disc_fields_get(const nvme_feat_disc_t * disc)69 nvme_feat_disc_fields_get(const nvme_feat_disc_t *disc)
70 {
71 	return (disc->nfd_in_get);
72 }
73 
74 nvme_set_feat_fields_t
nvme_feat_disc_fields_set(const nvme_feat_disc_t * disc)75 nvme_feat_disc_fields_set(const nvme_feat_disc_t *disc)
76 {
77 	return (disc->nfd_in_set);
78 }
79 
80 nvme_feat_output_t
nvme_feat_disc_output_get(const nvme_feat_disc_t * disc)81 nvme_feat_disc_output_get(const nvme_feat_disc_t *disc)
82 {
83 	return (disc->nfd_out_get);
84 }
85 
86 nvme_feat_output_t
nvme_feat_disc_output_set(const nvme_feat_disc_t * disc)87 nvme_feat_disc_output_set(const nvme_feat_disc_t *disc)
88 {
89 	return (disc->nfd_out_set);
90 }
91 
92 uint64_t
nvme_feat_disc_data_size(const nvme_feat_disc_t * disc)93 nvme_feat_disc_data_size(const nvme_feat_disc_t *disc)
94 {
95 	return (disc->nfd_len);
96 }
97 
98 static bool
nvme_feat_discover_validate(nvme_ctrl_t * ctrl,nvme_feat_scope_t scopes,uint32_t flags)99 nvme_feat_discover_validate(nvme_ctrl_t *ctrl, nvme_feat_scope_t scopes,
100     uint32_t flags)
101 {
102 	const nvme_feat_scope_t valid_scopes = NVME_FEAT_SCOPE_CTRL |
103 	    NVME_FEAT_SCOPE_NS;
104 
105 	/*
106 	 * See the note in nvme_log_discover_validate() on why we don't support
107 	 * a zeroed scope.
108 	 */
109 	if (scopes == 0) {
110 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,  "no "
111 		    "feature scope specified (given 0), a scope must be "
112 		    "requested"));
113 	}
114 
115 	if ((scopes & ~valid_scopes) != 0) {
116 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
117 		    "encountered invalid scope for the nvme_feat_disc_scope_t: "
118 		    "0x%x", scopes & ~valid_scopes));
119 	}
120 
121 	/*
122 	 * The flags are meant for future expansion here, hence the all zero
123 	 * requirement.
124 	 */
125 	if (flags != 0) {
126 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
127 		    "encountered invalid feature discovery flags: 0x%x",
128 		    flags));
129 	}
130 
131 	return (true);
132 }
133 
134 nvme_feat_impl_t
nvme_feat_disc_impl(const nvme_feat_disc_t * disc)135 nvme_feat_disc_impl(const nvme_feat_disc_t *disc)
136 {
137 	return (disc->nfd_impl);
138 }
139 
140 void
nvme_feat_disc_free(nvme_feat_disc_t * disc)141 nvme_feat_disc_free(nvme_feat_disc_t *disc)
142 {
143 	free(disc);
144 }
145 
146 bool
nvme_feat_disc_dup(nvme_ctrl_t * ctrl,const nvme_feat_disc_t * src,nvme_feat_disc_t ** discp)147 nvme_feat_disc_dup(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *src,
148     nvme_feat_disc_t **discp)
149 {
150 	nvme_feat_disc_t *disc;
151 
152 	if (src == NULL) {
153 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
154 		    "encountered invalid nvme_feat_disc_t pointer to "
155 		    "duplicate: %p", src));
156 	}
157 
158 	if (discp == NULL) {
159 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
160 		    "encountered invalid nvme_feat_disc_t output pointer: %p",
161 		    discp));
162 	}
163 
164 	disc = calloc(1, sizeof (nvme_feat_disc_t));
165 	if (disc == NULL) {
166 		int e = errno;
167 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
168 		    "allocate memory for a new nvme_feat_disc_t: %s",
169 		    strerror(e)));
170 	}
171 
172 	(void) memcpy(disc, src, sizeof (nvme_feat_disc_t));
173 	*discp = disc;
174 	return (nvme_ctrl_success(ctrl));
175 }
176 
177 void
nvme_feat_discover_fini(nvme_feat_iter_t * iter)178 nvme_feat_discover_fini(nvme_feat_iter_t *iter)
179 {
180 	free(iter);
181 }
182 
183 static bool
nvme_feat_discover_one(nvme_feat_iter_t * iter,const nvme_feat_info_t * info)184 nvme_feat_discover_one(nvme_feat_iter_t *iter, const nvme_feat_info_t *info)
185 {
186 	nvme_feat_disc_t *disc = &iter->nfi_disc;
187 	nvme_valid_ctrl_data_t data;
188 
189 	data.vcd_vers = &iter->nfi_ctrl->nc_vers;
190 	data.vcd_id = &iter->nfi_ctrl->nc_info;
191 
192 	/*
193 	 * The user is not interested in this feature. Do not include it.
194 	 */
195 	if ((iter->nfi_scope & info->nfeat_scope) == 0) {
196 		return (false);
197 	}
198 
199 	(void) memset(disc, 0, sizeof (nvme_feat_disc_t));
200 
201 	disc->nfd_short = info->nfeat_short;
202 	disc->nfd_spec = info->nfeat_spec;
203 	disc->nfd_fid = info->nfeat_fid;
204 	disc->nfd_kind = info->nfeat_kind;
205 	disc->nfd_scope = info->nfeat_scope;
206 	disc->nfd_flags = info->nfeat_flags;
207 	disc->nfd_csi = info->nfeat_csi;
208 	disc->nfd_in_get = info->nfeat_in_get;
209 	disc->nfd_in_set = info->nfeat_in_set;
210 	disc->nfd_out_get = info->nfeat_out_get;
211 	disc->nfd_out_set = info->nfeat_out_set;
212 	disc->nfd_len = info->nfeat_len;
213 	disc->nfd_impl = nvme_feat_supported(info, &data);
214 
215 	return (true);
216 }
217 
218 nvme_iter_t
nvme_feat_discover_step(nvme_feat_iter_t * iter,const nvme_feat_disc_t ** outp)219 nvme_feat_discover_step(nvme_feat_iter_t *iter, const nvme_feat_disc_t **outp)
220 {
221 	*outp = NULL;
222 
223 	if (iter->nfi_cur_idx == nvme_std_nfeats) {
224 		return (NVME_ITER_DONE);
225 	}
226 
227 	while (iter->nfi_cur_idx < nvme_std_nfeats) {
228 		const nvme_feat_info_t *feat =
229 		    &nvme_std_feats[iter->nfi_cur_idx];
230 		iter->nfi_cur_idx++;
231 
232 		if (nvme_feat_discover_one(iter, feat)) {
233 			*outp = &iter->nfi_disc;
234 			return (NVME_ITER_VALID);
235 		}
236 	}
237 
238 	/*
239 	 * When we add support for vendor-specific features, then we will want
240 	 * to check NVME_LOG_DISC_F_NO_DB here and if it is not set, proceed to
241 	 * move past the standard features onto the vendor-specific features
242 	 * like we do in the log page work right now.
243 	 */
244 	ASSERT3U(iter->nfi_cur_idx, ==, nvme_std_nfeats);
245 	return (NVME_ITER_DONE);
246 }
247 
248 bool
nvme_feat_discover_init(nvme_ctrl_t * ctrl,nvme_feat_scope_t scope,uint32_t flags,nvme_feat_iter_t ** iterp)249 nvme_feat_discover_init(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope,
250     uint32_t flags, nvme_feat_iter_t **iterp)
251 {
252 	nvme_feat_iter_t *iter;
253 
254 	if (!nvme_feat_discover_validate(ctrl, scope, flags)) {
255 		return (false);
256 	}
257 
258 	if (iterp == NULL) {
259 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
260 		    "encountered invalid nvme_feat_iter_t output pointer: %p",
261 		    iterp));
262 	}
263 
264 	iter = calloc(1, sizeof (nvme_feat_iter_t));
265 	if (iter == NULL) {
266 		int e = errno;
267 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
268 		    "allocate memory for a new nvme_feat_iter_t: %s",
269 		    strerror(e)));
270 	}
271 
272 	iter->nfi_ctrl = ctrl;
273 	iter->nfi_scope = scope;
274 
275 	*iterp = iter;
276 	return (nvme_ctrl_success(ctrl));
277 }
278 
279 bool
nvme_feat_discover(nvme_ctrl_t * ctrl,nvme_feat_scope_t scope,uint32_t flags,nvme_feat_disc_f func,void * arg)280 nvme_feat_discover(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope, uint32_t flags,
281     nvme_feat_disc_f func, void *arg)
282 {
283 	nvme_feat_iter_t *iter;
284 	nvme_iter_t ret;
285 	const nvme_feat_disc_t *disc;
286 
287 
288 	if (func == NULL) {
289 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
290 		    "encountered invalid nvme_feat_disc_f function pointer: %p",
291 		    func));
292 	}
293 
294 	if (!nvme_feat_discover_init(ctrl, scope, flags, &iter)) {
295 		return (false);
296 	}
297 
298 	while ((ret = nvme_feat_discover_step(iter, &disc)) ==
299 	    NVME_ITER_VALID) {
300 		if (!func(ctrl, disc, arg))
301 			break;
302 	}
303 
304 	nvme_feat_discover_fini(iter);
305 	if (ret == NVME_ITER_ERROR) {
306 		return (false);
307 	}
308 
309 	return (nvme_ctrl_success(ctrl));
310 }
311 
312 void
nvme_get_feat_req_fini(nvme_get_feat_req_t * req)313 nvme_get_feat_req_fini(nvme_get_feat_req_t *req)
314 {
315 	free(req);
316 }
317 
318 /*
319  * This instantiates a simple get features request that allows most fields to be
320  * specified.
321  */
322 bool
nvme_get_feat_req_init(nvme_ctrl_t * ctrl,nvme_get_feat_req_t ** reqp)323 nvme_get_feat_req_init(nvme_ctrl_t *ctrl, nvme_get_feat_req_t **reqp)
324 {
325 	nvme_get_feat_req_t *req;
326 
327 	if (reqp == NULL) {
328 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
329 		    "encountered invalid nvme_get_feat_req_t output pointer: "
330 		    "%p", reqp));
331 	}
332 
333 	req = calloc(1, sizeof (nvme_get_feat_req_t));
334 	if (req == NULL) {
335 		int e = errno;
336 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
337 		    "allocate memory for a new nvme_get_feat_req_t: %s",
338 		    strerror(e)));
339 	}
340 
341 	req->gfr_ctrl = ctrl;
342 	for (size_t i = 0; i < nvme_get_feat_nfields; i++) {
343 		if (nvme_get_feat_fields[i].nlfi_def_req) {
344 			req->gfr_need |= 1 << i;
345 		}
346 
347 		if (nvme_get_feat_fields[i].nlfi_def_allow) {
348 			req->gfr_allow |= 1 << i;
349 		}
350 	}
351 
352 	/*
353 	 * For a generic get feature request, we don't know if this is true or
354 	 * not. Even though none of the features the kernel supports at this
355 	 * moment support this, we still want to enable this for the future for
356 	 * when they do. The kernel will enforce this.
357 	 */
358 	req->gfr_flags = NVME_FEAT_F_GET_BCAST_NSID;
359 
360 	*reqp = req;
361 	return (nvme_ctrl_success(ctrl));
362 }
363 
364 bool
nvme_get_feat_req_init_by_disc(nvme_ctrl_t * ctrl,const nvme_feat_disc_t * disc,nvme_get_feat_req_t ** reqp)365 nvme_get_feat_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *disc,
366     nvme_get_feat_req_t **reqp)
367 {
368 	nvme_get_feat_req_t *req;
369 
370 	if (disc == NULL) {
371 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
372 		    "encountered invalid nvme_feat_disc_t pointer: %p", disc));
373 	}
374 
375 	if (reqp == NULL) {
376 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
377 		    "encountered invalid nvme_get_feat_req_t output pointer: "
378 		    "%p", reqp));
379 	}
380 
381 	if (disc->nfd_impl == NVME_FEAT_IMPL_UNSUPPORTED) {
382 		return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_UNSUP_BY_DEV, 0,
383 		    "cannot create get feature request for feature %s "
384 		    "(FID 0x%x) because it is not supported by the device",
385 		    disc->nfd_short, disc->nfd_fid));
386 
387 	}
388 
389 	req = calloc(1, sizeof (nvme_get_feat_req_t));
390 	if (req == NULL) {
391 		int e = errno;
392 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
393 		    "allocate memory for a new nvme_get_feat_req_t: %s",
394 		    strerror(e)));
395 	}
396 
397 	req->gfr_ctrl = ctrl;
398 
399 	/*
400 	 * Unlike the more generic get feature initialization, we only allow
401 	 * items to be set if they're actually required. We do not require a
402 	 * selector to be set and will default to just getting the current
403 	 * value. Otherwise every other field that is settable is required. We
404 	 * don't let someone change the fid on us, because that would be quite
405 	 * confusing.
406 	 */
407 	req->gfr_fid = disc->nfd_fid;
408 	req->gfr_flags = disc->nfd_flags;
409 	req->gfr_targ_len = disc->nfd_len;
410 	req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_SEL;
411 
412 	if ((disc->nfd_in_get & NVME_GET_FEAT_F_CDW11) != 0) {
413 		req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11;
414 		req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11;
415 	}
416 
417 	if ((disc->nfd_in_get & NVME_GET_FEAT_F_DATA) != 0) {
418 		req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR;
419 		req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR;
420 	}
421 
422 	if ((disc->nfd_in_get & NVME_GET_FEAT_F_NSID) != 0) {
423 		req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID;
424 		req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID;
425 	}
426 
427 	*reqp = req;
428 	return (nvme_ctrl_success(ctrl));
429 }
430 
431 typedef struct {
432 	bool ngfi_found;
433 	const char *ngfi_name;
434 	nvme_get_feat_req_t *ngfi_req;
435 	nvme_feat_disc_t **ngfi_discp;
436 	nvme_err_data_t ngfi_err;
437 } nvme_get_feat_init_arg_t;
438 
439 static bool
nvme_get_feat_req_init_by_name_cb(nvme_ctrl_t * ctrl,const nvme_feat_disc_t * disc,void * arg)440 nvme_get_feat_req_init_by_name_cb(nvme_ctrl_t *ctrl,
441     const nvme_feat_disc_t *disc, void *arg)
442 {
443 	nvme_get_feat_init_arg_t *init = arg;
444 
445 	if (strcmp(init->ngfi_name, disc->nfd_short) != 0 &&
446 	    strcmp(init->ngfi_name, disc->nfd_spec) != 0) {
447 		return (true);
448 	}
449 
450 	init->ngfi_found = true;
451 	if (!nvme_get_feat_req_init_by_disc(ctrl, disc, &init->ngfi_req)) {
452 		nvme_ctrl_err_save(ctrl, &init->ngfi_err);
453 		init->ngfi_req = NULL;
454 	} else if (init->ngfi_discp != NULL) {
455 		if (!nvme_feat_disc_dup(ctrl, disc, init->ngfi_discp)) {
456 			nvme_ctrl_err_save(ctrl, &init->ngfi_err);
457 			nvme_get_feat_req_fini(init->ngfi_req);
458 			init->ngfi_req = NULL;
459 		}
460 	}
461 
462 	return (false);
463 }
464 
465 bool
nvme_get_feat_req_init_by_name(nvme_ctrl_t * ctrl,const char * name,uint32_t df,nvme_feat_disc_t ** discp,nvme_get_feat_req_t ** reqp)466 nvme_get_feat_req_init_by_name(nvme_ctrl_t *ctrl, const char *name,
467     uint32_t df, nvme_feat_disc_t **discp, nvme_get_feat_req_t **reqp)
468 {
469 	nvme_get_feat_init_arg_t init;
470 
471 	/*
472 	 * Note, we consider discp optional unlikely the name and reqp. The
473 	 * discover flags, df, will be validated by the discover functions we
474 	 * call.
475 	 */
476 	if (name == NULL) {
477 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
478 		    "encountered invalid pointer for log page name: %p", name));
479 	}
480 
481 	if (reqp == NULL) {
482 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
483 		    "encountered invalid nvme_get_feat_req_t output pointer: "
484 		    "%p", reqp));
485 	}
486 
487 	(void) memset(&init, 0, sizeof (init));
488 	init.ngfi_name = name;
489 	init.ngfi_discp = discp;
490 
491 	if (!nvme_feat_discover(ctrl, NVME_FEAT_SCOPE_CTRL | NVME_FEAT_SCOPE_NS,
492 	    df, nvme_get_feat_req_init_by_name_cb, &init)) {
493 		return (false);
494 	}
495 
496 	if (!init.ngfi_found) {
497 		return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_NAME_UNKNOWN, 0,
498 		    "failed to find feature with name %s", name));
499 	}
500 
501 	if (init.ngfi_req == NULL) {
502 		nvme_ctrl_err_set(ctrl, &init.ngfi_err);
503 		return (false);
504 	}
505 
506 	*reqp = init.ngfi_req;
507 	return (nvme_ctrl_success(ctrl));
508 }
509 
510 static void
nvme_get_feat_req_set_need(nvme_get_feat_req_t * req,nvme_get_feat_req_field_t field)511 nvme_get_feat_req_set_need(nvme_get_feat_req_t *req,
512     nvme_get_feat_req_field_t field)
513 {
514 	req->gfr_need |= 1 << field;
515 }
516 
517 static void
nvme_get_feat_req_clear_need(nvme_get_feat_req_t * req,nvme_get_feat_req_field_t field)518 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req,
519     nvme_get_feat_req_field_t field)
520 {
521 	req->gfr_need &= ~(1 << field);
522 }
523 
524 static const nvme_field_check_t nvme_get_feat_check_fid = {
525 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID,
526 	NVME_ERR_FEAT_FID_RANGE, 0, 0
527 };
528 
529 bool
nvme_get_feat_req_set_fid(nvme_get_feat_req_t * req,uint32_t fid)530 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid)
531 {
532 	if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature",
533 	    &nvme_get_feat_check_fid, req->gfr_allow)) {
534 		return (false);
535 	}
536 
537 	req->gfr_fid = fid;
538 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID);
539 	return (nvme_ctrl_success(req->gfr_ctrl));
540 }
541 
542 static const nvme_field_check_t nvme_get_feat_check_sel = {
543 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL,
544 	NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0
545 };
546 
547 bool
nvme_get_feat_req_set_sel(nvme_get_feat_req_t * req,uint32_t sel)548 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel)
549 {
550 	if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature",
551 	    &nvme_get_feat_check_sel, req->gfr_allow)) {
552 		return (false);
553 	}
554 
555 	if (sel == NVME_FEATURE_SEL_SUPPORTED) {
556 		return (nvme_ctrl_error(req->gfr_ctrl,
557 		    NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do "
558 		    "not support supported capabilities selector"));
559 	}
560 
561 	req->gfr_sel = sel;
562 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL);
563 	return (nvme_ctrl_success(req->gfr_ctrl));
564 }
565 
566 static const nvme_field_check_t nvme_get_feat_check_cdw11 = {
567 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11,
568 	NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE
569 };
570 
571 bool
nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t * req,uint32_t cdw11)572 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11)
573 {
574 	if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature",
575 	    &nvme_get_feat_check_cdw11, req->gfr_allow)) {
576 		return (false);
577 	}
578 
579 	req->gfr_cdw11 = cdw11;
580 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11);
581 	return (nvme_ctrl_success(req->gfr_ctrl));
582 }
583 
584 static const nvme_field_check_t nvme_get_feat_check_nsid = {
585 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID,
586 	NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE
587 };
588 
589 bool
nvme_get_feat_req_set_nsid(nvme_get_feat_req_t * req,uint32_t nsid)590 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid)
591 {
592 	nvme_ctrl_t *ctrl = req->gfr_ctrl;
593 
594 	/*
595 	 * Check the NSID first before we go into the generic validation code so
596 	 * we can get a better error message.
597 	 */
598 	if (nsid == NVME_NSID_BCAST &&
599 	    (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) {
600 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
601 		    "namespaces/controller nsid (0x%x) is not allowed for this "
602 		    "feature, valid namespaces are [0x%x, 0x%x]", nsid,
603 		    NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn));
604 	}
605 
606 	if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature",
607 	    &nvme_get_feat_check_nsid, req->gfr_allow)) {
608 		return (false);
609 	}
610 
611 	req->gfr_nsid = nsid;
612 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID);
613 	return (nvme_ctrl_success(ctrl));
614 }
615 
616 bool
nvme_get_feat_req_set_output(nvme_get_feat_req_t * req,void * buf,size_t len)617 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len)
618 {
619 	if (buf == NULL) {
620 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
621 		    "get feature output buffer cannot be NULL"));
622 	}
623 
624 	if (len == 0) {
625 		return (nvme_ctrl_error(req->gfr_ctrl,
626 		    NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length "
627 		    "cannot be zero"));
628 	}
629 
630 	/*
631 	 * Something to consider for the future here is that we know the fixed
632 	 * size data that we're expecting for the feature. It would be nice if
633 	 * we validated that we have that size now versus later. Related,
634 	 * because there is no field check logic for this, we must manually
635 	 * check that it is allowed and cannot use nvme_field_check_one().
636 	 */
637 	if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) {
638 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
639 		    0, "field output (dptr) cannot be set in this get feature "
640 		    "request"));
641 	}
642 
643 	req->gfr_buf = buf;
644 	req->gfr_len = len;
645 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
646 	return (nvme_ctrl_success(req->gfr_ctrl));
647 }
648 
649 bool
nvme_get_feat_req_clear_output(nvme_get_feat_req_t * req)650 nvme_get_feat_req_clear_output(nvme_get_feat_req_t *req)
651 {
652 	if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) {
653 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
654 		    0, "field output (dptr) cannot be cleared in this get "
655 		    "feature request"));
656 	}
657 
658 	req->gfr_buf = NULL;
659 	req->gfr_len = 0;
660 	nvme_get_feat_req_set_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
661 	return (nvme_ctrl_success(req->gfr_ctrl));
662 }
663 
664 bool
nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t * req,uint32_t * cdw0)665 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0)
666 {
667 	if (cdw0 == NULL) {
668 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
669 		    "encountered invalid cdw0 output pointer: %p", cdw0));
670 	}
671 
672 	if (!req->gfr_results_valid) {
673 		return (nvme_ctrl_error(req->gfr_ctrl,
674 		    NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not "
675 		    "currently valid and cannot be returned"));
676 	}
677 
678 	*cdw0 = req->gfr_cdw0;
679 	return (nvme_ctrl_success(req->gfr_ctrl));
680 }
681 
682 bool
nvme_get_feat_req_exec(nvme_get_feat_req_t * req)683 nvme_get_feat_req_exec(nvme_get_feat_req_t *req)
684 {
685 	nvme_ctrl_t *ctrl = req->gfr_ctrl;
686 	nvme_ioctl_get_feature_t feat;
687 
688 	/*
689 	 * Because this has been called, we need to immediately invalidate our
690 	 * stored cdw0 results. We do this as a precaution regardless of whether
691 	 * or not it is valid.
692 	 */
693 	req->gfr_results_valid = false;
694 	req->gfr_cdw0 = 0;
695 
696 	if (req->gfr_need != 0) {
697 		return (nvme_field_miss_err(ctrl, nvme_get_feat_fields,
698 		    nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS,
699 		    "get feature", req->gfr_need));
700 	}
701 
702 	(void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t));
703 	feat.nigf_common.nioc_nsid = req->gfr_nsid;
704 	feat.nigf_fid = req->gfr_fid;
705 	feat.nigf_sel = req->gfr_sel;
706 	feat.nigf_cdw11 = req->gfr_cdw11;
707 	if (req->gfr_buf != NULL) {
708 		feat.nigf_data = (uintptr_t)req->gfr_buf;
709 		feat.nigf_len = req->gfr_len;
710 	}
711 
712 	if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) {
713 		int e = errno;
714 		return (nvme_ioctl_syserror(ctrl, e, "get feature"));
715 	}
716 
717 	if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) {
718 		return (nvme_ioctl_error(ctrl, &feat.nigf_common,
719 		    "get feature"));
720 	}
721 
722 	req->gfr_results_valid = true;
723 	req->gfr_cdw0 = feat.nigf_cdw0;
724 
725 	return (nvme_ctrl_success(ctrl));
726 }
727