xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_feature.c (revision e8d3efa1c56e5f2b5368600a2baeb7b1d54a07f8)
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 2026 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 *
27 nvme_feat_disc_short(const nvme_feat_disc_t *disc)
28 {
29 	return (disc->nfd_short);
30 }
31 
32 const char *
33 nvme_feat_disc_spec(const nvme_feat_disc_t *disc)
34 {
35 	return (disc->nfd_spec);
36 }
37 
38 uint32_t
39 nvme_feat_disc_fid(const nvme_feat_disc_t *disc)
40 {
41 	return (disc->nfd_fid);
42 }
43 
44 nvme_feat_scope_t
45 nvme_feat_disc_scope(const nvme_feat_disc_t *disc)
46 {
47 	return (disc->nfd_scope);
48 }
49 
50 nvme_feat_kind_t
51 nvme_feat_disc_kind(const nvme_feat_disc_t *disc)
52 {
53 	return (disc->nfd_kind);
54 }
55 
56 nvme_feat_csi_t
57 nvme_feat_disc_csi(const nvme_feat_disc_t *disc)
58 {
59 	return (disc->nfd_csi);
60 }
61 
62 nvme_feat_flags_t
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
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
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
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
87 nvme_feat_disc_output_set(const nvme_feat_disc_t *disc)
88 {
89 	return (disc->nfd_out_set);
90 }
91 
92 nvme_disc_impact_t
93 nvme_feat_disc_impact_set(const nvme_feat_disc_t *disc)
94 {
95 	return (disc->nfd_impact);
96 }
97 
98 uint64_t
99 nvme_feat_disc_data_size(const nvme_feat_disc_t *disc)
100 {
101 	return (disc->nfd_len);
102 }
103 
104 static bool
105 nvme_feat_discover_validate(nvme_ctrl_t *ctrl, nvme_feat_scope_t scopes,
106     uint32_t flags)
107 {
108 	const nvme_feat_scope_t valid_scopes = NVME_FEAT_SCOPE_CTRL |
109 	    NVME_FEAT_SCOPE_NS | NVME_FEAT_SCOPE_NVM;
110 
111 	/*
112 	 * See the note in nvme_log_discover_validate() on why we don't support
113 	 * a zeroed scope.
114 	 */
115 	if (scopes == 0) {
116 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,  "no "
117 		    "feature scope specified (given 0), a scope must be "
118 		    "requested"));
119 	}
120 
121 	if ((scopes & ~valid_scopes) != 0) {
122 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
123 		    "encountered invalid scope for the nvme_feat_disc_scope_t: "
124 		    "0x%x", scopes & ~valid_scopes));
125 	}
126 
127 	/*
128 	 * The flags are meant for future expansion here, hence the all zero
129 	 * requirement.
130 	 */
131 	if (flags != 0) {
132 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
133 		    "encountered invalid feature discovery flags: 0x%x",
134 		    flags));
135 	}
136 
137 	return (true);
138 }
139 
140 nvme_feat_impl_t
141 nvme_feat_disc_impl(const nvme_feat_disc_t *disc)
142 {
143 	return (disc->nfd_impl);
144 }
145 
146 void
147 nvme_feat_disc_free(nvme_feat_disc_t *disc)
148 {
149 	free(disc);
150 }
151 
152 bool
153 nvme_feat_disc_dup(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *src,
154     nvme_feat_disc_t **discp)
155 {
156 	nvme_feat_disc_t *disc;
157 
158 	if (src == NULL) {
159 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
160 		    "encountered invalid nvme_feat_disc_t pointer to "
161 		    "duplicate: %p", src));
162 	}
163 
164 	if (discp == NULL) {
165 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
166 		    "encountered invalid nvme_feat_disc_t output pointer: %p",
167 		    discp));
168 	}
169 
170 	disc = calloc(1, sizeof (nvme_feat_disc_t));
171 	if (disc == NULL) {
172 		int e = errno;
173 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
174 		    "allocate memory for a new nvme_feat_disc_t: %s",
175 		    strerror(e)));
176 	}
177 
178 	(void) memcpy(disc, src, sizeof (nvme_feat_disc_t));
179 	*discp = disc;
180 	return (nvme_ctrl_success(ctrl));
181 }
182 
183 void
184 nvme_feat_discover_fini(nvme_feat_iter_t *iter)
185 {
186 	free(iter);
187 }
188 
189 static bool
190 nvme_feat_discover_one(nvme_feat_iter_t *iter, const nvme_feat_info_t *info)
191 {
192 	nvme_feat_disc_t *disc = &iter->nfi_disc;
193 	nvme_valid_ctrl_data_t data;
194 
195 	data.vcd_vers = &iter->nfi_ctrl->nc_vers;
196 	data.vcd_id = &iter->nfi_ctrl->nc_info;
197 
198 	/*
199 	 * The user is not interested in this feature. Do not include it.
200 	 */
201 	if ((iter->nfi_scope & info->nfeat_scope) == 0) {
202 		return (false);
203 	}
204 
205 	(void) memset(disc, 0, sizeof (nvme_feat_disc_t));
206 
207 	disc->nfd_short = info->nfeat_short;
208 	disc->nfd_spec = info->nfeat_spec;
209 	disc->nfd_fid = info->nfeat_fid;
210 	disc->nfd_kind = info->nfeat_kind;
211 	disc->nfd_scope = info->nfeat_scope;
212 	disc->nfd_flags = info->nfeat_flags;
213 	disc->nfd_impact = info->nfeat_impact;
214 	disc->nfd_csi = info->nfeat_csi;
215 	disc->nfd_in_get = info->nfeat_in_get;
216 	disc->nfd_in_set = info->nfeat_in_set;
217 	disc->nfd_out_get = info->nfeat_out_get;
218 	disc->nfd_out_set = info->nfeat_out_set;
219 	disc->nfd_len = info->nfeat_len;
220 	disc->nfd_impl = nvme_feat_supported(info, &data);
221 
222 	return (true);
223 }
224 
225 nvme_iter_t
226 nvme_feat_discover_step(nvme_feat_iter_t *iter, const nvme_feat_disc_t **outp)
227 {
228 	*outp = NULL;
229 	nvme_ctrl_t *ctrl = iter->nfi_ctrl;
230 
231 	if (iter->nfi_std_done && iter->nfi_vs_done) {
232 		return (NVME_ITER_DONE);
233 	}
234 
235 	if (!iter->nfi_std_done) {
236 		while (iter->nfi_cur_idx < nvme_std_nfeats) {
237 			const nvme_feat_info_t *feat =
238 			    &nvme_std_feats[iter->nfi_cur_idx];
239 			iter->nfi_cur_idx++;
240 
241 			if (nvme_feat_discover_one(iter, feat)) {
242 				*outp = &iter->nfi_disc;
243 				return (NVME_ITER_VALID);
244 			}
245 		}
246 
247 		iter->nfi_std_done = true;
248 		iter->nfi_cur_idx = 0;
249 	}
250 
251 	if (ctrl->nc_vsd == NULL) {
252 		iter->nfi_vs_done = true;
253 		return (NVME_ITER_DONE);
254 	}
255 
256 	while (iter->nfi_cur_idx < ctrl->nc_vsd->nvd_nfeats) {
257 		const nvme_feat_info_t *feat =
258 		    ctrl->nc_vsd->nvd_feats[iter->nfi_cur_idx];
259 		iter->nfi_cur_idx++;
260 
261 		if (nvme_feat_discover_one(iter, feat)) {
262 			*outp = &iter->nfi_disc;
263 			return (NVME_ITER_VALID);
264 		}
265 	}
266 
267 	iter->nfi_vs_done = true;
268 	iter->nfi_cur_idx = 0;
269 
270 	return (NVME_ITER_DONE);
271 }
272 
273 bool
274 nvme_feat_discover_init(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope,
275     uint32_t flags, nvme_feat_iter_t **iterp)
276 {
277 	nvme_feat_iter_t *iter;
278 
279 	if (!nvme_feat_discover_validate(ctrl, scope, flags)) {
280 		return (false);
281 	}
282 
283 	if (iterp == NULL) {
284 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
285 		    "encountered invalid nvme_feat_iter_t output pointer: %p",
286 		    iterp));
287 	}
288 
289 	iter = calloc(1, sizeof (nvme_feat_iter_t));
290 	if (iter == NULL) {
291 		int e = errno;
292 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
293 		    "allocate memory for a new nvme_feat_iter_t: %s",
294 		    strerror(e)));
295 	}
296 
297 	iter->nfi_ctrl = ctrl;
298 	iter->nfi_scope = scope;
299 
300 	*iterp = iter;
301 	return (nvme_ctrl_success(ctrl));
302 }
303 
304 bool
305 nvme_feat_discover(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope, uint32_t flags,
306     nvme_feat_disc_f func, void *arg)
307 {
308 	nvme_feat_iter_t *iter;
309 	nvme_iter_t ret;
310 	const nvme_feat_disc_t *disc;
311 
312 
313 	if (func == NULL) {
314 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
315 		    "encountered invalid nvme_feat_disc_f function pointer: %p",
316 		    func));
317 	}
318 
319 	if (!nvme_feat_discover_init(ctrl, scope, flags, &iter)) {
320 		return (false);
321 	}
322 
323 	while ((ret = nvme_feat_discover_step(iter, &disc)) ==
324 	    NVME_ITER_VALID) {
325 		if (!func(ctrl, disc, arg))
326 			break;
327 	}
328 
329 	nvme_feat_discover_fini(iter);
330 	if (ret == NVME_ITER_ERROR) {
331 		return (false);
332 	}
333 
334 	return (nvme_ctrl_success(ctrl));
335 }
336 
337 void
338 nvme_get_feat_req_fini(nvme_get_feat_req_t *req)
339 {
340 	free(req);
341 }
342 
343 /*
344  * This instantiates a simple get features request that allows most fields to be
345  * specified.
346  */
347 bool
348 nvme_get_feat_req_init(nvme_ctrl_t *ctrl, nvme_get_feat_req_t **reqp)
349 {
350 	nvme_get_feat_req_t *req;
351 
352 	if (reqp == NULL) {
353 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
354 		    "encountered invalid nvme_get_feat_req_t output pointer: "
355 		    "%p", reqp));
356 	}
357 
358 	req = calloc(1, sizeof (nvme_get_feat_req_t));
359 	if (req == NULL) {
360 		int e = errno;
361 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
362 		    "allocate memory for a new nvme_get_feat_req_t: %s",
363 		    strerror(e)));
364 	}
365 
366 	req->gfr_ctrl = ctrl;
367 	for (size_t i = 0; i < nvme_get_feat_nfields; i++) {
368 		if (nvme_get_feat_fields[i].nlfi_def_req) {
369 			req->gfr_need |= 1 << i;
370 		}
371 
372 		if (nvme_get_feat_fields[i].nlfi_def_allow) {
373 			req->gfr_allow |= 1 << i;
374 		}
375 	}
376 
377 	/*
378 	 * For a generic get feature request, we don't know if this is true or
379 	 * not. Even though none of the features the kernel supports at this
380 	 * moment support this, we still want to enable this for the future for
381 	 * when they do. The kernel will enforce this.
382 	 */
383 	req->gfr_flags = NVME_FEAT_F_GET_BCAST_NSID;
384 
385 	*reqp = req;
386 	return (nvme_ctrl_success(ctrl));
387 }
388 
389 bool
390 nvme_get_feat_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *disc,
391     nvme_get_feat_req_t **reqp)
392 {
393 	nvme_get_feat_req_t *req;
394 
395 	if (disc == NULL) {
396 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
397 		    "encountered invalid nvme_feat_disc_t pointer: %p", disc));
398 	}
399 
400 	if (reqp == NULL) {
401 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
402 		    "encountered invalid nvme_get_feat_req_t output pointer: "
403 		    "%p", reqp));
404 	}
405 
406 	if (disc->nfd_impl == NVME_FEAT_IMPL_UNSUPPORTED) {
407 		return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_UNSUP_BY_DEV, 0,
408 		    "cannot create get feature request for feature %s "
409 		    "(FID 0x%x) because it is not supported by the device",
410 		    disc->nfd_short, disc->nfd_fid));
411 
412 	}
413 
414 	req = calloc(1, sizeof (nvme_get_feat_req_t));
415 	if (req == NULL) {
416 		int e = errno;
417 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
418 		    "allocate memory for a new nvme_get_feat_req_t: %s",
419 		    strerror(e)));
420 	}
421 
422 	req->gfr_ctrl = ctrl;
423 
424 	/*
425 	 * Unlike the more generic get feature initialization, we only allow
426 	 * items to be set if they're actually required. We do not require a
427 	 * selector to be set and will default to just getting the current
428 	 * value. Otherwise every other field that is settable is required. We
429 	 * don't let someone change the fid on us, because that would be quite
430 	 * confusing.
431 	 */
432 	req->gfr_fid = disc->nfd_fid;
433 	req->gfr_flags = disc->nfd_flags;
434 	req->gfr_targ_len = disc->nfd_len;
435 	req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_SEL;
436 
437 	if ((disc->nfd_in_get & NVME_GET_FEAT_F_CDW11) != 0) {
438 		req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11;
439 		req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11;
440 	}
441 
442 	if ((disc->nfd_in_get & NVME_GET_FEAT_F_DATA) != 0) {
443 		req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR;
444 		req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR;
445 	}
446 
447 	if ((disc->nfd_in_get & NVME_GET_FEAT_F_NSID) != 0) {
448 		req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID;
449 		req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID;
450 	}
451 
452 	*reqp = req;
453 	return (nvme_ctrl_success(ctrl));
454 }
455 
456 typedef struct {
457 	bool ngfi_found;
458 	const char *ngfi_name;
459 	nvme_get_feat_req_t *ngfi_req;
460 	nvme_feat_disc_t **ngfi_discp;
461 	nvme_err_data_t ngfi_err;
462 } nvme_get_feat_init_arg_t;
463 
464 static bool
465 nvme_get_feat_req_init_by_name_cb(nvme_ctrl_t *ctrl,
466     const nvme_feat_disc_t *disc, void *arg)
467 {
468 	nvme_get_feat_init_arg_t *init = arg;
469 
470 	if (strcmp(init->ngfi_name, disc->nfd_short) != 0 &&
471 	    strcmp(init->ngfi_name, disc->nfd_spec) != 0) {
472 		return (true);
473 	}
474 
475 	init->ngfi_found = true;
476 	if (!nvme_get_feat_req_init_by_disc(ctrl, disc, &init->ngfi_req)) {
477 		nvme_ctrl_err_save(ctrl, &init->ngfi_err);
478 		init->ngfi_req = NULL;
479 	} else if (init->ngfi_discp != NULL) {
480 		if (!nvme_feat_disc_dup(ctrl, disc, init->ngfi_discp)) {
481 			nvme_ctrl_err_save(ctrl, &init->ngfi_err);
482 			nvme_get_feat_req_fini(init->ngfi_req);
483 			init->ngfi_req = NULL;
484 		}
485 	}
486 
487 	return (false);
488 }
489 
490 bool
491 nvme_get_feat_req_init_by_name(nvme_ctrl_t *ctrl, const char *name,
492     uint32_t df, nvme_feat_disc_t **discp, nvme_get_feat_req_t **reqp)
493 {
494 	nvme_get_feat_init_arg_t init;
495 
496 	/*
497 	 * Note, we consider discp optional unlike the name and reqp. The
498 	 * discover flags, df, will be validated by the discover functions we
499 	 * call.
500 	 */
501 	if (name == NULL) {
502 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
503 		    "encountered invalid pointer for log page name: %p", name));
504 	}
505 
506 	if (reqp == NULL) {
507 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
508 		    "encountered invalid nvme_get_feat_req_t output pointer: "
509 		    "%p", reqp));
510 	}
511 
512 	(void) memset(&init, 0, sizeof (init));
513 	init.ngfi_name = name;
514 	init.ngfi_discp = discp;
515 
516 	if (!nvme_feat_discover(ctrl, NVME_FEAT_SCOPE_CTRL |
517 	    NVME_FEAT_SCOPE_NS | NVME_FEAT_SCOPE_NVM, df,
518 	    nvme_get_feat_req_init_by_name_cb, &init)) {
519 		return (false);
520 	}
521 
522 	if (!init.ngfi_found) {
523 		return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_NAME_UNKNOWN, 0,
524 		    "failed to find feature with name %s", name));
525 	}
526 
527 	if (init.ngfi_req == NULL) {
528 		nvme_ctrl_err_set(ctrl, &init.ngfi_err);
529 		return (false);
530 	}
531 
532 	*reqp = init.ngfi_req;
533 	return (nvme_ctrl_success(ctrl));
534 }
535 
536 static void
537 nvme_get_feat_req_set_need(nvme_get_feat_req_t *req,
538     nvme_get_feat_req_field_t field)
539 {
540 	req->gfr_need |= 1 << field;
541 }
542 
543 static void
544 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req,
545     nvme_get_feat_req_field_t field)
546 {
547 	req->gfr_need &= ~(1 << field);
548 }
549 
550 static const nvme_field_check_t nvme_get_feat_check_fid = {
551 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID,
552 	NVME_ERR_FEAT_FID_RANGE, 0, 0
553 };
554 
555 bool
556 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid)
557 {
558 	if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature",
559 	    &nvme_get_feat_check_fid, req->gfr_allow)) {
560 		return (false);
561 	}
562 
563 	req->gfr_fid = fid;
564 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID);
565 	return (nvme_ctrl_success(req->gfr_ctrl));
566 }
567 
568 static const nvme_field_check_t nvme_get_feat_check_sel = {
569 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL,
570 	NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0
571 };
572 
573 bool
574 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel)
575 {
576 	if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature",
577 	    &nvme_get_feat_check_sel, req->gfr_allow)) {
578 		return (false);
579 	}
580 
581 	if (sel == NVME_FEATURE_SEL_SUPPORTED) {
582 		return (nvme_ctrl_error(req->gfr_ctrl,
583 		    NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do "
584 		    "not support supported capabilities selector"));
585 	}
586 
587 	req->gfr_sel = sel;
588 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL);
589 	return (nvme_ctrl_success(req->gfr_ctrl));
590 }
591 
592 static const nvme_field_check_t nvme_get_feat_check_cdw11 = {
593 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11,
594 	NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE
595 };
596 
597 bool
598 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11)
599 {
600 	if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature",
601 	    &nvme_get_feat_check_cdw11, req->gfr_allow)) {
602 		return (false);
603 	}
604 
605 	req->gfr_cdw11 = cdw11;
606 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11);
607 	return (nvme_ctrl_success(req->gfr_ctrl));
608 }
609 
610 static const nvme_field_check_t nvme_get_feat_check_nsid = {
611 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID,
612 	NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE
613 };
614 
615 bool
616 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid)
617 {
618 	nvme_ctrl_t *ctrl = req->gfr_ctrl;
619 
620 	/*
621 	 * Check the NSID first before we go into the generic validation code so
622 	 * we can get a better error message.
623 	 */
624 	if (nsid == NVME_NSID_BCAST &&
625 	    (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) {
626 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
627 		    "namespaces/controller nsid (0x%x) is not allowed for this "
628 		    "feature, valid namespaces are [0x%x, 0x%x]", nsid,
629 		    NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn));
630 	}
631 
632 	if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature",
633 	    &nvme_get_feat_check_nsid, req->gfr_allow)) {
634 		return (false);
635 	}
636 
637 	req->gfr_nsid = nsid;
638 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID);
639 	return (nvme_ctrl_success(ctrl));
640 }
641 
642 bool
643 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len)
644 {
645 	if (buf == NULL) {
646 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
647 		    "get feature output buffer cannot be NULL"));
648 	}
649 
650 	if (len == 0) {
651 		return (nvme_ctrl_error(req->gfr_ctrl,
652 		    NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length "
653 		    "cannot be zero"));
654 	}
655 
656 	if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) {
657 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
658 		    0, "field output (dptr) cannot be set in this get feature "
659 		    "request"));
660 	}
661 
662 	if (req->gfr_targ_len != 0 && len < req->gfr_targ_len) {
663 		return (nvme_ctrl_error(req->gfr_ctrl,
664 		    NVME_ERR_FEAT_DATA_RANGE, 0, "requested data length 0x%zx "
665 		    "is less than the feature's target size of 0x%" PRIx64, len,
666 		    req->gfr_targ_len));
667 	}
668 
669 	req->gfr_buf = buf;
670 	req->gfr_len = len;
671 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
672 	return (nvme_ctrl_success(req->gfr_ctrl));
673 }
674 
675 bool
676 nvme_get_feat_req_clear_output(nvme_get_feat_req_t *req)
677 {
678 	if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) {
679 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
680 		    0, "field output (dptr) cannot be cleared in this get "
681 		    "feature request"));
682 	}
683 
684 	req->gfr_buf = NULL;
685 	req->gfr_len = 0;
686 	nvme_get_feat_req_set_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
687 	return (nvme_ctrl_success(req->gfr_ctrl));
688 }
689 
690 bool
691 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0)
692 {
693 	if (cdw0 == NULL) {
694 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
695 		    "encountered invalid cdw0 output pointer: %p", cdw0));
696 	}
697 
698 	if (!req->gfr_results_valid) {
699 		return (nvme_ctrl_error(req->gfr_ctrl,
700 		    NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not "
701 		    "currently valid and cannot be returned"));
702 	}
703 
704 	*cdw0 = req->gfr_cdw0;
705 	return (nvme_ctrl_success(req->gfr_ctrl));
706 }
707 
708 bool
709 nvme_get_feat_req_exec(nvme_get_feat_req_t *req)
710 {
711 	nvme_ctrl_t *ctrl = req->gfr_ctrl;
712 	nvme_ioctl_get_feature_t feat;
713 
714 	/*
715 	 * Because this has been called, we need to immediately invalidate our
716 	 * stored cdw0 results. We do this as a precaution regardless of whether
717 	 * or not it is valid.
718 	 */
719 	req->gfr_results_valid = false;
720 	req->gfr_cdw0 = 0;
721 
722 	if (req->gfr_need != 0) {
723 		return (nvme_field_miss_err(ctrl, nvme_get_feat_fields,
724 		    nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS,
725 		    "get feature", req->gfr_need));
726 	}
727 
728 	(void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t));
729 	feat.nigf_common.nioc_nsid = req->gfr_nsid;
730 	feat.nigf_fid = req->gfr_fid;
731 	feat.nigf_sel = req->gfr_sel;
732 	feat.nigf_cdw11 = req->gfr_cdw11;
733 	if (req->gfr_buf != NULL) {
734 		feat.nigf_data = (uintptr_t)req->gfr_buf;
735 		feat.nigf_len = req->gfr_len;
736 	}
737 
738 	if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) {
739 		int e = errno;
740 		return (nvme_ioctl_syserror(ctrl, e, "get feature"));
741 	}
742 
743 	if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) {
744 		return (nvme_ioctl_error(ctrl, &feat.nigf_common,
745 		    "get feature"));
746 	}
747 
748 	req->gfr_results_valid = true;
749 	req->gfr_cdw0 = feat.nigf_cdw0;
750 
751 	return (nvme_ctrl_success(ctrl));
752 }
753 
754 void
755 nvme_set_feat_req_fini(nvme_set_feat_req_t *req)
756 {
757 	free(req);
758 }
759 
760 bool
761 nvme_set_feat_req_init(nvme_ctrl_t *ctrl, nvme_set_feat_req_t **reqp)
762 {
763 	nvme_set_feat_req_t *req;
764 
765 	if (reqp == NULL) {
766 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
767 		    "encountered invalid nvme_set_feat_req_t output pointer: "
768 		    "%p", reqp));
769 	}
770 
771 	req = calloc(1, sizeof (nvme_set_feat_req_t));
772 	if (req == NULL) {
773 		int e = errno;
774 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
775 		    "allocate memory for a new nvme_set_feat_req_t: %s",
776 		    strerror(e)));
777 	}
778 
779 	req->sfr_ctrl = ctrl;
780 	for (size_t i = 0; i < nvme_set_feat_nfields; i++) {
781 		if (nvme_set_feat_fields[i].nlfi_def_req) {
782 			req->sfr_need |= 1 << i;
783 		}
784 
785 		if (nvme_set_feat_fields[i].nlfi_def_allow) {
786 			req->sfr_allow |= 1 << i;
787 		}
788 	}
789 
790 	/*
791 	 * We set this here for the same reason that we do in
792 	 * nvme_get_feat_req_init().
793 	 */
794 	req->sfr_flags = NVME_FEAT_F_GET_BCAST_NSID;
795 
796 	*reqp = req;
797 	return (nvme_ctrl_success(ctrl));
798 }
799 
800 bool nvme_set_feat_req_init_by_disc(nvme_ctrl_t *ctrl,
801     const nvme_feat_disc_t *disc, nvme_set_feat_req_t **reqp)
802 {
803 	nvme_set_feat_req_t *req;
804 
805 	if (disc == NULL) {
806 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
807 		    "encountered invalid nvme_feat_disc_t pointer: %p", disc));
808 	}
809 
810 	if (reqp == NULL) {
811 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
812 		    "encountered invalid nvme_set_feat_req_t output pointer: "
813 		    "%p", reqp));
814 	}
815 
816 	if (disc->nfd_impl == NVME_FEAT_IMPL_UNSUPPORTED) {
817 		return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_UNSUP_BY_DEV, 0,
818 		    "cannot create set feature request for feature %s "
819 		    "(FID 0x%x) because it is not supported by the device",
820 		    disc->nfd_short, disc->nfd_fid));
821 
822 	}
823 
824 	req = calloc(1, sizeof (nvme_set_feat_req_t));
825 	if (req == NULL) {
826 		int e = errno;
827 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
828 		    "allocate memory for a new nvme_set_feat_req_t: %s",
829 		    strerror(e)));
830 	}
831 
832 	req->sfr_ctrl = ctrl;
833 
834 	/*
835 	 * Using the feature discovery information set a number of fields from
836 	 * this including the feature id, the target size, the allowed flags,
837 	 * and the impact. We also use this to seed the set of allowed and
838 	 * needed fields.
839 	 */
840 	req->sfr_fid = disc->nfd_fid;
841 	req->sfr_flags = disc->nfd_flags;
842 	req->sfr_targ_len = disc->nfd_len;
843 	req->sfr_impact = disc->nfd_impact;
844 	req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_SAVE;
845 
846 	if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW11) != 0) {
847 		req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW11;
848 		req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW11;
849 	}
850 
851 	if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW12) != 0) {
852 		req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW12;
853 		req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW12;
854 	}
855 
856 	if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW13) != 0) {
857 		req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW13;
858 		req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW13;
859 	}
860 
861 	if ((disc->nfd_in_set & NVME_SET_FEAT_F_CDW15) != 0) {
862 		req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW15;
863 		req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_CDW15;
864 	}
865 
866 	if ((disc->nfd_in_set & NVME_SET_FEAT_F_DATA) != 0) {
867 		req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_DPTR;
868 		req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_DPTR;
869 	}
870 
871 	if ((disc->nfd_in_set & NVME_SET_FEAT_F_NSID) != 0) {
872 		req->sfr_need |= 1 << NVME_SET_FEAT_REQ_FIELD_NSID;
873 		req->sfr_allow |= 1 << NVME_SET_FEAT_REQ_FIELD_NSID;
874 	}
875 
876 	*reqp = req;
877 	return (nvme_ctrl_success(ctrl));
878 }
879 
880 typedef struct {
881 	bool nsfi_found;
882 	const char *nsfi_name;
883 	nvme_set_feat_req_t *nsfi_req;
884 	nvme_feat_disc_t **nsfi_discp;
885 	nvme_err_data_t nsfi_err;
886 } nvme_set_feat_init_arg_t;
887 
888 static bool
889 nvme_set_feat_req_init_by_name_cb(nvme_ctrl_t *ctrl,
890     const nvme_feat_disc_t *disc, void *arg)
891 {
892 	nvme_set_feat_init_arg_t *init = arg;
893 
894 	if (strcmp(init->nsfi_name, disc->nfd_short) != 0 &&
895 	    strcmp(init->nsfi_name, disc->nfd_spec) != 0) {
896 		return (true);
897 	}
898 
899 	init->nsfi_found = true;
900 	if (!nvme_set_feat_req_init_by_disc(ctrl, disc, &init->nsfi_req)) {
901 		nvme_ctrl_err_save(ctrl, &init->nsfi_err);
902 		init->nsfi_req = NULL;
903 	} else if (init->nsfi_discp != NULL) {
904 		if (!nvme_feat_disc_dup(ctrl, disc, init->nsfi_discp)) {
905 			nvme_ctrl_err_save(ctrl, &init->nsfi_err);
906 			nvme_set_feat_req_fini(init->nsfi_req);
907 			init->nsfi_req = NULL;
908 		}
909 	}
910 
911 	return (false);
912 }
913 
914 bool
915 nvme_set_feat_req_init_by_name(nvme_ctrl_t *ctrl, const char *name,
916     uint32_t df, nvme_feat_disc_t **discp, nvme_set_feat_req_t **reqp)
917 {
918 	nvme_set_feat_init_arg_t init;
919 
920 	/*
921 	 * Note, we consider discp optional unlike the name and reqp. The
922 	 * discover flags, df, will be validated by the discover functions we
923 	 * call.
924 	 */
925 	if (name == NULL) {
926 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
927 		    "encountered invalid pointer for log page name: %p", name));
928 	}
929 
930 	if (reqp == NULL) {
931 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
932 		    "encountered invalid nvme_set_feat_req_t output pointer: "
933 		    "%p", reqp));
934 	}
935 
936 	(void) memset(&init, 0, sizeof (init));
937 	init.nsfi_name = name;
938 	init.nsfi_discp = discp;
939 
940 	if (!nvme_feat_discover(ctrl, NVME_FEAT_SCOPE_CTRL |
941 	    NVME_FEAT_SCOPE_NS | NVME_FEAT_SCOPE_NVM, df,
942 	    nvme_set_feat_req_init_by_name_cb, &init)) {
943 		return (false);
944 	}
945 
946 	if (!init.nsfi_found) {
947 		return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_NAME_UNKNOWN, 0,
948 		    "failed to find feature with name %s", name));
949 	}
950 
951 	if (init.nsfi_req == NULL) {
952 		nvme_ctrl_err_set(ctrl, &init.nsfi_err);
953 		return (false);
954 	}
955 
956 	*reqp = init.nsfi_req;
957 	return (nvme_ctrl_success(ctrl));
958 }
959 
960 static void
961 nvme_set_feat_req_set_need(nvme_set_feat_req_t *req,
962     nvme_set_feat_req_field_t field)
963 {
964 	req->sfr_need |= 1 << field;
965 }
966 
967 static void
968 nvme_set_feat_req_clear_need(nvme_set_feat_req_t *req,
969     nvme_set_feat_req_field_t field)
970 {
971 	req->sfr_need &= ~(1 << field);
972 }
973 
974 static const nvme_field_check_t nvme_set_feat_check_fid = {
975 	nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_FID,
976 	NVME_ERR_FEAT_FID_RANGE, 0, 0
977 };
978 
979 bool
980 nvme_set_feat_req_set_fid(nvme_set_feat_req_t *req, uint32_t fid)
981 {
982 	if (!nvme_field_check_one(req->sfr_ctrl, fid, "set feature",
983 	    &nvme_set_feat_check_fid, req->sfr_allow)) {
984 		return (false);
985 	}
986 
987 	req->sfr_fid = fid;
988 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_FID);
989 	return (nvme_ctrl_success(req->sfr_ctrl));
990 }
991 
992 static const nvme_field_check_t nvme_set_feat_check_save = {
993 	nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_SAVE,
994 	NVME_ERR_FEAT_SAVE_RANGE, NVME_ERR_FEAT_SAVE_UNSUP, 0
995 };
996 
997 bool
998 nvme_set_feat_req_set_save(nvme_set_feat_req_t *req, bool save)
999 {
1000 	if (!nvme_field_check_one(req->sfr_ctrl, save, "set feature",
1001 	    &nvme_set_feat_check_save, req->sfr_allow)) {
1002 		return (false);
1003 	}
1004 
1005 	req->sfr_save = save;
1006 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_SAVE);
1007 	return (nvme_ctrl_success(req->sfr_ctrl));
1008 }
1009 
1010 static const nvme_field_check_t nvme_set_feat_check_nsid = {
1011 	nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_NSID,
1012 	NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE
1013 };
1014 
1015 bool
1016 nvme_set_feat_req_set_nsid(nvme_set_feat_req_t *req, uint32_t nsid)
1017 {
1018 	nvme_ctrl_t *ctrl = req->sfr_ctrl;
1019 
1020 	/*
1021 	 * Check the NSID first before we go into the generic validation code so
1022 	 * we can get a better error message.
1023 	 */
1024 	if (nsid == NVME_NSID_BCAST &&
1025 	    (req->sfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) {
1026 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
1027 		    "namespaces/controller nsid (0x%x) is not allowed for this "
1028 		    "feature, valid namespaces are [0x%x, 0x%x]", nsid,
1029 		    NVME_NSID_MIN, req->sfr_ctrl->nc_info.id_nn));
1030 	}
1031 
1032 	if (!nvme_field_check_one(req->sfr_ctrl, nsid, "set feature",
1033 	    &nvme_set_feat_check_nsid, req->sfr_allow)) {
1034 		return (false);
1035 	}
1036 
1037 	req->sfr_nsid = nsid;
1038 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_NSID);
1039 	return (nvme_ctrl_success(ctrl));
1040 }
1041 
1042 static const nvme_field_check_t nvme_set_feat_check_cdw11 = {
1043 	nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW11,
1044 	NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE
1045 };
1046 
1047 bool
1048 nvme_set_feat_req_set_cdw11(nvme_set_feat_req_t *req, uint32_t cdw11)
1049 {
1050 	if (!nvme_field_check_one(req->sfr_ctrl, cdw11, "set feature",
1051 	    &nvme_set_feat_check_cdw11, req->sfr_allow)) {
1052 		return (false);
1053 	}
1054 
1055 	req->sfr_cdw11 = cdw11;
1056 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW11);
1057 	return (nvme_ctrl_success(req->sfr_ctrl));
1058 }
1059 
1060 static const nvme_field_check_t nvme_set_feat_check_cdw12 = {
1061 	nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW12,
1062 	NVME_ERR_FEAT_CDW12_RANGE, NVME_ERR_FEAT_CDW12_UNSUP,
1063 	NVME_ERR_FEAT_CDW12_UNUSE
1064 };
1065 
1066 bool
1067 nvme_set_feat_req_set_cdw12(nvme_set_feat_req_t *req, uint32_t cdw12)
1068 {
1069 	if (!nvme_field_check_one(req->sfr_ctrl, cdw12, "set feature",
1070 	    &nvme_set_feat_check_cdw12, req->sfr_allow)) {
1071 		return (false);
1072 	}
1073 
1074 	req->sfr_cdw12 = cdw12;
1075 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW12);
1076 	return (nvme_ctrl_success(req->sfr_ctrl));
1077 }
1078 
1079 static const nvme_field_check_t nvme_set_feat_check_cdw13 = {
1080 	nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW13,
1081 	NVME_ERR_FEAT_CDW13_RANGE, NVME_ERR_FEAT_CDW13_UNSUP,
1082 	NVME_ERR_FEAT_CDW13_UNUSE
1083 };
1084 
1085 bool
1086 nvme_set_feat_req_set_cdw13(nvme_set_feat_req_t *req, uint32_t cdw13)
1087 {
1088 	if (!nvme_field_check_one(req->sfr_ctrl, cdw13, "set feature",
1089 	    &nvme_set_feat_check_cdw13, req->sfr_allow)) {
1090 		return (false);
1091 	}
1092 
1093 	req->sfr_cdw13 = cdw13;
1094 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW13);
1095 	return (nvme_ctrl_success(req->sfr_ctrl));
1096 }
1097 
1098 static const nvme_field_check_t nvme_set_feat_check_cdw15 = {
1099 	nvme_set_feat_fields, NVME_SET_FEAT_REQ_FIELD_CDW15,
1100 	NVME_ERR_FEAT_CDW15_RANGE, NVME_ERR_FEAT_CDW15_UNSUP,
1101 	NVME_ERR_FEAT_CDW15_UNUSE
1102 };
1103 
1104 bool
1105 nvme_set_feat_req_set_cdw15(nvme_set_feat_req_t *req, uint32_t cdw15)
1106 {
1107 	if (!nvme_field_check_one(req->sfr_ctrl, cdw15, "set feature",
1108 	    &nvme_set_feat_check_cdw15, req->sfr_allow)) {
1109 		return (false);
1110 	}
1111 
1112 	req->sfr_cdw15 = cdw15;
1113 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_CDW15);
1114 	return (nvme_ctrl_success(req->sfr_ctrl));
1115 }
1116 
1117 bool
1118 nvme_set_feat_req_set_input(nvme_set_feat_req_t *req, const void *buf,
1119     size_t len)
1120 {
1121 	if (buf == NULL) {
1122 		return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_BAD_PTR, 0,
1123 		    "set feature output buffer cannot be NULL"));
1124 	}
1125 
1126 	if (len == 0) {
1127 		return (nvme_ctrl_error(req->sfr_ctrl,
1128 		    NVME_ERR_FEAT_DATA_RANGE, 0, "set feature output length "
1129 		    "cannot be zero"));
1130 	}
1131 
1132 	if ((req->sfr_allow & (1 << NVME_SET_FEAT_REQ_FIELD_DPTR)) == 0) {
1133 		return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
1134 		    0, "field input (dptr) cannot be set in this set feature "
1135 		    "request"));
1136 	}
1137 
1138 	if (req->sfr_targ_len != 0 && len < req->sfr_targ_len) {
1139 		return (nvme_ctrl_error(req->sfr_ctrl,
1140 		    NVME_ERR_FEAT_DATA_RANGE, 0, "requested data length 0x%zx "
1141 		    "is less than the feature's target size of 0x%" PRIx64, len,
1142 		    req->sfr_targ_len));
1143 	}
1144 
1145 	req->sfr_buf = buf;
1146 	req->sfr_len = len;
1147 	nvme_set_feat_req_clear_need(req, NVME_SET_FEAT_REQ_FIELD_DPTR);
1148 	return (nvme_ctrl_success(req->sfr_ctrl));
1149 }
1150 
1151 bool
1152 nvme_set_feat_req_clear_input(nvme_set_feat_req_t *req)
1153 {
1154 	if ((req->sfr_allow & (1 << NVME_SET_FEAT_REQ_FIELD_DPTR)) == 0) {
1155 		return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
1156 		    0, "field input (dptr) cannot be cleared in this set "
1157 		    "feature request"));
1158 	}
1159 
1160 	req->sfr_buf = NULL;
1161 	req->sfr_len = 0;
1162 	nvme_set_feat_req_set_need(req, NVME_SET_FEAT_REQ_FIELD_DPTR);
1163 	return (nvme_ctrl_success(req->sfr_ctrl));
1164 }
1165 
1166 /*
1167  * As impact values are a libnvme specific item, we don't use common parsing
1168  * routines.
1169  */
1170 bool
1171 nvme_set_feat_req_set_impact(nvme_set_feat_req_t *req,
1172     nvme_disc_impact_t impact)
1173 {
1174 	if (!nvme_field_valid_impact(req->sfr_ctrl, impact,
1175 	    NVME_ERR_FEAT_IMPACT_RANGE)) {
1176 		return (false);
1177 	}
1178 
1179 	req->sfr_impact = impact;
1180 	return (nvme_ctrl_success(req->sfr_ctrl));
1181 }
1182 
1183 bool
1184 nvme_set_feat_req_get_cdw0(nvme_set_feat_req_t *req, uint32_t *cdw0)
1185 {
1186 	if (cdw0 == NULL) {
1187 		return (nvme_ctrl_error(req->sfr_ctrl, NVME_ERR_BAD_PTR, 0,
1188 		    "encountered invalid cdw0 output pointer: %p", cdw0));
1189 	}
1190 
1191 	if (!req->sfr_results_valid) {
1192 		return (nvme_ctrl_error(req->sfr_ctrl,
1193 		    NVME_ERR_FEAT_NO_RESULTS, 0, "set feature results are not "
1194 		    "currently valid and cannot be returned"));
1195 	}
1196 
1197 	*cdw0 = req->sfr_cdw0;
1198 	return (nvme_ctrl_success(req->sfr_ctrl));
1199 }
1200 
1201 bool
1202 nvme_set_feat_req_exec(nvme_set_feat_req_t *req)
1203 {
1204 	nvme_ctrl_t *ctrl = req->sfr_ctrl;
1205 	nvme_ioctl_set_feature_t feat;
1206 
1207 	/*
1208 	 * Immediately invalidate cdw0 so regardless of the results of this,
1209 	 * callers cannot be confused by whether or not a call accidentally
1210 	 * succeeded.
1211 	 */
1212 	req->sfr_results_valid = false;
1213 	req->sfr_cdw0 = 0;
1214 
1215 	if (req->sfr_need != 0) {
1216 		return (nvme_field_miss_err(ctrl, nvme_set_feat_fields,
1217 		    nvme_set_feat_nfields, NVME_ERR_SET_FEAT_REQ_MISSING_FIELDS,
1218 		    "set feature", req->sfr_need));
1219 	}
1220 
1221 	(void) memset(&feat, 0, sizeof (nvme_ioctl_set_feature_t));
1222 	feat.nisf_common.nioc_nsid = req->sfr_nsid;
1223 	feat.nisf_fid = req->sfr_fid;
1224 	feat.nisf_save = req->sfr_save;
1225 	feat.nisf_cdw11 = req->sfr_cdw11;
1226 	feat.nisf_cdw12 = req->sfr_cdw12;
1227 	feat.nisf_cdw13 = req->sfr_cdw13;
1228 	feat.nisf_cdw15 = req->sfr_cdw15;
1229 	if (req->sfr_impact != 0) {
1230 		feat.nisf_impact |= NVME_IMPACT_NS;
1231 	}
1232 	if (req->sfr_buf != NULL) {
1233 		feat.nisf_data = (uintptr_t)req->sfr_buf;
1234 		feat.nisf_len = req->sfr_len;
1235 	}
1236 
1237 	if (ioctl(ctrl->nc_fd, NVME_IOC_SET_FEATURE, &feat) != 0) {
1238 		int e = errno;
1239 		return (nvme_ioctl_syserror(ctrl, e, "set feature"));
1240 	}
1241 
1242 	if (feat.nisf_common.nioc_drv_err != NVME_IOCTL_E_OK) {
1243 		return (nvme_ioctl_error(ctrl, &feat.nisf_common,
1244 		    "set feature"));
1245 	}
1246 
1247 	req->sfr_results_valid = true;
1248 	req->sfr_cdw0 = feat.nisf_cdw0;
1249 
1250 	return (nvme_ctrl_success(ctrl));
1251 }
1252