ac9086876c24f11f105bcd7188d343c1684f1619
[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_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
61 int 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
80 static 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
96 int fio_nvme_get_info(struct fio_file *f, __u32 *nsid, __u32 *lba_sz,
97                       __u64 *nlba)
98 {
99         struct nvme_id_ns ns;
100         int namespace_id;
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) {
115                 log_err("failed to fetch namespace-id");
116                 close(fd);
117                 return -errno;
118         }
119
120         /*
121          * Identify namespace to get namespace-id, namespace size in LBA's
122          * and LBA data size.
123          */
124         err = nvme_identify(fd, namespace_id, NVME_IDENTIFY_CNS_NS,
125                                 NVME_CSI_NVM, &ns);
126         if (err) {
127                 log_err("failed to fetch identify namespace\n");
128                 close(fd);
129                 return err;
130         }
131
132         *nsid = namespace_id;
133         *lba_sz = 1 << ns.lbaf[(ns.flbas & 0x0f)].ds;
134         *nlba = ns.nsze;
135
136         close(fd);
137         return 0;
138 }
139
140 int fio_nvme_get_zoned_model(struct thread_data *td, struct fio_file *f,
141                              enum zbd_zoned_model *model)
142 {
143         struct nvme_data *data = FILE_ENG_DATA(f);
144         struct nvme_id_ns ns;
145         struct nvme_passthru_cmd cmd;
146         int fd, ret = 0;
147
148         if (f->filetype != FIO_TYPE_CHAR)
149                 return -EINVAL;
150
151         /* File is not yet opened */
152         fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
153         if (fd < 0)
154                 return -errno;
155
156         /* Using nvme_id_ns for data as sizes are same */
157         ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_CTRL,
158                                 NVME_CSI_ZNS, &ns);
159         if (ret) {
160                 *model = ZBD_NONE;
161                 goto out;
162         }
163
164         memset(&cmd, 0, sizeof(struct nvme_passthru_cmd));
165
166         /* Using nvme_id_ns for data as sizes are same */
167         ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_NS,
168                                 NVME_CSI_ZNS, &ns);
169         if (ret) {
170                 *model = ZBD_NONE;
171                 goto out;
172         }
173
174         *model = ZBD_HOST_MANAGED;
175 out:
176         close(fd);
177         return 0;
178 }
179
180 static int nvme_report_zones(int fd, __u32 nsid, __u64 slba, __u32 zras_feat,
181                              __u32 data_len, void *data)
182 {
183         struct nvme_passthru_cmd cmd = {
184                 .opcode         = nvme_zns_cmd_mgmt_recv,
185                 .nsid           = nsid,
186                 .addr           = (__u64)(uintptr_t)data,
187                 .data_len       = data_len,
188                 .cdw10          = slba & 0xffffffff,
189                 .cdw11          = slba >> 32,
190                 .cdw12          = (data_len >> 2) - 1,
191                 .cdw13          = NVME_ZNS_ZRA_REPORT_ZONES | zras_feat,
192                 .timeout_ms     = NVME_DEFAULT_IOCTL_TIMEOUT,
193         };
194
195         return ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
196 }
197
198 int fio_nvme_report_zones(struct thread_data *td, struct fio_file *f,
199                           uint64_t offset, struct zbd_zone *zbdz,
200                           unsigned int nr_zones)
201 {
202         struct nvme_data *data = FILE_ENG_DATA(f);
203         struct nvme_zone_report *zr;
204         struct nvme_zns_id_ns zns_ns;
205         struct nvme_id_ns ns;
206         unsigned int i = 0, j, zones_fetched = 0;
207         unsigned int max_zones, zones_chunks = 1024;
208         int fd, ret = 0;
209         __u32 zr_len;
210         __u64 zlen;
211
212         /* File is not yet opened */
213         fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
214         if (fd < 0)
215                 return -errno;
216
217         zones_fetched = 0;
218         zr_len = sizeof(*zr) + (zones_chunks * sizeof(struct nvme_zns_desc));
219         zr = calloc(1, zr_len);
220         if (!zr) {
221                 close(fd);
222                 return -ENOMEM;
223         }
224
225         ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_NS,
226                                 NVME_CSI_NVM, &ns);
227         if (ret) {
228                 log_err("%s: nvme_identify_ns failed, err=%d\n", f->file_name,
229                         ret);
230                 goto out;
231         }
232
233         ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_NS,
234                                 NVME_CSI_ZNS, &zns_ns);
235         if (ret) {
236                 log_err("%s: nvme_zns_identify_ns failed, err=%d\n",
237                         f->file_name, ret);
238                 goto out;
239         }
240         zlen = zns_ns.lbafe[ns.flbas & 0x0f].zsze << data->lba_shift;
241
242         max_zones = (f->real_file_size - offset) / zlen;
243         if (max_zones < nr_zones)
244                 nr_zones = max_zones;
245
246         if (nr_zones < zones_chunks)
247                 zones_chunks = nr_zones;
248
249         while (zones_fetched < nr_zones) {
250                 if (zones_fetched + zones_chunks >= nr_zones) {
251                         zones_chunks = nr_zones - zones_fetched;
252                         zr_len = sizeof(*zr) + (zones_chunks * sizeof(struct nvme_zns_desc));
253                 }
254                 ret = nvme_report_zones(fd, data->nsid, offset >> data->lba_shift,
255                                         NVME_ZNS_ZRAS_FEAT_ERZ, zr_len, (void *)zr);
256                 if (ret) {
257                         log_err("%s: nvme_zns_report_zones failed, err=%d\n",
258                                 f->file_name, ret);
259                         goto out;
260                 }
261
262                 /* Transform the zone-report */
263                 for (j = 0; j < zr->nr_zones; j++, i++) {
264                         struct nvme_zns_desc *desc = (struct nvme_zns_desc *)&(zr->entries[j]);
265
266                         zbdz[i].start = desc->zslba << data->lba_shift;
267                         zbdz[i].len = zlen;
268                         zbdz[i].wp = desc->wp << data->lba_shift;
269                         zbdz[i].capacity = desc->zcap << data->lba_shift;
270
271                         /* Zone Type is stored in first 4 bits. */
272                         switch (desc->zt & 0x0f) {
273                         case NVME_ZONE_TYPE_SEQWRITE_REQ:
274                                 zbdz[i].type = ZBD_ZONE_TYPE_SWR;
275                                 break;
276                         default:
277                                 log_err("%s: invalid type for zone at offset %llu.\n",
278                                         f->file_name, (unsigned long long) desc->zslba);
279                                 ret = -EIO;
280                                 goto out;
281                         }
282
283                         /* Zone State is stored in last 4 bits. */
284                         switch (desc->zs >> 4) {
285                         case NVME_ZNS_ZS_EMPTY:
286                                 zbdz[i].cond = ZBD_ZONE_COND_EMPTY;
287                                 break;
288                         case NVME_ZNS_ZS_IMPL_OPEN:
289                                 zbdz[i].cond = ZBD_ZONE_COND_IMP_OPEN;
290                                 break;
291                         case NVME_ZNS_ZS_EXPL_OPEN:
292                                 zbdz[i].cond = ZBD_ZONE_COND_EXP_OPEN;
293                                 break;
294                         case NVME_ZNS_ZS_CLOSED:
295                                 zbdz[i].cond = ZBD_ZONE_COND_CLOSED;
296                                 break;
297                         case NVME_ZNS_ZS_FULL:
298                                 zbdz[i].cond = ZBD_ZONE_COND_FULL;
299                                 break;
300                         case NVME_ZNS_ZS_READ_ONLY:
301                         case NVME_ZNS_ZS_OFFLINE:
302                         default:
303                                 /* Treat all these conditions as offline (don't use!) */
304                                 zbdz[i].cond = ZBD_ZONE_COND_OFFLINE;
305                                 zbdz[i].wp = zbdz[i].start;
306                         }
307                 }
308                 zones_fetched += zr->nr_zones;
309                 offset += zr->nr_zones * zlen;
310         }
311
312         ret = zones_fetched;
313 out:
314         free(zr);
315         close(fd);
316
317         return ret;
318 }
319
320 int fio_nvme_reset_wp(struct thread_data *td, struct fio_file *f,
321                       uint64_t offset, uint64_t length)
322 {
323         struct nvme_data *data = FILE_ENG_DATA(f);
324         unsigned int nr_zones;
325         unsigned long long zslba;
326         int i, fd, ret = 0;
327
328         /* If the file is not yet opened, open it for this function. */
329         fd = f->fd;
330         if (fd < 0) {
331                 fd = open(f->file_name, O_RDWR | O_LARGEFILE);
332                 if (fd < 0)
333                         return -errno;
334         }
335
336         zslba = offset >> data->lba_shift;
337         nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
338
339         for (i = 0; i < nr_zones; i++, zslba += (td->o.zone_size >> data->lba_shift)) {
340                 struct nvme_passthru_cmd cmd = {
341                         .opcode         = nvme_zns_cmd_mgmt_send,
342                         .nsid           = data->nsid,
343                         .cdw10          = zslba & 0xffffffff,
344                         .cdw11          = zslba >> 32,
345                         .cdw13          = NVME_ZNS_ZSA_RESET,
346                         .addr           = (__u64)(uintptr_t)NULL,
347                         .data_len       = 0,
348                         .timeout_ms     = NVME_DEFAULT_IOCTL_TIMEOUT,
349                 };
350
351                 ret = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
352         }
353
354         if (f->fd < 0)
355                 close(fd);
356         return -ret;
357 }
358
359 int fio_nvme_get_max_open_zones(struct thread_data *td, struct fio_file *f,
360                                 unsigned int *max_open_zones)
361 {
362         struct nvme_data *data = FILE_ENG_DATA(f);
363         struct nvme_zns_id_ns zns_ns;
364         int fd, ret = 0;
365
366         fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
367         if (fd < 0)
368                 return -errno;
369
370         ret = nvme_identify(fd, data->nsid, NVME_IDENTIFY_CNS_CSI_NS,
371                                 NVME_CSI_ZNS, &zns_ns);
372         if (ret) {
373                 log_err("%s: nvme_zns_identify_ns failed, err=%d\n",
374                         f->file_name, ret);
375                 goto out;
376         }
377
378         *max_open_zones = zns_ns.mor + 1;
379 out:
380         close(fd);
381         return ret;
382 }
383
384 static inline int nvme_fdp_reclaim_unit_handle_status(int fd, __u32 nsid,
385                                                       __u32 data_len, void *data)
386 {
387         struct nvme_passthru_cmd cmd = {
388                 .opcode         = nvme_cmd_io_mgmt_recv,
389                 .nsid           = nsid,
390                 .addr           = (__u64)(uintptr_t)data,
391                 .data_len       = data_len,
392                 .cdw10          = 1,
393                 .cdw11          = (data_len >> 2) - 1,
394         };
395
396         return ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
397 }
398
399 int fio_nvme_iomgmt_ruhs(struct thread_data *td, struct fio_file *f,
400                          struct nvme_fdp_ruh_status *ruhs, __u32 bytes)
401 {
402         struct nvme_data *data = FILE_ENG_DATA(f);
403         int fd, ret;
404
405         fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
406         if (fd < 0)
407                 return -errno;
408
409         ret = nvme_fdp_reclaim_unit_handle_status(fd, data->nsid, bytes, ruhs);
410         if (ret) {
411                 log_err("%s: nvme_fdp_reclaim_unit_handle_status failed, err=%d\n",
412                         f->file_name, ret);
413                 errno = ENOTSUP;
414         } else
415                 errno = 0;
416
417         close(fd);
418         return -errno;
419 }