1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * Common field and validation for NVMe Namespace related commands. This covers
18 * attaching and detaching controllers to namespaces as well as creating and
19 * deleting namespaces.
20 */
21
22 #include "nvme_common.h"
23
24 #include <sys/sysmacros.h>
25
26 static bool
nvme_ns_attach_field_valid_sel(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t act,char * msg,size_t msglen)27 nvme_ns_attach_field_valid_sel(const nvme_field_info_t *field,
28 const nvme_valid_ctrl_data_t *data, uint64_t act, char *msg, size_t msglen)
29 {
30 const uint64_t min = NVME_NS_ATTACH_CTRL_ATTACH;
31 const uint64_t max = NVME_NS_ATTACH_CTRL_DETACH;
32
33 return (nvme_field_range_check(field, min, max, msg, msglen, act));
34 }
35
36 const nvme_field_info_t nvme_ns_attach_fields[] = {
37 [NVME_NS_ATTACH_REQ_FIELD_SEL] = {
38 .nlfi_vers = &nvme_vers_1v2,
39 .nlfi_valid = nvme_ns_attach_field_valid_sel,
40 .nlfi_spec = "sel",
41 .nlfi_human = "select",
42 .nlfi_def_req = true,
43 .nlfi_def_allow = false
44 },
45 [NVME_NS_ATTACH_REQ_FIELD_NSID] = {
46 .nlfi_vers = &nvme_vers_1v2,
47 .nlfi_valid = nvme_field_valid_nsid,
48 .nlfi_spec = "nsid",
49 .nlfi_human = "namespace ID",
50 .nlfi_def_req = true,
51 .nlfi_def_allow = true
52 },
53 /*
54 * The interpretation of the data pointer is technically specific to the
55 * select command. As we don't actually accept any data, right now this
56 * is here just for libnvme tracking purposes.
57 */
58 [NVME_NS_ATTACH_REQ_FIELD_DPTR] = {
59 .nlfi_vers = &nvme_vers_1v2,
60 .nlfi_spec = "dptr",
61 .nlfi_human = "data pointer",
62 .nlfi_def_req = true,
63 .nlfi_def_allow = true
64 }
65 };
66
67 const size_t nvme_ns_attach_nfields = ARRAY_SIZE(nvme_ns_attach_fields);
68
69 const nvme_field_info_t nvme_ns_delete_fields[] = {
70 [NVME_NS_DELETE_REQ_FIELD_NSID] = {
71 .nlfi_vers = &nvme_vers_1v2,
72 .nlfi_valid = nvme_field_valid_nsid,
73 .nlfi_spec = "nsid",
74 .nlfi_human = "namespace ID",
75 .nlfi_def_req = true,
76 .nlfi_def_allow = true
77 }
78 };
79
80 const size_t nvme_ns_delete_nfields = ARRAY_SIZE(nvme_ns_delete_fields);
81
82 /*
83 * This is an integer which currently has only a single bit defined, bit 0.
84 * Though NVMe 2.1 will change this.
85 */
86 static bool
nvme_ns_create_field_valid_nmic(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t act,char * msg,size_t msglen)87 nvme_ns_create_field_valid_nmic(const nvme_field_info_t *field,
88 const nvme_valid_ctrl_data_t *data, uint64_t act, char *msg, size_t msglen)
89 {
90 return (nvme_field_mask_check(field, NVME_NS_MGMT_NMIC_MASK, msg,
91 msglen, act));
92 }
93
94 /*
95 * The NSZE and NCAP fields are related. Because these are in terms of a
96 * formatted LBA which we don't have quite at this moment, we can't confirm the
97 * maximum value; however, we do know enough to know a value of zero is wrong.
98 */
99 static bool
nvme_ns_attach_field_valid_nsze(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t act,char * msg,size_t msglen)100 nvme_ns_attach_field_valid_nsze(const nvme_field_info_t *field,
101 const nvme_valid_ctrl_data_t *data, uint64_t act, char *msg, size_t msglen)
102 {
103 return (nvme_field_range_check(field, 1, UINT64_MAX, msg, msglen, act));
104 }
105
106 /*
107 * The set of fields that are required to be allowed or defaults varies based
108 * upon the CSI that is used. For example the NVM and K/V command sets have
109 * different required fields. As such, the notion of what's required and
110 * settable is something that is determined by the CSI, hence why the required
111 * and allowed fields are missing for everything that isn't the CSI.
112 */
113 const nvme_field_info_t nvme_ns_create_fields[] = {
114 [NVME_NS_CREATE_REQ_FIELD_CSI] = {
115 /*
116 * The libnvme APIs require that a CSI is passed to create this,
117 * so this 2.0 isn't quite truly enforced.
118 */
119 .nlfi_vers = &nvme_vers_2v0,
120 .nlfi_max_size = NVME_NS_MGMT_MAX_CSI,
121 .nlfi_spec = "csi",
122 .nlfi_human = "command set ID",
123 .nlfi_def_req = true,
124 .nlfi_def_allow = false
125 },
126 [NVME_NS_CREATE_REQ_FIELD_NSZE] = {
127 .nlfi_vers = &nvme_vers_1v2,
128 /*
129 * The NSZE and NCAP fields are required to be related by a
130 * namespace granularity record; however, this was not
131 * introduced until NVMe 1.4. Absent this information, the main
132 * constraint is that the two are equivalent unless thin
133 * provisioning is supported. In addition, when setting the
134 * field, we don't know what LBA size someone has selected, so
135 * this can only be checked at command execution time.
136 */
137 .nlfi_valid = nvme_ns_attach_field_valid_nsze,
138 .nlfi_spec = "nsze",
139 .nlfi_human = "namespace size"
140 },
141 [NVME_NS_CREATE_REQ_FIELD_NCAP] = {
142 .nlfi_vers = &nvme_vers_1v2,
143 .nlfi_valid = nvme_ns_attach_field_valid_nsze,
144 .nlfi_spec = "ncap",
145 .nlfi_human = "namespace capacity"
146 },
147 [NVME_NS_CREATE_REQ_FIELD_FLBAS] = {
148 .nlfi_vers = &nvme_vers_1v2,
149 /*
150 * See the notes in common/nvme/nvme_format.c around this choice
151 * of maximum.
152 */
153 .nlfi_max_size = NVME_NS_MGMT_MAX_FLBAS,
154 .nlfi_spec = "flbas",
155 .nlfi_human = "formatted LBA size"
156 },
157 [NVME_NS_CREATE_REQ_FIELD_NMIC] = {
158 .nlfi_vers = &nvme_vers_1v2,
159 .nlfi_valid = nvme_ns_create_field_valid_nmic,
160 .nlfi_max_size = NVME_NS_MGMT_MAX_FLBAS,
161 .nlfi_spec = "nmic",
162 .nlfi_human = "namespace multi-path I/O and namespace sharing "
163 "capabilities"
164 }
165 };
166
167 const size_t nvme_ns_create_nfields = ARRAY_SIZE(nvme_ns_create_fields);
168
169 /*
170 * These are the default fields that are required for an NVM command. The CSI
171 * cannot be set. The we allow to default to zero, unshared, and therefore is
172 * optional.
173 */
174 const nvme_ns_create_req_field_t nvme_ns_create_fields_nvm_req[] = {
175 NVME_NS_CREATE_REQ_FIELD_NSZE, NVME_NS_CREATE_REQ_FIELD_NCAP,
176 NVME_NS_CREATE_REQ_FIELD_FLBAS
177 };
178 const size_t nvme_ns_create_fields_nvm_nreq =
179 ARRAY_SIZE(nvme_ns_create_fields_nvm_req);
180
181 const nvme_ns_create_req_field_t nvme_ns_create_fields_nvm_allow[] = {
182 NVME_NS_CREATE_REQ_FIELD_NSZE, NVME_NS_CREATE_REQ_FIELD_NCAP,
183 NVME_NS_CREATE_REQ_FIELD_FLBAS, NVME_NS_CREATE_REQ_FIELD_NMIC
184 };
185 const size_t nvme_ns_create_fields_nvm_nallow =
186 ARRAY_SIZE(nvme_ns_create_fields_nvm_allow);
187
188 bool
nvme_nsmgmt_cmds_supported(const nvme_valid_ctrl_data_t * data)189 nvme_nsmgmt_cmds_supported(const nvme_valid_ctrl_data_t *data)
190 {
191 if (nvme_vers_atleast(data->vcd_vers, &nvme_vers_1v2)) {
192 return (data->vcd_id->id_oacs.oa_nsmgmt != 0);
193 }
194
195 return (false);
196 }
197