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