xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_ns_mgmt.c (revision f5f0964ce91892f7482efc86903b0ec7c7b6ba66)
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  * This implements support for Namespace Management and Namespace Attach
18  * commands.
19  */
20 
21 #include <string.h>
22 #include <unistd.h>
23 
24 #include "libnvme_impl.h"
25 
26 void
nvme_ns_attach_req_fini(nvme_ns_attach_req_t * req)27 nvme_ns_attach_req_fini(nvme_ns_attach_req_t *req)
28 {
29 	free(req);
30 }
31 
32 static void
nvme_ns_attach_req_clear_need(nvme_ns_attach_req_t * req,nvme_ns_attach_req_field_t field)33 nvme_ns_attach_req_clear_need(nvme_ns_attach_req_t *req,
34     nvme_ns_attach_req_field_t field)
35 {
36 	req->nar_need &= ~(1 << field);
37 }
38 
39 bool
nvme_ns_attach_req_init_by_sel(nvme_ctrl_t * ctrl,uint32_t sel,nvme_ns_attach_req_t ** reqp)40 nvme_ns_attach_req_init_by_sel(nvme_ctrl_t *ctrl, uint32_t sel,
41     nvme_ns_attach_req_t **reqp)
42 {
43 	nvme_ns_attach_req_t *req;
44 	nvme_valid_ctrl_data_t ctrl_data;
45 
46 	if (reqp == NULL) {
47 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
48 		    "encountered invalid nvme_ns_attach_req_t output pointer: "
49 		    "%p", reqp));
50 	}
51 
52 	ctrl_data.vcd_vers = &ctrl->nc_vers;
53 	ctrl_data.vcd_id = &ctrl->nc_info;
54 
55 	if (!nvme_nsmgmt_cmds_supported(&ctrl_data)) {
56 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_MGMT_UNSUP_BY_DEV, 0,
57 		    "controller does not support namespace management"));
58 	}
59 
60 	/*
61 	 * See discussion in nvme_ns_create_req_init_by_csi() down below for
62 	 * rationale around the single error here.
63 	 */
64 	if (sel != NVME_NS_ATTACH_CTRL_ATTACH &&
65 	    sel != NVME_NS_ATTACH_CTRL_DETACH) {
66 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_ATTACH_BAD_SEL, 0,
67 		    "the system (and possibly device) does not support "
68 		    "attaching namespaces with selector 0x%x", sel));
69 	}
70 
71 	req = calloc(1, sizeof (nvme_ns_attach_req_t));
72 	if (req == NULL) {
73 		int e = errno;
74 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
75 		    "allocate memory for a new nvme_ns_attach_req_t: %s",
76 		    strerror(e)));
77 	}
78 
79 	req->nar_ctrl = ctrl;
80 	req->nar_sel = sel;
81 	for (size_t i = 0; i < nvme_ns_attach_nfields; i++) {
82 		if (nvme_ns_attach_fields[i].nlfi_def_req) {
83 			req->nar_need |= 1 << i;
84 		}
85 	}
86 	nvme_ns_attach_req_clear_need(req, NVME_NS_ATTACH_REQ_FIELD_SEL);
87 
88 	*reqp = req;
89 	return (nvme_ctrl_success(ctrl));
90 }
91 
92 static const nvme_field_check_t nvme_ns_attach_check_nsid = {
93 	nvme_ns_attach_fields, NVME_NS_ATTACH_REQ_FIELD_NSID,
94 	NVME_ERR_NS_RANGE, 0, 0
95 };
96 
97 bool
nvme_ns_attach_req_set_nsid(nvme_ns_attach_req_t * req,uint32_t nsid)98 nvme_ns_attach_req_set_nsid(nvme_ns_attach_req_t *req, uint32_t nsid)
99 {
100 	if (!nvme_field_check_one(req->nar_ctrl, nsid, "namespace attach",
101 	    &nvme_ns_attach_check_nsid, 0)) {
102 		return (false);
103 	}
104 
105 	req->nar_nsid = nsid;
106 	nvme_ns_attach_req_clear_need(req, NVME_NS_ATTACH_REQ_FIELD_NSID);
107 	return (nvme_ctrl_success(req->nar_ctrl));
108 }
109 
110 /*
111  * Right now we don't support setting an explicit controller list in the kernel
112  * so this is a short-hand for saying just do it for my current controller
113  * without requiring us to actually set anything here, we just need to clear
114  * that this has been explicitly set that way the target is not implicit.
115  */
116 bool
nvme_ns_attach_req_set_ctrlid_self(nvme_ns_attach_req_t * req)117 nvme_ns_attach_req_set_ctrlid_self(nvme_ns_attach_req_t *req)
118 {
119 	nvme_ns_attach_req_clear_need(req, NVME_NS_ATTACH_REQ_FIELD_DPTR);
120 	return (nvme_ctrl_success(req->nar_ctrl));
121 }
122 
123 bool
nvme_ns_attach_req_exec(nvme_ns_attach_req_t * req)124 nvme_ns_attach_req_exec(nvme_ns_attach_req_t *req)
125 {
126 	nvme_ctrl_t *ctrl = req->nar_ctrl;
127 	nvme_ioctl_common_t common;
128 	int code;
129 
130 	if (req->nar_need != 0) {
131 		return (nvme_field_miss_err(ctrl, nvme_ns_attach_fields,
132 		    nvme_ns_attach_nfields,
133 		    NVME_ERR_NS_ATTACH_REQ_MISSING_FIELDS, "namespace attach",
134 		    req->nar_need));
135 	}
136 
137 	(void) memset(&common, 0, sizeof (common));
138 	common.nioc_nsid = req->nar_nsid;
139 
140 	code = req->nar_sel == NVME_NS_ATTACH_CTRL_ATTACH ?
141 	    NVME_IOC_CTRL_ATTACH : NVME_IOC_CTRL_DETACH;
142 	if (ioctl(ctrl->nc_fd, code, &common) != 0) {
143 		int e = errno;
144 		return (nvme_ioctl_syserror(ctrl, e, "namespace attach"));
145 	}
146 
147 	if (common.nioc_drv_err != NVME_IOCTL_E_OK) {
148 		return (nvme_ioctl_error(ctrl, &common, "namespace attach"));
149 	}
150 
151 	return (nvme_ctrl_success(ctrl));
152 }
153 
154 void
nvme_ns_delete_req_fini(nvme_ns_delete_req_t * req)155 nvme_ns_delete_req_fini(nvme_ns_delete_req_t *req)
156 {
157 	free(req);
158 }
159 
160 bool
nvme_ns_delete_req_init(nvme_ctrl_t * ctrl,nvme_ns_delete_req_t ** reqp)161 nvme_ns_delete_req_init(nvme_ctrl_t *ctrl, nvme_ns_delete_req_t **reqp)
162 {
163 	nvme_ns_delete_req_t *req;
164 	nvme_valid_ctrl_data_t ctrl_data;
165 
166 	if (reqp == NULL) {
167 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
168 		    "encountered invalid nvme_ns_delete_req_t output pointer: "
169 		    "%p", reqp));
170 	}
171 
172 	ctrl_data.vcd_vers = &ctrl->nc_vers;
173 	ctrl_data.vcd_id = &ctrl->nc_info;
174 
175 	if (!nvme_nsmgmt_cmds_supported(&ctrl_data)) {
176 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_MGMT_UNSUP_BY_DEV, 0,
177 		    "controller does not support namespace management"));
178 	}
179 
180 	req = calloc(1, sizeof (nvme_ns_delete_req_t));
181 	if (req == NULL) {
182 		int e = errno;
183 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
184 		    "allocate memory for a new nvme_ns_delete_req_t: %s",
185 		    strerror(e)));
186 	}
187 
188 	req->ndr_ctrl = ctrl;
189 	for (size_t i = 0; i < nvme_ns_delete_nfields; i++) {
190 		if (nvme_ns_delete_fields[i].nlfi_def_req) {
191 			req->ndr_need |= 1 << i;
192 		}
193 	}
194 
195 	*reqp = req;
196 	return (nvme_ctrl_success(ctrl));
197 }
198 
199 static void
nvme_ns_delete_req_clear_need(nvme_ns_delete_req_t * req,nvme_ns_delete_req_field_t field)200 nvme_ns_delete_req_clear_need(nvme_ns_delete_req_t *req,
201     nvme_ns_delete_req_field_t field)
202 {
203 	req->ndr_need &= ~(1 << field);
204 }
205 
206 static const nvme_field_check_t nvme_ns_delete_check_nsid = {
207 	nvme_ns_delete_fields, NVME_NS_DELETE_REQ_FIELD_NSID,
208 	NVME_ERR_NS_RANGE, 0, 0
209 };
210 
211 bool
nvme_ns_delete_req_set_nsid(nvme_ns_delete_req_t * req,uint32_t nsid)212 nvme_ns_delete_req_set_nsid(nvme_ns_delete_req_t *req, uint32_t nsid)
213 {
214 	if (!nvme_field_check_one(req->ndr_ctrl, nsid, "namespace delete",
215 	    &nvme_ns_delete_check_nsid, 0)) {
216 		return (false);
217 	}
218 
219 	req->ndr_nsid = nsid;
220 	nvme_ns_delete_req_clear_need(req, NVME_NS_DELETE_REQ_FIELD_NSID);
221 	return (nvme_ctrl_success(req->ndr_ctrl));
222 }
223 
224 bool
nvme_ns_delete_req_exec(nvme_ns_delete_req_t * req)225 nvme_ns_delete_req_exec(nvme_ns_delete_req_t *req)
226 {
227 	nvme_ctrl_t *ctrl = req->ndr_ctrl;
228 	nvme_ioctl_common_t common;
229 
230 	if (req->ndr_need != 0) {
231 		return (nvme_field_miss_err(ctrl, nvme_ns_delete_fields,
232 		    nvme_ns_delete_nfields,
233 		    NVME_ERR_NS_DELETE_REQ_MISSING_FIELDS, "namespace delete",
234 		    req->ndr_need));
235 	}
236 
237 	(void) memset(&common, 0, sizeof (common));
238 	common.nioc_nsid = req->ndr_nsid;
239 
240 	if (ioctl(ctrl->nc_fd, NVME_IOC_NS_DELETE, &common) != 0) {
241 		int e = errno;
242 		return (nvme_ioctl_syserror(ctrl, e, "namespace delete"));
243 	}
244 
245 	if (common.nioc_drv_err != NVME_IOCTL_E_OK) {
246 		return (nvme_ioctl_error(ctrl, &common, "namespace delete"));
247 	}
248 
249 	return (nvme_ctrl_success(ctrl));
250 }
251 
252 void
nvme_ns_create_req_fini(nvme_ns_create_req_t * req)253 nvme_ns_create_req_fini(nvme_ns_create_req_t *req)
254 {
255 	free(req);
256 }
257 
258 bool
nvme_ns_create_req_init_by_csi(nvme_ctrl_t * ctrl,nvme_csi_t csi,nvme_ns_create_req_t ** reqp)259 nvme_ns_create_req_init_by_csi(nvme_ctrl_t *ctrl, nvme_csi_t csi,
260     nvme_ns_create_req_t **reqp)
261 {
262 	nvme_ns_create_req_t *req;
263 	nvme_valid_ctrl_data_t ctrl_data;
264 
265 	if (reqp == NULL) {
266 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
267 		    "encountered invalid nvme_ns_create_req_t output pointer: "
268 		    "%p", reqp));
269 	}
270 
271 	ctrl_data.vcd_vers = &ctrl->nc_vers;
272 	ctrl_data.vcd_id = &ctrl->nc_info;
273 
274 	if (!nvme_nsmgmt_cmds_supported(&ctrl_data)) {
275 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_MGMT_UNSUP_BY_DEV, 0,
276 		    "controller does not support namespace management"));
277 	}
278 
279 	/*
280 	 * The CSI determines what fields are supported and required. Not all
281 	 * CSIs support namespace creation and in addition, we only support the
282 	 * NVM CSI. The notion of CSIs was added in NVMe 2.0. There are several
283 	 * things that could go wrong here:
284 	 *
285 	 * - We could have an NVMe controller that is pre-2.0 and therefore
286 	 *   anything other than the NVM CSI is invalid (it's implicit pre-2.0).
287 	 * - We could have a CSI that's just not defined by any version of the
288 	 *   spec.
289 	 * - We could have a CSI that's defined by a spec that's newer than the
290 	 *   device.
291 	 * - The CSI may not support namespace creation. The device may not even
292 	 *   support the CSI!
293 	 *
294 	 * In addition, the kernel doesn't support anything other than the NVM
295 	 * CSI today. Rather than break these all apart, we give a generic error
296 	 * message about this. We can make this higher fidelity in the future.
297 	 */
298 	if (csi != NVME_CSI_NVM) {
299 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_CREATE_BAD_CSI, 0,
300 		    "the system (and possibly device) does not support "
301 		    "creating namespaces with CSI 0x%x", csi));
302 	}
303 
304 	req = calloc(1, sizeof (nvme_ns_create_req_t));
305 	if (req == NULL) {
306 		int e = errno;
307 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
308 		    "allocate memory for a new nvme_ns_create_req_t: %s",
309 		    strerror(e)));
310 	}
311 
312 	for (size_t i = 0; i < nvme_ns_create_fields_nvm_nreq; i++) {
313 		req->ncr_need |= 1 << nvme_ns_create_fields_nvm_req[i];
314 	}
315 
316 	for (size_t i = 0; i < nvme_ns_create_fields_nvm_nallow; i++) {
317 		req->ncr_allow |= 1 << nvme_ns_create_fields_nvm_allow[i];
318 	}
319 
320 	req->ncr_ctrl = ctrl;
321 	req->ncr_csi = csi;
322 
323 	*reqp = req;
324 	return (nvme_ctrl_success(ctrl));
325 }
326 
327 static void
nvme_ns_create_req_clear_need(nvme_ns_create_req_t * req,nvme_ns_create_req_field_t field)328 nvme_ns_create_req_clear_need(nvme_ns_create_req_t *req,
329     nvme_ns_create_req_field_t field)
330 {
331 	req->ncr_need &= ~(1 << field);
332 }
333 
334 static const nvme_field_check_t nvme_ns_create_check_flbas = {
335 	nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_FLBAS,
336 	NVME_ERR_NS_CREATE_FLBAS_RANGE, 0, 0
337 };
338 
339 bool
nvme_ns_create_req_set_flbas(nvme_ns_create_req_t * req,uint32_t flbas)340 nvme_ns_create_req_set_flbas(nvme_ns_create_req_t *req, uint32_t flbas)
341 {
342 	if (!nvme_field_check_one(req->ncr_ctrl, flbas, "namespace create",
343 	    &nvme_ns_create_check_flbas, req->ncr_allow)) {
344 		return (false);
345 	}
346 
347 	req->ncr_flbas = flbas;
348 	nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_FLBAS);
349 	return (nvme_ctrl_success(req->ncr_ctrl));
350 }
351 
352 static const nvme_field_check_t nvme_ns_create_check_nsze = {
353 	nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_NSZE,
354 	NVME_ERR_NS_CREATE_NSZE_RANGE, 0, 0
355 };
356 
357 bool
nvme_ns_create_req_set_nsze(nvme_ns_create_req_t * req,uint64_t nsze)358 nvme_ns_create_req_set_nsze(nvme_ns_create_req_t *req, uint64_t nsze)
359 {
360 	if (!nvme_field_check_one(req->ncr_ctrl, nsze, "namespace create",
361 	    &nvme_ns_create_check_nsze, req->ncr_allow)) {
362 		return (false);
363 	}
364 
365 	req->ncr_nsze = nsze;
366 	nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_NSZE);
367 	return (nvme_ctrl_success(req->ncr_ctrl));
368 }
369 
370 static const nvme_field_check_t nvme_ns_create_check_ncap = {
371 	nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_NCAP,
372 	NVME_ERR_NS_CREATE_NCAP_RANGE, 0, 0
373 };
374 
375 bool
nvme_ns_create_req_set_ncap(nvme_ns_create_req_t * req,uint64_t ncap)376 nvme_ns_create_req_set_ncap(nvme_ns_create_req_t *req, uint64_t ncap)
377 {
378 	if (!nvme_field_check_one(req->ncr_ctrl, ncap, "namespace create",
379 	    &nvme_ns_create_check_ncap, req->ncr_allow)) {
380 		return (false);
381 	}
382 
383 	req->ncr_ncap = ncap;
384 	nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_NCAP);
385 	return (nvme_ctrl_success(req->ncr_ctrl));
386 }
387 
388 static const nvme_field_check_t nvme_ns_create_check_nmic = {
389 	nvme_ns_create_fields, NVME_NS_CREATE_REQ_FIELD_NMIC,
390 	NVME_ERR_NS_CREATE_NMIC_RANGE, 0, 0
391 };
392 
393 bool
nvme_ns_create_req_set_nmic(nvme_ns_create_req_t * req,uint32_t nmic)394 nvme_ns_create_req_set_nmic(nvme_ns_create_req_t *req, uint32_t nmic)
395 {
396 	if (!nvme_field_check_one(req->ncr_ctrl, nmic, "namespace create",
397 	    &nvme_ns_create_check_nmic, req->ncr_allow)) {
398 		return (false);
399 	}
400 
401 	req->ncr_nmic = nmic;
402 	nvme_ns_create_req_clear_need(req, NVME_NS_CREATE_REQ_FIELD_NMIC);
403 	return (nvme_ctrl_success(req->ncr_ctrl));
404 }
405 
406 bool
nvme_ns_create_req_get_nsid(nvme_ns_create_req_t * req,uint32_t * nsid)407 nvme_ns_create_req_get_nsid(nvme_ns_create_req_t *req, uint32_t *nsid)
408 {
409 	if (nsid == NULL) {
410 		return (nvme_ctrl_error(req->ncr_ctrl, NVME_ERR_BAD_PTR, 0,
411 		    "encountered invalid nsid output pointer: %p", nsid));
412 	}
413 
414 	if (!req->ncr_results_valid) {
415 		return (nvme_ctrl_error(req->ncr_ctrl,
416 		    NVME_ERR_NS_CREATE_NO_RESULTS, 0, "namespace create "
417 		    "results are not currently valid and cannot be returned"));
418 	}
419 
420 	*nsid = req->ncr_nsid;
421 	return (nvme_ctrl_success(req->ncr_ctrl));
422 }
423 
424 bool
nvme_ns_create_req_exec(nvme_ns_create_req_t * req)425 nvme_ns_create_req_exec(nvme_ns_create_req_t *req)
426 {
427 	nvme_ctrl_t *ctrl = req->ncr_ctrl;
428 	nvme_ioctl_ns_create_t create;
429 
430 	/*
431 	 * Immediately invalidate our results if someone calls this again.
432 	 */
433 	req->ncr_results_valid = false;
434 	req->ncr_nsid = 0;
435 
436 	if (req->ncr_need != 0) {
437 		return (nvme_field_miss_err(ctrl, nvme_ns_create_fields,
438 		    nvme_ns_create_nfields,
439 		    NVME_ERR_NS_CREATE_REQ_MISSING_FIELDS, "namespace create",
440 		    req->ncr_need));
441 	}
442 
443 	(void) memset(&create, 0, sizeof (create));
444 	create.nnc_nsze = req->ncr_nsze;
445 	create.nnc_ncap = req->ncr_ncap;
446 	create.nnc_csi = req->ncr_csi;
447 	create.nnc_flbas = req->ncr_flbas;
448 	create.nnc_nmic = req->ncr_nmic;
449 
450 	if (ioctl(ctrl->nc_fd, NVME_IOC_NS_CREATE, &create) != 0) {
451 		int e = errno;
452 		return (nvme_ioctl_syserror(ctrl, e, "namespace create"));
453 	}
454 
455 	if (create.nnc_common.nioc_drv_err != NVME_IOCTL_E_OK) {
456 		return (nvme_ioctl_error(ctrl, &create.nnc_common,
457 		    "namespace create"));
458 	}
459 
460 	req->ncr_results_valid = true;
461 	req->ncr_nsid = create.nnc_nsid;
462 	return (nvme_ctrl_success(ctrl));
463 }
464