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