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
nvme_format_req_fini(nvme_format_req_t * req)26 nvme_format_req_fini(nvme_format_req_t *req)
27 {
28 free(req);
29 }
30
31 bool
nvme_format_req_init(nvme_ctrl_t * ctrl,nvme_format_req_t ** reqp)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
nvme_format_req_clear_need(nvme_format_req_t * req,nvme_format_req_field_t field)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
nvme_format_req_set_ses(nvme_format_req_t * req,uint32_t ses)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
nvme_format_req_set_lbaf(nvme_format_req_t * req,uint32_t lbaf)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
nvme_format_req_set_nsid(nvme_format_req_t * req,uint32_t nsid)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
nvme_format_req_exec(nvme_format_req_t * req)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