stat: make add lat percentile functions inline
[fio.git] / engines / libzbc.c
1 /*
2  * Copyright (C) 2019 Western Digital Corporation or its affiliates.
3  *
4  * This file is released under the GPL.
5  *
6  * libzbc engine
7  * IO engine using libzbc library to talk to SMR disks.
8  */
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <libzbc/zbc.h>
13
14 #include "fio.h"
15 #include "err.h"
16 #include "zbd_types.h"
17 #include "zbd.h"
18
19 struct libzbc_data {
20         struct zbc_device       *zdev;
21         enum zbc_dev_model      model;
22         uint64_t                nr_sectors;
23         uint32_t                max_open_seq_req;
24 };
25
26 static int libzbc_get_dev_info(struct libzbc_data *ld, struct fio_file *f)
27 {
28         struct zbc_device_info *zinfo;
29
30         zinfo = calloc(1, sizeof(*zinfo));
31         if (!zinfo)
32                 return -ENOMEM;
33
34         zbc_get_device_info(ld->zdev, zinfo);
35         ld->model = zinfo->zbd_model;
36         ld->nr_sectors = zinfo->zbd_sectors;
37         ld->max_open_seq_req = zinfo->zbd_max_nr_open_seq_req;
38
39         dprint(FD_ZBD, "%s: vendor_id:%s, type: %s, model: %s\n",
40                f->file_name, zinfo->zbd_vendor_id,
41                zbc_device_type_str(zinfo->zbd_type),
42                zbc_device_model_str(zinfo->zbd_model));
43
44         free(zinfo);
45
46         return 0;
47 }
48
49 static int libzbc_open_dev(struct thread_data *td, struct fio_file *f,
50                            struct libzbc_data **p_ld)
51 {
52         struct libzbc_data *ld = td->io_ops_data;
53         int ret, flags = OS_O_DIRECT;
54
55         if (ld) {
56                 /* Already open */
57                 assert(ld->zdev);
58                 goto out;
59         }
60
61         if (f->filetype != FIO_TYPE_BLOCK && f->filetype != FIO_TYPE_CHAR) {
62                 td_verror(td, EINVAL, "wrong file type");
63                 log_err("ioengine libzbc only works on block or character devices\n");
64                 return -EINVAL;
65         }
66
67         if (td_write(td) || td_trim(td)) {
68                 if (!read_only)
69                         flags |= O_RDWR;
70         } else if (td_read(td)) {
71                 if (f->filetype == FIO_TYPE_CHAR && !read_only)
72                         flags |= O_RDWR;
73                 else
74                         flags |= O_RDONLY;
75         }
76
77         if (td->o.oatomic) {
78                 td_verror(td, EINVAL, "libzbc does not support O_ATOMIC");
79                 log_err("%s: libzbc does not support O_ATOMIC\n", f->file_name);
80                 return -EINVAL;
81         }
82
83         ld = calloc(1, sizeof(*ld));
84         if (!ld)
85                 return -ENOMEM;
86
87         ret = zbc_open(f->file_name,
88                        flags | ZBC_O_DRV_BLOCK | ZBC_O_DRV_SCSI | ZBC_O_DRV_ATA,
89                        &ld->zdev);
90         if (ret) {
91                 log_err("%s: zbc_open() failed, err=%d\n",
92                         f->file_name, ret);
93                 goto err;
94         }
95
96         ret = libzbc_get_dev_info(ld, f);
97         if (ret)
98                 goto err_close;
99
100         td->io_ops_data = ld;
101 out:
102         if (p_ld)
103                 *p_ld = ld;
104
105         return 0;
106
107 err_close:
108         zbc_close(ld->zdev);
109 err:
110         free(ld);
111         return ret;
112 }
113
114 static int libzbc_close_dev(struct thread_data *td)
115 {
116         struct libzbc_data *ld = td->io_ops_data;
117         int ret = 0;
118
119         td->io_ops_data = NULL;
120         if (ld) {
121                 if (ld->zdev)
122                         ret = zbc_close(ld->zdev);
123                 free(ld);
124         }
125
126         return ret;
127 }
128 static int libzbc_open_file(struct thread_data *td, struct fio_file *f)
129 {
130         return libzbc_open_dev(td, f, NULL);
131 }
132
133 static int libzbc_close_file(struct thread_data *td, struct fio_file *f)
134 {
135         int ret;
136
137         ret = libzbc_close_dev(td);
138         if (ret)
139                 log_err("%s: close device failed err %d\n",
140                         f->file_name, ret);
141
142         return ret;
143 }
144
145 static void libzbc_cleanup(struct thread_data *td)
146 {
147         libzbc_close_dev(td);
148 }
149
150 static int libzbc_invalidate(struct thread_data *td, struct fio_file *f)
151 {
152         /* Passthrough IO do not cache data. Nothing to do */
153         return 0;
154 }
155
156 static int libzbc_get_file_size(struct thread_data *td, struct fio_file *f)
157 {
158         struct libzbc_data *ld;
159         int ret;
160
161         if (fio_file_size_known(f))
162                 return 0;
163
164         ret = libzbc_open_dev(td, f, &ld);
165         if (ret)
166                 return ret;
167
168         f->real_file_size = ld->nr_sectors << 9;
169         fio_file_set_size_known(f);
170
171         return 0;
172 }
173
174 static int libzbc_get_zoned_model(struct thread_data *td, struct fio_file *f,
175                                   enum zbd_zoned_model *model)
176 {
177         struct libzbc_data *ld;
178         int ret;
179
180         if (f->filetype != FIO_TYPE_BLOCK && f->filetype != FIO_TYPE_CHAR)
181                 return -EINVAL;
182
183         ret = libzbc_open_dev(td, f, &ld);
184         if (ret)
185                 return ret;
186
187         switch (ld->model) {
188         case ZBC_DM_HOST_AWARE:
189                 *model = ZBD_HOST_AWARE;
190                 break;
191         case ZBC_DM_HOST_MANAGED:
192                 *model = ZBD_HOST_MANAGED;
193                 break;
194         default:
195                 *model = ZBD_NONE;
196                 break;
197         }
198
199         return 0;
200 }
201
202 static int libzbc_report_zones(struct thread_data *td, struct fio_file *f,
203                                uint64_t offset, struct zbd_zone *zbdz,
204                                unsigned int nr_zones)
205 {
206         struct libzbc_data *ld;
207         uint64_t sector = offset >> 9;
208         struct zbc_zone *zones;
209         unsigned int i;
210         int ret;
211
212         ret = libzbc_open_dev(td, f, &ld);
213         if (ret)
214                 return ret;
215
216         if (sector >= ld->nr_sectors)
217                 return 0;
218
219         zones = calloc(nr_zones, sizeof(struct zbc_zone));
220         if (!zones) {
221                 ret = -ENOMEM;
222                 goto out;
223         }
224
225         ret = zbc_report_zones(ld->zdev, sector, ZBC_RO_ALL, zones, &nr_zones);
226         if (ret < 0) {
227                 log_err("%s: zbc_report_zones failed, err=%d\n",
228                         f->file_name, ret);
229                 goto out;
230         }
231
232         for (i = 0; i < nr_zones; i++, zbdz++) {
233                 zbdz->start = zones[i].zbz_start << 9;
234                 zbdz->len = zones[i].zbz_length << 9;
235                 zbdz->wp = zones[i].zbz_write_pointer << 9;
236                 /*
237                  * ZBC/ZAC do not define zone capacity, so use the zone size as
238                  * the zone capacity.
239                  */
240                 zbdz->capacity = zbdz->len;
241
242                 switch (zones[i].zbz_type) {
243                 case ZBC_ZT_CONVENTIONAL:
244                         zbdz->type = ZBD_ZONE_TYPE_CNV;
245                         break;
246                 case ZBC_ZT_SEQUENTIAL_REQ:
247                         zbdz->type = ZBD_ZONE_TYPE_SWR;
248                         break;
249                 case ZBC_ZT_SEQUENTIAL_PREF:
250                         zbdz->type = ZBD_ZONE_TYPE_SWP;
251                         break;
252                 default:
253                         td_verror(td, errno, "invalid zone type");
254                         log_err("%s: invalid type for zone at sector %llu.\n",
255                                 f->file_name, (unsigned long long)zbdz->start);
256                         ret = -EIO;
257                         goto out;
258                 }
259
260                 switch (zones[i].zbz_condition) {
261                 case ZBC_ZC_NOT_WP:
262                         zbdz->cond = ZBD_ZONE_COND_NOT_WP;
263                         break;
264                 case ZBC_ZC_EMPTY:
265                         zbdz->cond = ZBD_ZONE_COND_EMPTY;
266                         break;
267                 case ZBC_ZC_IMP_OPEN:
268                         zbdz->cond = ZBD_ZONE_COND_IMP_OPEN;
269                         break;
270                 case ZBC_ZC_EXP_OPEN:
271                         zbdz->cond = ZBD_ZONE_COND_EXP_OPEN;
272                         break;
273                 case ZBC_ZC_CLOSED:
274                         zbdz->cond = ZBD_ZONE_COND_CLOSED;
275                         break;
276                 case ZBC_ZC_FULL:
277                         zbdz->cond = ZBD_ZONE_COND_FULL;
278                         break;
279                 case ZBC_ZC_RDONLY:
280                 case ZBC_ZC_OFFLINE:
281                 default:
282                         /* Treat all these conditions as offline (don't use!) */
283                         zbdz->cond = ZBD_ZONE_COND_OFFLINE;
284                         zbdz->wp = zbdz->start;
285                 }
286         }
287
288         ret = nr_zones;
289 out:
290         free(zones);
291         return ret;
292 }
293
294 static int libzbc_reset_wp(struct thread_data *td, struct fio_file *f,
295                            uint64_t offset, uint64_t length)
296 {
297         struct libzbc_data *ld = td->io_ops_data;
298         uint64_t sector = offset >> 9;
299         uint64_t end_sector = (offset + length) >> 9;
300         unsigned int nr_zones;
301         struct zbc_errno err;
302         int i, ret;
303
304         assert(ld);
305         assert(ld->zdev);
306
307         nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
308         if (!sector && end_sector >= ld->nr_sectors) {
309                 /* Reset all zones */
310                 ret = zbc_reset_zone(ld->zdev, 0, ZBC_OP_ALL_ZONES);
311                 if (ret)
312                         goto err;
313
314                 return 0;
315         }
316
317         for (i = 0; i < nr_zones; i++, sector += td->o.zone_size >> 9) {
318                 ret = zbc_reset_zone(ld->zdev, sector, 0);
319                 if (ret)
320                         goto err;
321         }
322
323         return 0;
324
325 err:
326         zbc_errno(ld->zdev, &err);
327         td_verror(td, errno, "zbc_reset_zone failed");
328         if (err.sk)
329                 log_err("%s: reset wp failed %s:%s\n",
330                         f->file_name,
331                         zbc_sk_str(err.sk), zbc_asc_ascq_str(err.asc_ascq));
332         return -ret;
333 }
334
335 static int libzbc_get_max_open_zones(struct thread_data *td, struct fio_file *f,
336                                      unsigned int *max_open_zones)
337 {
338         struct libzbc_data *ld;
339         int ret;
340
341         ret = libzbc_open_dev(td, f, &ld);
342         if (ret)
343                 return ret;
344
345         if (ld->max_open_seq_req == ZBC_NO_LIMIT)
346                 *max_open_zones = 0;
347         else
348                 *max_open_zones = ld->max_open_seq_req;
349
350         return 0;
351 }
352
353 ssize_t libzbc_rw(struct thread_data *td, struct io_u *io_u)
354 {
355         struct libzbc_data *ld = td->io_ops_data;
356         struct fio_file *f = io_u->file;
357         uint64_t sector = io_u->offset >> 9;
358         size_t count = io_u->xfer_buflen >> 9;
359         struct zbc_errno err;
360         ssize_t ret;
361
362         if (io_u->ddir == DDIR_WRITE)
363                 ret = zbc_pwrite(ld->zdev, io_u->xfer_buf, count, sector);
364         else
365                 ret = zbc_pread(ld->zdev, io_u->xfer_buf, count, sector);
366         if (ret == count)
367                 return ret;
368
369         if (ret > 0) {
370                 log_err("Short %s, len=%zu, ret=%zd\n",
371                         io_u->ddir == DDIR_READ ? "read" : "write",
372                         count << 9, ret << 9);
373                 return -EIO;
374         }
375
376         /* I/O error */
377         zbc_errno(ld->zdev, &err);
378         td_verror(td, errno, "libzbc i/o failed");
379         if (err.sk) {
380                 log_err("%s: op %u offset %llu+%llu failed (%s:%s), err %zd\n",
381                         f->file_name, io_u->ddir,
382                         io_u->offset, io_u->xfer_buflen,
383                         zbc_sk_str(err.sk),
384                         zbc_asc_ascq_str(err.asc_ascq), ret);
385         } else {
386                 log_err("%s: op %u offset %llu+%llu failed, err %zd\n",
387                         f->file_name, io_u->ddir,
388                         io_u->offset, io_u->xfer_buflen, ret);
389         }
390
391         return -EIO;
392 }
393
394 static enum fio_q_status libzbc_queue(struct thread_data *td, struct io_u *io_u)
395 {
396         struct libzbc_data *ld = td->io_ops_data;
397         struct fio_file *f = io_u->file;
398         ssize_t ret = 0;
399
400         fio_ro_check(td, io_u);
401
402         dprint(FD_ZBD, "%p:%s: libzbc queue %llu\n",
403                td, f->file_name, io_u->offset);
404
405         if (io_u->ddir == DDIR_READ || io_u->ddir == DDIR_WRITE) {
406                 ret = libzbc_rw(td, io_u);
407         } else if (ddir_sync(io_u->ddir)) {
408                 ret = zbc_flush(ld->zdev);
409                 if (ret)
410                         log_err("zbc_flush error %zd\n", ret);
411         } else if (io_u->ddir == DDIR_TRIM) {
412                 ret = zbd_do_io_u_trim(td, io_u);
413                 if (!ret)
414                         ret = EINVAL;
415         } else {
416                 log_err("Unsupported operation %u\n", io_u->ddir);
417                 ret = -EINVAL;
418         }
419         if (ret < 0)
420                 io_u->error = -ret;
421
422         return FIO_Q_COMPLETED;
423 }
424
425 FIO_STATIC struct ioengine_ops ioengine = {
426         .name                   = "libzbc",
427         .version                = FIO_IOOPS_VERSION,
428         .open_file              = libzbc_open_file,
429         .close_file             = libzbc_close_file,
430         .cleanup                = libzbc_cleanup,
431         .invalidate             = libzbc_invalidate,
432         .get_file_size          = libzbc_get_file_size,
433         .get_zoned_model        = libzbc_get_zoned_model,
434         .report_zones           = libzbc_report_zones,
435         .reset_wp               = libzbc_reset_wp,
436         .get_max_open_zones     = libzbc_get_max_open_zones,
437         .queue                  = libzbc_queue,
438         .flags                  = FIO_SYNCIO | FIO_NOEXTEND | FIO_RAWIO,
439 };
440
441 static void fio_init fio_libzbc_register(void)
442 {
443         register_ioengine(&ioengine);
444 }
445
446 static void fio_exit fio_libzbc_unregister(void)
447 {
448         unregister_ioengine(&ioengine);
449 }