engines/nvme: cache errno value
[fio.git] / engines / nvme.c
CommitLineData
b3d5e3fd
AK
1/*
2 * nvme structure declarations and helper functions for the
3 * io_uring_cmd engine.
4 */
5
6#include "nvme.h"
7
8int fio_nvme_uring_cmd_prep(struct nvme_uring_cmd *cmd, struct io_u *io_u,
9 struct iovec *iov)
10{
11 struct nvme_data *data = FILE_ENG_DATA(io_u->file);
12 __u64 slba;
13 __u32 nlb;
14
15 memset(cmd, 0, sizeof(struct nvme_uring_cmd));
16
17 if (io_u->ddir == DDIR_READ)
18 cmd->opcode = nvme_cmd_read;
19 else if (io_u->ddir == DDIR_WRITE)
20 cmd->opcode = nvme_cmd_write;
21 else
22 return -ENOTSUP;
23
24 slba = io_u->offset >> data->lba_shift;
25 nlb = (io_u->xfer_buflen >> data->lba_shift) - 1;
26
27 /* cdw10 and cdw11 represent starting lba */
28 cmd->cdw10 = slba & 0xffffffff;
29 cmd->cdw11 = slba >> 32;
30 /* cdw12 represent number of lba's for read/write */
a7e8aae0
KB
31 cmd->cdw12 = nlb | (io_u->dtype << 20);
32 cmd->cdw13 = io_u->dspec << 16;
b3d5e3fd
AK
33 if (iov) {
34 iov->iov_base = io_u->xfer_buf;
35 iov->iov_len = io_u->xfer_buflen;
36 cmd->addr = (__u64)(uintptr_t)iov;
37 cmd->data_len = 1;
38 } else {
39 cmd->addr = (__u64)(uintptr_t)io_u->xfer_buf;
40 cmd->data_len = io_u->xfer_buflen;
41 }
42 cmd->nsid = data->nsid;
43 return 0;
44}
45
16be6037
AK
46static int nvme_trim(int fd, __u32 nsid, __u32 nr_range, __u32 data_len,
47 void *data)
48{
49 struct nvme_passthru_cmd cmd = {
50 .opcode = nvme_cmd_dsm,
51 .nsid = nsid,
52 .addr = (__u64)(uintptr_t)data,
53 .data_len = data_len,
54 .cdw10 = nr_range - 1,
55 .cdw11 = NVME_ATTRIBUTE_DEALLOCATE,
56 };
57
58 return ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
59}
60
61int fio_nvme_trim(const struct thread_data *td, struct fio_file *f,
62 unsigned long long offset, unsigned long long len)
63{
64 struct nvme_data *data = FILE_ENG_DATA(f);
65 struct nvme_dsm_range dsm;
66 int ret;
67
68 dsm.nlb = (len >> data->lba_shift);
69 dsm.slba = (offset >> data->lba_shift);
70
71 ret = nvme_trim(f->fd, data->nsid, 1, sizeof(struct nvme_dsm_range),
72 &dsm);
73 if (ret)
74 log_err("%s: nvme_trim failed for offset %llu and len %llu, err=%d\n",
75 f->file_name, offset, len, ret);
76
77 return ret;
78}
79
b3d5e3fd
AK
80static int nvme_identify(int fd, __u32 nsid, enum nvme_identify_cns cns,
81 enum nvme_csi csi, void *data)
82{
83 struct nvme_passthru_cmd cmd = {
84 .opcode = nvme_admin_identify,
85 .nsid = nsid,
86 .addr = (__u64)(uintptr_t)data,
87 .data_len = NVME_IDENTIFY_DATA_SIZE,
88 .cdw10 = cns,
89 .cdw11 = csi << NVME_IDENTIFY_CSI_SHIFT,
90 .timeout_ms = NVME_DEFAULT_IOCTL_TIMEOUT,
91 };
92
93 return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
94}
95
96int fio_nvme_get_info(struct fio_file *f, __u32 *nsid, __u32 *lba_sz,
97 __u64 *nlba)
98{
99 struct nvme_id_ns ns;
37a0881f 100 int namespace_id;
b3d5e3fd
AK
101 int fd, err;
102
103 if (f->filetype != FIO_TYPE_CHAR) {
104 log_err("ioengine io_uring_cmd only works with nvme ns "
105 "generic char devices (/dev/ngXnY)\n");
106 return 1;
107 }
108
109 fd = open(f->file_name, O_RDONLY);
110 if (fd < 0)
111 return -errno;
112
113 namespace_id = ioctl(fd, NVME_IOCTL_ID);
114 if (namespace_id < 0) {
af10f514 115 err = -errno;
b3d5e3fd
AK
116 log_err("failed to fetch namespace-id");
117 close(fd);
af10f514 118 return err;
b3d5e3fd
AK
119 }
120
121 /*
122 * Identify namespace to get namespace-id, namespace size in LBA's
123 * and LBA data size.
124 */
125 err = nvme_identify(fd, namespace_id, NVME_IDENTIFY_CNS_NS,
126 NVME_CSI_NVM, &ns);
127 if (err) {
128 log_err("failed to fetch identify namespace\n");
129 close(fd);
130 return err;
131 }
132
133 *nsid = namespace_id;
134 *lba_sz = 1 << ns.lbaf[(ns.flbas & 0x0f)].ds;
135 *nlba = ns.nsze;
136
137 close(fd);
138 return 0;
139}
3d05e0ff
AK
140
141int fio_nvme_get_zoned_model(struct thread_data *td, struct fio_file *f,
142 enum zbd_zoned_model *model)
143{
144 struct nvme_data *data = FILE_ENG_DATA(f);
145 struct nvme_id_ns ns;
146 struct nvme_passthru_cmd cmd;
147 int fd, ret = 0;
148
149 if (f->filetype != FIO_TYPE_CHAR)
150 return -EINVAL;
151
152 /* File is not yet opened */
153 fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
154 if (fd < 0)
155 return -errno;
156
157 /* Using nvme_id_ns for data as sizes are same */
158 ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_CTRL,
159 NVME_CSI_ZNS, &ns);
160 if (ret) {
161 *model = ZBD_NONE;
162 goto out;
163 }
164
165 memset(&cmd, 0, sizeof(struct nvme_passthru_cmd));
166
167 /* Using nvme_id_ns for data as sizes are same */
168 ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_NS,
169 NVME_CSI_ZNS, &ns);
170 if (ret) {
171 *model = ZBD_NONE;
172 goto out;
173 }
174
175 *model = ZBD_HOST_MANAGED;
176out:
177 close(fd);
178 return 0;
179}
180
181static int nvme_report_zones(int fd, __u32 nsid, __u64 slba, __u32 zras_feat,
182 __u32 data_len, void *data)
183{
184 struct nvme_passthru_cmd cmd = {
185 .opcode = nvme_zns_cmd_mgmt_recv,
186 .nsid = nsid,
187 .addr = (__u64)(uintptr_t)data,
188 .data_len = data_len,
189 .cdw10 = slba & 0xffffffff,
190 .cdw11 = slba >> 32,
191 .cdw12 = (data_len >> 2) - 1,
192 .cdw13 = NVME_ZNS_ZRA_REPORT_ZONES | zras_feat,
193 .timeout_ms = NVME_DEFAULT_IOCTL_TIMEOUT,
194 };
195
196 return ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
197}
198
199int fio_nvme_report_zones(struct thread_data *td, struct fio_file *f,
200 uint64_t offset, struct zbd_zone *zbdz,
201 unsigned int nr_zones)
202{
203 struct nvme_data *data = FILE_ENG_DATA(f);
204 struct nvme_zone_report *zr;
205 struct nvme_zns_id_ns zns_ns;
206 struct nvme_id_ns ns;
207 unsigned int i = 0, j, zones_fetched = 0;
208 unsigned int max_zones, zones_chunks = 1024;
209 int fd, ret = 0;
210 __u32 zr_len;
211 __u64 zlen;
212
213 /* File is not yet opened */
214 fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
215 if (fd < 0)
216 return -errno;
217
218 zones_fetched = 0;
219 zr_len = sizeof(*zr) + (zones_chunks * sizeof(struct nvme_zns_desc));
220 zr = calloc(1, zr_len);
3efcb23f
JA
221 if (!zr) {
222 close(fd);
3d05e0ff 223 return -ENOMEM;
3efcb23f 224 }
3d05e0ff
AK
225
226 ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_NS,
227 NVME_CSI_NVM, &ns);
228 if (ret) {
229 log_err("%s: nvme_identify_ns failed, err=%d\n", f->file_name,
230 ret);
231 goto out;
232 }
233
234 ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_NS,
235 NVME_CSI_ZNS, &zns_ns);
236 if (ret) {
237 log_err("%s: nvme_zns_identify_ns failed, err=%d\n",
238 f->file_name, ret);
239 goto out;
240 }
241 zlen = zns_ns.lbafe[ns.flbas & 0x0f].zsze << data->lba_shift;
242
243 max_zones = (f->real_file_size - offset) / zlen;
244 if (max_zones < nr_zones)
245 nr_zones = max_zones;
246
247 if (nr_zones < zones_chunks)
248 zones_chunks = nr_zones;
249
250 while (zones_fetched < nr_zones) {
251 if (zones_fetched + zones_chunks >= nr_zones) {
252 zones_chunks = nr_zones - zones_fetched;
253 zr_len = sizeof(*zr) + (zones_chunks * sizeof(struct nvme_zns_desc));
254 }
255 ret = nvme_report_zones(fd, data->nsid, offset >> data->lba_shift,
256 NVME_ZNS_ZRAS_FEAT_ERZ, zr_len, (void *)zr);
257 if (ret) {
258 log_err("%s: nvme_zns_report_zones failed, err=%d\n",
259 f->file_name, ret);
260 goto out;
261 }
262
263 /* Transform the zone-report */
264 for (j = 0; j < zr->nr_zones; j++, i++) {
265 struct nvme_zns_desc *desc = (struct nvme_zns_desc *)&(zr->entries[j]);
266
267 zbdz[i].start = desc->zslba << data->lba_shift;
268 zbdz[i].len = zlen;
269 zbdz[i].wp = desc->wp << data->lba_shift;
270 zbdz[i].capacity = desc->zcap << data->lba_shift;
271
272 /* Zone Type is stored in first 4 bits. */
273 switch (desc->zt & 0x0f) {
274 case NVME_ZONE_TYPE_SEQWRITE_REQ:
275 zbdz[i].type = ZBD_ZONE_TYPE_SWR;
276 break;
277 default:
278 log_err("%s: invalid type for zone at offset %llu.\n",
2fa0ab21 279 f->file_name, (unsigned long long) desc->zslba);
3d05e0ff
AK
280 ret = -EIO;
281 goto out;
282 }
283
284 /* Zone State is stored in last 4 bits. */
285 switch (desc->zs >> 4) {
286 case NVME_ZNS_ZS_EMPTY:
287 zbdz[i].cond = ZBD_ZONE_COND_EMPTY;
288 break;
289 case NVME_ZNS_ZS_IMPL_OPEN:
290 zbdz[i].cond = ZBD_ZONE_COND_IMP_OPEN;
291 break;
292 case NVME_ZNS_ZS_EXPL_OPEN:
293 zbdz[i].cond = ZBD_ZONE_COND_EXP_OPEN;
294 break;
295 case NVME_ZNS_ZS_CLOSED:
296 zbdz[i].cond = ZBD_ZONE_COND_CLOSED;
297 break;
298 case NVME_ZNS_ZS_FULL:
299 zbdz[i].cond = ZBD_ZONE_COND_FULL;
300 break;
301 case NVME_ZNS_ZS_READ_ONLY:
302 case NVME_ZNS_ZS_OFFLINE:
303 default:
304 /* Treat all these conditions as offline (don't use!) */
305 zbdz[i].cond = ZBD_ZONE_COND_OFFLINE;
306 zbdz[i].wp = zbdz[i].start;
307 }
308 }
309 zones_fetched += zr->nr_zones;
310 offset += zr->nr_zones * zlen;
311 }
312
313 ret = zones_fetched;
314out:
315 free(zr);
316 close(fd);
317
318 return ret;
319}
320
321int fio_nvme_reset_wp(struct thread_data *td, struct fio_file *f,
322 uint64_t offset, uint64_t length)
323{
324 struct nvme_data *data = FILE_ENG_DATA(f);
325 unsigned int nr_zones;
326 unsigned long long zslba;
327 int i, fd, ret = 0;
328
329 /* If the file is not yet opened, open it for this function. */
330 fd = f->fd;
331 if (fd < 0) {
332 fd = open(f->file_name, O_RDWR | O_LARGEFILE);
333 if (fd < 0)
334 return -errno;
335 }
336
337 zslba = offset >> data->lba_shift;
338 nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
339
340 for (i = 0; i < nr_zones; i++, zslba += (td->o.zone_size >> data->lba_shift)) {
341 struct nvme_passthru_cmd cmd = {
342 .opcode = nvme_zns_cmd_mgmt_send,
343 .nsid = data->nsid,
344 .cdw10 = zslba & 0xffffffff,
345 .cdw11 = zslba >> 32,
346 .cdw13 = NVME_ZNS_ZSA_RESET,
347 .addr = (__u64)(uintptr_t)NULL,
348 .data_len = 0,
349 .timeout_ms = NVME_DEFAULT_IOCTL_TIMEOUT,
350 };
351
352 ret = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
353 }
354
355 if (f->fd < 0)
356 close(fd);
357 return -ret;
358}
359
360int fio_nvme_get_max_open_zones(struct thread_data *td, struct fio_file *f,
361 unsigned int *max_open_zones)
362{
363 struct nvme_data *data = FILE_ENG_DATA(f);
364 struct nvme_zns_id_ns zns_ns;
365 int fd, ret = 0;
366
367 fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
368 if (fd < 0)
369 return -errno;
370
371 ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_NS,
372 NVME_CSI_ZNS, &zns_ns);
373 if (ret) {
374 log_err("%s: nvme_zns_identify_ns failed, err=%d\n",
375 f->file_name, ret);
376 goto out;
377 }
378
379 *max_open_zones = zns_ns.mor + 1;
380out:
381 close(fd);
382 return ret;
383}
a7e8aae0
KB
384
385static inline int nvme_fdp_reclaim_unit_handle_status(int fd, __u32 nsid,
386 __u32 data_len, void *data)
387{
388 struct nvme_passthru_cmd cmd = {
389 .opcode = nvme_cmd_io_mgmt_recv,
390 .nsid = nsid,
391 .addr = (__u64)(uintptr_t)data,
392 .data_len = data_len,
393 .cdw10 = 1,
394 .cdw11 = (data_len >> 2) - 1,
395 };
396
397 return ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
398}
399
400int fio_nvme_iomgmt_ruhs(struct thread_data *td, struct fio_file *f,
401 struct nvme_fdp_ruh_status *ruhs, __u32 bytes)
402{
403 struct nvme_data *data = FILE_ENG_DATA(f);
404 int fd, ret;
405
406 fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
407 if (fd < 0)
408 return -errno;
409
410 ret = nvme_fdp_reclaim_unit_handle_status(fd, data->nsid, bytes, ruhs);
411 if (ret) {
412 log_err("%s: nvme_fdp_reclaim_unit_handle_status failed, err=%d\n",
413 f->file_name, ret);
414 errno = ENOTSUP;
415 } else
416 errno = 0;
417
af10f514 418 ret = -errno;
a7e8aae0 419 close(fd);
af10f514 420 return ret;
a7e8aae0 421}