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