ci: Also test the Android recovery environment
[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                 err = -errno;
116                 log_err("failed to fetch namespace-id");
117                 close(fd);
118                 return err;
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 }
140
141 int 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;
176 out:
177         close(fd);
178         return 0;
179 }
180
181 static 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
199 int 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);
221         if (!zr) {
222                 close(fd);
223                 return -ENOMEM;
224         }
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",
279                                         f->file_name, (unsigned long long) desc->zslba);
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;
314 out:
315         free(zr);
316         close(fd);
317
318         return ret;
319 }
320
321 int 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
360 int 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;
380 out:
381         close(fd);
382         return ret;
383 }
384
385 static 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
400 int 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
418         ret = -errno;
419         close(fd);
420         return ret;
421 }