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