engines/libzbc: for read workloads always open devices with O_RDONLY flag
[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                         flags |= O_RDONLY;
72         }
73
74         if (td->o.oatomic) {
75                 td_verror(td, EINVAL, "libzbc does not support O_ATOMIC");
76                 log_err("%s: libzbc does not support O_ATOMIC\n", f->file_name);
77                 return -EINVAL;
78         }
79
80         ld = calloc(1, sizeof(*ld));
81         if (!ld)
82                 return -ENOMEM;
83
84         ret = zbc_open(f->file_name,
85                        flags | ZBC_O_DRV_SCSI | ZBC_O_DRV_ATA,
86                        &ld->zdev);
87         if (ret) {
88                 log_err("%s: zbc_open() failed, err=%d\n",
89                         f->file_name, ret);
90                 goto err;
91         }
92
93         ret = libzbc_get_dev_info(ld, f);
94         if (ret)
95                 goto err_close;
96
97         td->io_ops_data = ld;
98 out:
99         if (p_ld)
100                 *p_ld = ld;
101
102         return 0;
103
104 err_close:
105         zbc_close(ld->zdev);
106 err:
107         free(ld);
108         return ret;
109 }
110
111 static int libzbc_close_dev(struct thread_data *td)
112 {
113         struct libzbc_data *ld = td->io_ops_data;
114         int ret = 0;
115
116         td->io_ops_data = NULL;
117         if (ld) {
118                 if (ld->zdev)
119                         ret = zbc_close(ld->zdev);
120                 free(ld);
121         }
122
123         return ret;
124 }
125 static int libzbc_open_file(struct thread_data *td, struct fio_file *f)
126 {
127         return libzbc_open_dev(td, f, NULL);
128 }
129
130 static int libzbc_close_file(struct thread_data *td, struct fio_file *f)
131 {
132         int ret;
133
134         ret = libzbc_close_dev(td);
135         if (ret)
136                 log_err("%s: close device failed err %d\n",
137                         f->file_name, ret);
138
139         return ret;
140 }
141
142 static void libzbc_cleanup(struct thread_data *td)
143 {
144         libzbc_close_dev(td);
145 }
146
147 static int libzbc_invalidate(struct thread_data *td, struct fio_file *f)
148 {
149         /* Passthrough IO do not cache data. Nothing to do */
150         return 0;
151 }
152
153 static int libzbc_get_file_size(struct thread_data *td, struct fio_file *f)
154 {
155         struct libzbc_data *ld;
156         int ret;
157
158         if (fio_file_size_known(f))
159                 return 0;
160
161         ret = libzbc_open_dev(td, f, &ld);
162         if (ret)
163                 return ret;
164
165         f->real_file_size = ld->nr_sectors << 9;
166         fio_file_set_size_known(f);
167
168         return 0;
169 }
170
171 static int libzbc_get_zoned_model(struct thread_data *td, struct fio_file *f,
172                                   enum zbd_zoned_model *model)
173 {
174         struct libzbc_data *ld;
175         int ret;
176
177         if (f->filetype != FIO_TYPE_BLOCK && f->filetype != FIO_TYPE_CHAR)
178                 return -EINVAL;
179
180         ret = libzbc_open_dev(td, f, &ld);
181         if (ret)
182                 return ret;
183
184         switch (ld->model) {
185         case ZBC_DM_HOST_AWARE:
186                 *model = ZBD_HOST_AWARE;
187                 break;
188         case ZBC_DM_HOST_MANAGED:
189                 *model = ZBD_HOST_MANAGED;
190                 break;
191         default:
192                 *model = ZBD_NONE;
193                 break;
194         }
195
196         return 0;
197 }
198
199 static int libzbc_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 libzbc_data *ld;
204         uint64_t sector = offset >> 9;
205         struct zbc_zone *zones;
206         unsigned int i;
207         int ret;
208
209         ret = libzbc_open_dev(td, f, &ld);
210         if (ret)
211                 return ret;
212
213         if (sector >= ld->nr_sectors)
214                 return 0;
215
216         zones = calloc(nr_zones, sizeof(struct zbc_zone));
217         if (!zones) {
218                 ret = -ENOMEM;
219                 goto out;
220         }
221
222         ret = zbc_report_zones(ld->zdev, sector, ZBC_RO_ALL, zones, &nr_zones);
223         if (ret < 0) {
224                 log_err("%s: zbc_report_zones failed, err=%d\n",
225                         f->file_name, ret);
226                 goto out;
227         }
228
229         for (i = 0; i < nr_zones; i++, zbdz++) {
230                 zbdz->start = zones[i].zbz_start << 9;
231                 zbdz->len = zones[i].zbz_length << 9;
232                 zbdz->wp = zones[i].zbz_write_pointer << 9;
233                 /*
234                  * ZBC/ZAC do not define zone capacity, so use the zone size as
235                  * the zone capacity.
236                  */
237                 zbdz->capacity = zbdz->len;
238
239                 switch (zones[i].zbz_type) {
240                 case ZBC_ZT_CONVENTIONAL:
241                         zbdz->type = ZBD_ZONE_TYPE_CNV;
242                         break;
243                 case ZBC_ZT_SEQUENTIAL_REQ:
244                         zbdz->type = ZBD_ZONE_TYPE_SWR;
245                         break;
246                 case ZBC_ZT_SEQUENTIAL_PREF:
247                         zbdz->type = ZBD_ZONE_TYPE_SWP;
248                         break;
249                 default:
250                         td_verror(td, errno, "invalid zone type");
251                         log_err("%s: invalid type for zone at sector %llu.\n",
252                                 f->file_name, (unsigned long long)zbdz->start);
253                         ret = -EIO;
254                         goto out;
255                 }
256
257                 switch (zones[i].zbz_condition) {
258                 case ZBC_ZC_NOT_WP:
259                         zbdz->cond = ZBD_ZONE_COND_NOT_WP;
260                         break;
261                 case ZBC_ZC_EMPTY:
262                         zbdz->cond = ZBD_ZONE_COND_EMPTY;
263                         break;
264                 case ZBC_ZC_IMP_OPEN:
265                         zbdz->cond = ZBD_ZONE_COND_IMP_OPEN;
266                         break;
267                 case ZBC_ZC_EXP_OPEN:
268                         zbdz->cond = ZBD_ZONE_COND_EXP_OPEN;
269                         break;
270                 case ZBC_ZC_CLOSED:
271                         zbdz->cond = ZBD_ZONE_COND_CLOSED;
272                         break;
273                 case ZBC_ZC_FULL:
274                         zbdz->cond = ZBD_ZONE_COND_FULL;
275                         break;
276                 case ZBC_ZC_RDONLY:
277                 case ZBC_ZC_OFFLINE:
278                 default:
279                         /* Treat all these conditions as offline (don't use!) */
280                         zbdz->cond = ZBD_ZONE_COND_OFFLINE;
281                         zbdz->wp = zbdz->start;
282                 }
283         }
284
285         ret = nr_zones;
286 out:
287         free(zones);
288         return ret;
289 }
290
291 static int libzbc_reset_wp(struct thread_data *td, struct fio_file *f,
292                            uint64_t offset, uint64_t length)
293 {
294         struct libzbc_data *ld = td->io_ops_data;
295         uint64_t sector = offset >> 9;
296         uint64_t end_sector = (offset + length) >> 9;
297         unsigned int nr_zones;
298         struct zbc_errno err;
299         int i, ret;
300
301         assert(ld);
302         assert(ld->zdev);
303
304         nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
305         if (!sector && end_sector >= ld->nr_sectors) {
306                 /* Reset all zones */
307                 ret = zbc_reset_zone(ld->zdev, 0, ZBC_OP_ALL_ZONES);
308                 if (ret)
309                         goto err;
310
311                 return 0;
312         }
313
314         for (i = 0; i < nr_zones; i++, sector += td->o.zone_size >> 9) {
315                 ret = zbc_reset_zone(ld->zdev, sector, 0);
316                 if (ret)
317                         goto err;
318         }
319
320         return 0;
321
322 err:
323         zbc_errno(ld->zdev, &err);
324         td_verror(td, errno, "zbc_reset_zone failed");
325         if (err.sk)
326                 log_err("%s: reset wp failed %s:%s\n",
327                         f->file_name,
328                         zbc_sk_str(err.sk), zbc_asc_ascq_str(err.asc_ascq));
329         return -ret;
330 }
331
332 static int libzbc_finish_zone(struct thread_data *td, struct fio_file *f,
333                               uint64_t offset, uint64_t length)
334 {
335         struct libzbc_data *ld = td->io_ops_data;
336         uint64_t sector = offset >> 9;
337         unsigned int nr_zones;
338         struct zbc_errno err;
339         int i, ret;
340
341         assert(ld);
342         assert(ld->zdev);
343
344         nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
345         assert(nr_zones > 0);
346
347         for (i = 0; i < nr_zones; i++, sector += td->o.zone_size >> 9) {
348                 ret = zbc_finish_zone(ld->zdev, sector, 0);
349                 if (ret)
350                         goto err;
351         }
352
353         return 0;
354
355 err:
356         zbc_errno(ld->zdev, &err);
357         td_verror(td, errno, "zbc_finish_zone failed");
358         if (err.sk)
359                 log_err("%s: finish zone failed %s:%s\n",
360                         f->file_name,
361                         zbc_sk_str(err.sk), zbc_asc_ascq_str(err.asc_ascq));
362         return -ret;
363 }
364
365 static int libzbc_get_max_open_zones(struct thread_data *td, struct fio_file *f,
366                                      unsigned int *max_open_zones)
367 {
368         struct libzbc_data *ld;
369         int ret;
370
371         ret = libzbc_open_dev(td, f, &ld);
372         if (ret)
373                 return ret;
374
375         if (ld->max_open_seq_req == ZBC_NO_LIMIT)
376                 *max_open_zones = 0;
377         else
378                 *max_open_zones = ld->max_open_seq_req;
379
380         return 0;
381 }
382
383 ssize_t libzbc_rw(struct thread_data *td, struct io_u *io_u)
384 {
385         struct libzbc_data *ld = td->io_ops_data;
386         struct fio_file *f = io_u->file;
387         uint64_t sector = io_u->offset >> 9;
388         size_t count = io_u->xfer_buflen >> 9;
389         struct zbc_errno err;
390         ssize_t ret;
391
392         if (io_u->ddir == DDIR_WRITE)
393                 ret = zbc_pwrite(ld->zdev, io_u->xfer_buf, count, sector);
394         else
395                 ret = zbc_pread(ld->zdev, io_u->xfer_buf, count, sector);
396         if (ret == count)
397                 return ret;
398
399         if (ret > 0) {
400                 log_err("Short %s, len=%zu, ret=%zd\n",
401                         io_u->ddir == DDIR_READ ? "read" : "write",
402                         count << 9, ret << 9);
403                 return -EIO;
404         }
405
406         /* I/O error */
407         zbc_errno(ld->zdev, &err);
408         td_verror(td, errno, "libzbc i/o failed");
409         if (err.sk) {
410                 log_err("%s: op %u offset %llu+%llu failed (%s:%s), err %zd\n",
411                         f->file_name, io_u->ddir,
412                         io_u->offset, io_u->xfer_buflen,
413                         zbc_sk_str(err.sk),
414                         zbc_asc_ascq_str(err.asc_ascq), ret);
415         } else {
416                 log_err("%s: op %u offset %llu+%llu failed, err %zd\n",
417                         f->file_name, io_u->ddir,
418                         io_u->offset, io_u->xfer_buflen, ret);
419         }
420
421         return -EIO;
422 }
423
424 static enum fio_q_status libzbc_queue(struct thread_data *td, struct io_u *io_u)
425 {
426         struct libzbc_data *ld = td->io_ops_data;
427         struct fio_file *f = io_u->file;
428         ssize_t ret = 0;
429
430         fio_ro_check(td, io_u);
431
432         dprint(FD_ZBD, "%p:%s: libzbc queue %llu\n",
433                td, f->file_name, io_u->offset);
434
435         if (io_u->ddir == DDIR_READ || io_u->ddir == DDIR_WRITE) {
436                 ret = libzbc_rw(td, io_u);
437         } else if (ddir_sync(io_u->ddir)) {
438                 ret = zbc_flush(ld->zdev);
439                 if (ret)
440                         log_err("zbc_flush error %zd\n", ret);
441         } else if (io_u->ddir == DDIR_TRIM) {
442                 ret = zbd_do_io_u_trim(td, io_u);
443                 if (!ret)
444                         ret = EINVAL;
445         } else {
446                 log_err("Unsupported operation %u\n", io_u->ddir);
447                 ret = -EINVAL;
448         }
449         if (ret < 0)
450                 io_u->error = -ret;
451
452         return FIO_Q_COMPLETED;
453 }
454
455 FIO_STATIC struct ioengine_ops ioengine = {
456         .name                   = "libzbc",
457         .version                = FIO_IOOPS_VERSION,
458         .open_file              = libzbc_open_file,
459         .close_file             = libzbc_close_file,
460         .cleanup                = libzbc_cleanup,
461         .invalidate             = libzbc_invalidate,
462         .get_file_size          = libzbc_get_file_size,
463         .get_zoned_model        = libzbc_get_zoned_model,
464         .report_zones           = libzbc_report_zones,
465         .reset_wp               = libzbc_reset_wp,
466         .get_max_open_zones     = libzbc_get_max_open_zones,
467         .finish_zone            = libzbc_finish_zone,
468         .queue                  = libzbc_queue,
469         .flags                  = FIO_SYNCIO | FIO_NOEXTEND | FIO_RAWIO,
470 };
471
472 static void fio_init fio_libzbc_register(void)
473 {
474         register_ioengine(&ioengine);
475 }
476
477 static void fio_exit fio_libzbc_unregister(void)
478 {
479         unregister_ioengine(&ioengine);
480 }