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