fio: add fdp support for io_uring_cmd nvme engine
[fio.git] / engines / nvme.c
1 /*
2  * nvme structure declarations and helper functions for the
3  * io_uring_cmd engine.
4  */
5
6 #include "nvme.h"
7
8 int 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 */
31         cmd->cdw12 = nlb | (io_u->dtype << 20);
32         cmd->cdw13 = io_u->dspec << 16;
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
46 static 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
62 int fio_nvme_get_info(struct fio_file *f, __u32 *nsid, __u32 *lba_sz,
63                       __u64 *nlba)
64 {
65         struct nvme_id_ns ns;
66         int namespace_id;
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 }
105
106 int 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;
141 out:
142         close(fd);
143         return 0;
144 }
145
146 static 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
164 int 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);
186         if (!zr) {
187                 close(fd);
188                 return -ENOMEM;
189         }
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;
279 out:
280         free(zr);
281         close(fd);
282
283         return ret;
284 }
285
286 int 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
325 int 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;
345 out:
346         close(fd);
347         return ret;
348 }
349
350 static 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
365 int 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 }