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