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 * Namespace information.
18 */
19
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23
24 #include "libnvme_impl.h"
25
26 bool
nvme_ns_info_error(nvme_ns_info_t * info,nvme_info_err_t err,int32_t sys,const char * fmt,...)27 nvme_ns_info_error(nvme_ns_info_t *info, nvme_info_err_t err, int32_t sys,
28 const char *fmt, ...)
29 {
30 int ret;
31 va_list ap;
32
33 info->nni_err = err;
34 info->nni_syserr = sys;
35 va_start(ap, fmt);
36 ret = vsnprintf(info->nni_errmsg, sizeof (info->nni_errmsg), fmt, ap);
37 va_end(ap);
38 if (ret >= sizeof (info->nni_errmsg)) {
39 info->nni_errlen = sizeof (info->nni_errmsg) - 1;
40 } else if (ret <= 0) {
41 info->nni_errlen = 0;
42 info->nni_errmsg[0] = '\0';
43 } else {
44 info->nni_errlen = (size_t)ret;
45 }
46
47 return (false);
48 }
49
50 bool
nvme_ns_info_success(nvme_ns_info_t * info)51 nvme_ns_info_success(nvme_ns_info_t *info)
52 {
53 info->nni_err = NVME_INFO_ERR_OK;
54 info->nni_syserr = 0;
55 info->nni_errmsg[0] = '\0';
56 info->nni_errlen = 0;
57
58 return (true);
59 }
60
61 nvme_info_err_t
nvme_ns_info_err(nvme_ns_info_t * info)62 nvme_ns_info_err(nvme_ns_info_t *info)
63 {
64 return (info->nni_err);
65 }
66
67 int32_t
nvme_ns_info_syserr(nvme_ns_info_t * info)68 nvme_ns_info_syserr(nvme_ns_info_t *info)
69 {
70 return (info->nni_syserr);
71 }
72
73 const char *
nvme_ns_info_errmsg(nvme_ns_info_t * info)74 nvme_ns_info_errmsg(nvme_ns_info_t *info)
75 {
76 return (info->nni_errmsg);
77 }
78
79 size_t
nvme_ns_info_errlen(nvme_ns_info_t * info)80 nvme_ns_info_errlen(nvme_ns_info_t *info)
81 {
82 return (info->nni_errlen);
83 }
84
85 const char *
nvme_ns_info_errtostr(nvme_ns_info_t * info,nvme_info_err_t err)86 nvme_ns_info_errtostr(nvme_ns_info_t *info, nvme_info_err_t err)
87 {
88 return (nvme_ctrl_info_errtostr(NULL, err));
89 }
90
91 void
nvme_ns_info_free(nvme_ns_info_t * info)92 nvme_ns_info_free(nvme_ns_info_t *info)
93 {
94 free(info);
95 }
96
97 bool
nvme_ns_info_snap(nvme_ns_t * ns,nvme_ns_info_t ** infop)98 nvme_ns_info_snap(nvme_ns_t *ns, nvme_ns_info_t **infop)
99 {
100 nvme_ctrl_t *ctrl = ns->nn_ctrl;
101 nvme_ns_info_t *info;
102
103 if (infop == NULL) {
104 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
105 "encountered invalid nvme_ns_info_t output pointer: %p",
106 infop));
107 }
108
109 info = calloc(1, sizeof (nvme_ns_info_t));
110 if (info == NULL) {
111 int e = errno;
112 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
113 "allocate memory for a new nvme_ns_info_t: %s",
114 strerror(e)));
115 }
116
117 info->nni_nsid = ns->nn_nsid;
118 if (!nvme_ioc_ns_info(ns->nn_ctrl, ns->nn_nsid, &info->nni_info)) {
119 nvme_ns_info_free(info);
120 return (false);
121 }
122 info->nni_vers = ns->nn_ctrl->nc_vers;
123 info->nni_level = nvme_ns_state_to_disc_level(info->nni_info.nni_state);
124
125 *infop = info;
126 return (nvme_ctrl_success(ctrl));
127 }
128
129 bool
nvme_ctrl_ns_info_snap(nvme_ctrl_t * ctrl,uint32_t nsid,nvme_ns_info_t ** infop)130 nvme_ctrl_ns_info_snap(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_ns_info_t **infop)
131 {
132 nvme_ns_info_t *info;
133
134 if (infop == NULL) {
135 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
136 "encountered invalid nvme_ns_info_t output pointer: %p",
137 infop));
138 }
139
140 if (nsid < NVME_NSID_MIN || nsid > ctrl->nc_info.id_nn) {
141 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "requested "
142 "namespace %u is invalid, valid namespaces are [0x%x, "
143 "0x%x]", nsid, NVME_NSID_MIN, ctrl->nc_info.id_nn));
144 }
145
146 info = calloc(1, sizeof (nvme_ns_info_t));
147 if (info == NULL) {
148 int e = errno;
149 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
150 "allocate memory for a new nvme_ns_info_t: %s",
151 strerror(e)));
152 }
153
154 info->nni_nsid = nsid;
155 if (!nvme_ioc_ns_info(ctrl, nsid, &info->nni_info)) {
156 nvme_ns_info_free(info);
157 return (false);
158 }
159 info->nni_vers = ctrl->nc_vers;
160 info->nni_level = nvme_ns_state_to_disc_level(info->nni_info.nni_state);
161
162 *infop = info;
163 return (nvme_ctrl_success(ctrl));
164 }
165
166 uint32_t
nvme_ns_info_nsid(nvme_ns_info_t * info)167 nvme_ns_info_nsid(nvme_ns_info_t *info)
168 {
169 return (info->nni_nsid);
170 }
171
172 const nvme_identify_nsid_t *
nvme_ns_info_identify(nvme_ns_info_t * info)173 nvme_ns_info_identify(nvme_ns_info_t *info)
174 {
175 return (&info->nni_info.nni_id);
176 }
177
178 nvme_ns_disc_level_t
nvme_ns_info_level(nvme_ns_info_t * info)179 nvme_ns_info_level(nvme_ns_info_t *info)
180 {
181 return (info->nni_level);
182 }
183
184 static bool
nvme_ns_info_req_active(nvme_ns_info_t * info,const nvme_version_t * vers)185 nvme_ns_info_req_active(nvme_ns_info_t *info, const nvme_version_t *vers)
186 {
187 if (info->nni_level < NVME_NS_DISC_F_ACTIVE) {
188 return (nvme_ns_info_error(info, NVME_INFO_ERR_NS_INACTIVE, 0,
189 "information cannot be provided for inactive namespaces: "
190 "namespace is %s (0x%x)",
191 nvme_nsleveltostr(info->nni_level), info->nni_level));
192 }
193
194 if (!nvme_vers_ns_info_atleast(info, vers)) {
195 return (nvme_ns_info_error(info, NVME_INFO_ERR_VERSION, 0,
196 "cannot provide information, device must be at least "
197 "version %u.%u, but is %u.%u", vers->v_major, vers->v_minor,
198 info->nni_vers.v_major, info->nni_vers.v_minor));
199 }
200
201 return (true);
202 }
203
204 bool
nvme_ns_info_nguid(nvme_ns_info_t * info,uint8_t nguid[16])205 nvme_ns_info_nguid(nvme_ns_info_t *info, uint8_t nguid[16])
206 {
207 const uint8_t zero_guid[16] = { 0 };
208
209 if (!nvme_ns_info_req_active(info, &nvme_vers_1v2)) {
210 return (false);
211 }
212
213 if (memcmp(zero_guid, info->nni_info.nni_id.id_nguid,
214 sizeof (zero_guid)) == 0) {
215 return (nvme_ns_info_error(info, NVME_INFO_ERR_MISSING_CAP, 0,
216 "Namespace GUID invalid: found all 0s"));
217 }
218
219 (void) memcpy(nguid, info->nni_info.nni_id.id_nguid,
220 sizeof (info->nni_info.nni_id.id_nguid));
221
222 return (nvme_ns_info_success(info));
223 }
224
225 bool
nvme_ns_info_eui64(nvme_ns_info_t * info,uint8_t eui64[8])226 nvme_ns_info_eui64(nvme_ns_info_t *info, uint8_t eui64[8])
227 {
228 const uint8_t zero_eui64[8] = { 0 };
229
230 if (!nvme_ns_info_req_active(info, &nvme_vers_1v1)) {
231 return (false);
232 }
233
234 if (memcmp(zero_eui64, info->nni_info.nni_id.id_eui64,
235 sizeof (zero_eui64)) == 0) {
236 return (nvme_ns_info_error(info, NVME_INFO_ERR_MISSING_CAP, 0,
237 "Namespace EUI64 invalid: found all 0s"));
238 }
239
240 (void) memcpy(eui64, info->nni_info.nni_id.id_eui64,
241 sizeof (info->nni_info.nni_id.id_eui64));
242
243 return (nvme_ns_info_success(info));
244 }
245
246 bool
nvme_ns_info_size(nvme_ns_info_t * info,uint64_t * sizep)247 nvme_ns_info_size(nvme_ns_info_t *info, uint64_t *sizep)
248 {
249 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
250 return (false);
251 }
252
253 *sizep = info->nni_info.nni_id.id_nsize;
254 return (nvme_ns_info_success(info));
255 }
256
257 bool
nvme_ns_info_cap(nvme_ns_info_t * info,uint64_t * capp)258 nvme_ns_info_cap(nvme_ns_info_t *info, uint64_t *capp)
259 {
260 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
261 return (false);
262 }
263
264 *capp = info->nni_info.nni_id.id_ncap;
265 return (nvme_ns_info_success(info));
266 }
267
268 bool
nvme_ns_info_use(nvme_ns_info_t * info,uint64_t * usep)269 nvme_ns_info_use(nvme_ns_info_t *info, uint64_t *usep)
270 {
271 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
272 return (false);
273 }
274
275 *usep = info->nni_info.nni_id.id_nuse;
276 return (nvme_ns_info_success(info));
277 }
278
279 bool
nvme_ns_info_nformats(nvme_ns_info_t * info,uint32_t * nfmtp)280 nvme_ns_info_nformats(nvme_ns_info_t *info, uint32_t *nfmtp)
281 {
282 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
283 return (false);
284 }
285
286 *nfmtp = info->nni_info.nni_id.id_nlbaf + 1;
287 return (nvme_ns_info_success(info));
288 }
289
290 bool
nvme_ns_info_format(nvme_ns_info_t * info,uint32_t idx,const nvme_nvm_lba_fmt_t ** fmtp)291 nvme_ns_info_format(nvme_ns_info_t *info, uint32_t idx,
292 const nvme_nvm_lba_fmt_t **fmtp)
293 {
294 uint32_t max;
295 const nvme_identify_nsid_t *nsid = &info->nni_info.nni_id;
296
297 if (!nvme_ns_info_nformats(info, &max)) {
298 return (false);
299 }
300
301 if (idx >= max) {
302 return (nvme_ns_info_error(info, NVME_INFO_ERR_BAD_FMT, 0,
303 "requested index %u is invalid: valid range is [0, %u]",
304 idx, max - 1));
305 }
306
307 if (!info->nni_lbaf_valid[idx]) {
308 uint8_t lbads = nsid->id_lbaf[idx].lbaf_lbads;
309
310 if (lbads == 0) {
311 return (nvme_ns_info_error(info, NVME_INFO_ERR_BAD_FMT,
312 0, "format %u is not actually valid due to 0 LBA "
313 "data size even though it is considered a valid "
314 "LBA format by NLBAF", lbads));
315 }
316
317 if (lbads < 9) {
318 return (nvme_ns_info_error(info,
319 NVME_INFO_ERR_BAD_FMT_DATA, 0, "NVMe devices are "
320 "not allowed to have a LBA data size of less than "
321 "512 bytes, found raw shift value of %u for "
322 "format %u", lbads, idx));
323 }
324
325 if (lbads >= 64) {
326 return (nvme_ns_info_error(info,
327 NVME_INFO_ERR_BAD_FMT_DATA, 0, "LBA format %u has "
328 "LBA data size greater " "than 64 (%u), cannot be "
329 "represented as a byte size", idx, lbads));
330 }
331
332 info->nni_lbaf[idx].nnlf_id = idx;
333 info->nni_lbaf[idx].nnlf_ms = nsid->id_lbaf[idx].lbaf_ms;
334 info->nni_lbaf[idx].nnlf_lbasz = 1ULL << lbads;
335 info->nni_lbaf[idx].nnlf_rel = nsid->id_lbaf[idx].lbaf_rp;
336 info->nni_lbaf_valid[idx] = true;
337 }
338
339 *fmtp = &info->nni_lbaf[idx];
340 return (nvme_ns_info_success(info));
341 }
342
343
344 bool
nvme_ns_info_curformat(nvme_ns_info_t * info,const nvme_nvm_lba_fmt_t ** fmtp)345 nvme_ns_info_curformat(nvme_ns_info_t *info, const nvme_nvm_lba_fmt_t **fmtp)
346 {
347 uint32_t idx;
348
349 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
350 return (false);
351 }
352
353 idx = info->nni_info.nni_id.id_flbas.lba_format;
354 return (nvme_ns_info_format(info, idx, fmtp));
355 }
356
357 bool
nvme_ns_info_bd_addr(nvme_ns_info_t * info,const char ** addrp)358 nvme_ns_info_bd_addr(nvme_ns_info_t *info, const char **addrp)
359 {
360 if (info->nni_level < NVME_NS_DISC_F_BLKDEV) {
361 return (nvme_ns_info_error(info, NVME_INFO_ERR_NS_NO_BLKDEV, 0,
362 "the blkdev address cannot be provided for namespaces "
363 "without blkdev attached: namespace is %s (0x%x)",
364 nvme_nsleveltostr(info->nni_level), info->nni_level));
365 }
366
367 *addrp = info->nni_info.nni_addr;
368 return (nvme_ns_info_success(info));
369 }
370