xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_format.c (revision e00bdde3c6d406f40f53f3025defadc22f7ec31a)
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  * This file implements support for Format NVM.
18  */
19 
20 #include <string.h>
21 #include <unistd.h>
22 
23 #include "libnvme_impl.h"
24 
25 void
26 nvme_format_req_fini(nvme_format_req_t *req)
27 {
28 	free(req);
29 }
30 
31 bool
32 nvme_format_req_init(nvme_ctrl_t *ctrl, nvme_format_req_t **reqp)
33 {
34 	nvme_format_req_t *req;
35 	nvme_valid_ctrl_data_t ctrl_data;
36 
37 	if (reqp == NULL) {
38 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
39 		    "encountered invalid nvme_format_req_t output pointer: %p",
40 		    reqp));
41 	}
42 
43 	ctrl_data.vcd_vers = &ctrl->nc_vers;
44 	ctrl_data.vcd_id = &ctrl->nc_info;
45 
46 	if (!nvme_format_cmds_supported(&ctrl_data)) {
47 		return (nvme_ctrl_error(ctrl, NVME_ERR_FORMAT_UNSUP_BY_DEV, 0,
48 		    "controller does not support format NVM"));
49 	}
50 
51 	req = calloc(1, sizeof (nvme_format_req_t));
52 	if (req == NULL) {
53 		int e = errno;
54 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
55 		    "allocate memory for a new nvme_log_req_t: %s",
56 		    strerror(e)));
57 	}
58 
59 	req->nfr_ctrl = ctrl;
60 	req->nfr_nsid = NVME_NSID_BCAST;
61 
62 	for (size_t i = 0; i < nvme_format_nfields; i++) {
63 		if (nvme_format_fields[i].nlfi_def_req) {
64 			req->nfr_need |= 1 << i;
65 		}
66 	}
67 
68 	*reqp = req;
69 	return (nvme_ctrl_success(ctrl));
70 }
71 
72 static void
73 nvme_format_req_clear_need(nvme_format_req_t *req,
74     nvme_format_req_field_t field)
75 {
76 	req->nfr_need &= ~(1 << field);
77 }
78 
79 static const nvme_field_check_t nvme_format_check_ses = {
80 	nvme_format_fields, NVME_FORMAT_REQ_FIELD_SES,
81 	NVME_ERR_FORMAT_SES_RANGE, 0, 0
82 };
83 
84 bool
85 nvme_format_req_set_ses(nvme_format_req_t *req, uint32_t ses)
86 {
87 	if (!nvme_field_check_one(req->nfr_ctrl, ses, "format NVM",
88 	    &nvme_format_check_ses, 0)) {
89 		return (false);
90 	}
91 
92 	req->nfr_ses = ses;
93 	nvme_format_req_clear_need(req, NVME_FORMAT_REQ_FIELD_SES);
94 	return (nvme_ctrl_success(req->nfr_ctrl));
95 }
96 
97 static const nvme_field_check_t nvme_format_check_lbaf = {
98 	nvme_format_fields, NVME_FORMAT_REQ_FIELD_LBAF,
99 	NVME_ERR_FORMAT_LBAF_RANGE, 0, 0
100 };
101 
102 /*
103  * We don't try to check the range of the actual LBA formats that are here and
104  * instead leave that to the kernel which always has valid common namespace
105  * information. It would be nice to fold that in to the common logic in the
106  * future.
107  */
108 bool
109 nvme_format_req_set_lbaf(nvme_format_req_t *req, uint32_t lbaf)
110 {
111 	if (!nvme_field_check_one(req->nfr_ctrl, lbaf, "format NVM",
112 	    &nvme_format_check_lbaf, 0)) {
113 		return (false);
114 	}
115 
116 	req->nfr_lbaf = lbaf;
117 	nvme_format_req_clear_need(req, NVME_FORMAT_REQ_FIELD_LBAF);
118 	return (nvme_ctrl_success(req->nfr_ctrl));
119 }
120 
121 static const nvme_field_check_t nvme_format_check_nsid = {
122 	nvme_format_fields, NVME_FORMAT_REQ_FIELD_NSID,
123 	NVME_ERR_NS_RANGE, 0, 0
124 };
125 
126 bool
127 nvme_format_req_set_nsid(nvme_format_req_t *req, uint32_t nsid)
128 {
129 	if (!nvme_field_check_one(req->nfr_ctrl, nsid, "format NVM",
130 	    &nvme_format_check_nsid, 0)) {
131 		return (false);
132 	}
133 
134 	req->nfr_nsid = nsid;
135 	nvme_format_req_clear_need(req, NVME_FORMAT_REQ_FIELD_NSID);
136 	return (nvme_ctrl_success(req->nfr_ctrl));
137 }
138 
139 /*
140  * All error checking with respect to whether or not the controller supports
141  * operating on a single namespace is left to the kernel and translated back
142  * here.
143  */
144 bool
145 nvme_format_req_exec(nvme_format_req_t *req)
146 {
147 	nvme_ctrl_t *ctrl = req->nfr_ctrl;
148 	nvme_ioctl_format_t format;
149 
150 	if (req->nfr_need != 0) {
151 		return (nvme_field_miss_err(ctrl, nvme_format_fields,
152 		    nvme_format_nfields, NVME_ERR_FORMAT_REQ_MISSING_FIELDS,
153 		    "format", req->nfr_need));
154 	}
155 
156 	(void) memset(&format, 0, sizeof (format));
157 	format.nif_common.nioc_nsid = req->nfr_nsid;
158 	format.nif_lbaf = req->nfr_lbaf;
159 	format.nif_ses = req->nfr_ses;
160 
161 	if (ioctl(ctrl->nc_fd, NVME_IOC_FORMAT, &format) != 0) {
162 		int e = errno;
163 		return (nvme_ioctl_syserror(ctrl, e, "format"));
164 	}
165 
166 	if (format.nif_common.nioc_drv_err != NVME_IOCTL_E_OK) {
167 		return (nvme_ioctl_error(ctrl, &format.nif_common, "format"));
168 	}
169 
170 	return (nvme_ctrl_success(ctrl));
171 }
172