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
511 static void
nvme_get_feat_req_clear_need(nvme_get_feat_req_t * req,nvme_get_feat_req_field_t field)512 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req,
513 nvme_get_feat_req_field_t field)
514 {
515 req->gfr_need &= ~(1 << field);
516 }
517
518 static const nvme_field_check_t nvme_get_feat_check_fid = {
519 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID,
520 NVME_ERR_FEAT_FID_RANGE, 0, 0
521 };
522
523 bool
nvme_get_feat_req_set_fid(nvme_get_feat_req_t * req,uint32_t fid)524 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid)
525 {
526 if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature",
527 &nvme_get_feat_check_fid, req->gfr_allow)) {
528 return (false);
529 }
530
531 req->gfr_fid = fid;
532 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID);
533 return (nvme_ctrl_success(req->gfr_ctrl));
534 }
535
536 static const nvme_field_check_t nvme_get_feat_check_sel = {
537 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL,
538 NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0
539 };
540
541 bool
nvme_get_feat_req_set_sel(nvme_get_feat_req_t * req,uint32_t sel)542 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel)
543 {
544 if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature",
545 &nvme_get_feat_check_sel, req->gfr_allow)) {
546 return (false);
547 }
548
549 if (sel == NVME_FEATURE_SEL_SUPPORTED) {
550 return (nvme_ctrl_error(req->gfr_ctrl,
551 NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do "
552 "not support supported capabilities selector"));
553 }
554
555 req->gfr_sel = sel;
556 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL);
557 return (nvme_ctrl_success(req->gfr_ctrl));
558 }
559
560 static const nvme_field_check_t nvme_get_feat_check_cdw11 = {
561 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11,
562 NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE
563 };
564
565 bool
nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t * req,uint32_t cdw11)566 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11)
567 {
568 if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature",
569 &nvme_get_feat_check_cdw11, req->gfr_allow)) {
570 return (false);
571 }
572
573 req->gfr_cdw11 = cdw11;
574 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11);
575 return (nvme_ctrl_success(req->gfr_ctrl));
576 }
577
578 static const nvme_field_check_t nvme_get_feat_check_nsid = {
579 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID,
580 NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE
581 };
582
583 bool
nvme_get_feat_req_set_nsid(nvme_get_feat_req_t * req,uint32_t nsid)584 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid)
585 {
586 nvme_ctrl_t *ctrl = req->gfr_ctrl;
587
588 /*
589 * Check the NSID first before we go into the generic validation code so
590 * we can get a better error message.
591 */
592 if (nsid == NVME_NSID_BCAST &&
593 (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) {
594 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
595 "namespaces/controller nsid (0x%x) is not allowed for this "
596 "feature, valid namespaces are [0x%x, 0x%x]", nsid,
597 NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn));
598 }
599
600 if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature",
601 &nvme_get_feat_check_nsid, req->gfr_allow)) {
602 return (false);
603 }
604
605 req->gfr_nsid = nsid;
606 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID);
607 return (nvme_ctrl_success(ctrl));
608 }
609
610 bool
nvme_get_feat_req_set_output(nvme_get_feat_req_t * req,void * buf,size_t len)611 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len)
612 {
613 if (buf == NULL) {
614 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
615 "get feature output buffer cannot be NULL"));
616 }
617
618 if (len == 0) {
619 return (nvme_ctrl_error(req->gfr_ctrl,
620 NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length "
621 "cannot be zero"));
622 }
623
624 /*
625 * Something to consider for the future here is that we know the fixed
626 * size data that we're expecting for the feature. It would be nice if
627 * we validated that we have that size now versus later.
628 */
629
630 req->gfr_buf = buf;
631 req->gfr_len = len;
632 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
633 return (nvme_ctrl_success(req->gfr_ctrl));
634 }
635
636 bool
nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t * req,uint32_t * cdw0)637 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0)
638 {
639 if (cdw0 == NULL) {
640 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
641 "encountered invalid cdw0 output pointer: %p", cdw0));
642 }
643
644 if (!req->gfr_results_valid) {
645 return (nvme_ctrl_error(req->gfr_ctrl,
646 NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not "
647 "currently valid and cannot be returned"));
648 }
649
650 *cdw0 = req->gfr_cdw0;
651 return (nvme_ctrl_success(req->gfr_ctrl));
652 }
653
654 bool
nvme_get_feat_req_exec(nvme_get_feat_req_t * req)655 nvme_get_feat_req_exec(nvme_get_feat_req_t *req)
656 {
657 nvme_ctrl_t *ctrl = req->gfr_ctrl;
658 nvme_ioctl_get_feature_t feat;
659
660 /*
661 * Because this has been called, we need to immediately invalidate our
662 * stored cdw0 results. We do this as a precaution regardless of whether
663 * or not it is valid.
664 */
665 req->gfr_results_valid = false;
666 req->gfr_cdw0 = 0;
667
668 if (req->gfr_need != 0) {
669 return (nvme_field_miss_err(ctrl, nvme_get_feat_fields,
670 nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS,
671 "get feature", req->gfr_need));
672 }
673
674 (void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t));
675 feat.nigf_common.nioc_nsid = req->gfr_nsid;
676 feat.nigf_fid = req->gfr_fid;
677 feat.nigf_sel = req->gfr_sel;
678 feat.nigf_cdw11 = req->gfr_cdw11;
679 if (req->gfr_buf != NULL) {
680 feat.nigf_data = (uintptr_t)req->gfr_buf;
681 feat.nigf_len = req->gfr_len;
682 }
683
684 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) {
685 int e = errno;
686 return (nvme_ioctl_syserror(ctrl, e, "get feature"));
687 }
688
689 if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) {
690 return (nvme_ioctl_error(ctrl, &feat.nigf_common,
691 "get feature"));
692 }
693
694 req->gfr_results_valid = true;
695 req->gfr_cdw0 = feat.nigf_cdw0;
696
697 return (nvme_ctrl_success(ctrl));
698 }
699