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