2 * Copyright (C) 2019 Western Digital Corporation or its affiliates.
4 * This file is released under the GPL.
7 * IO engine using libzbc library to talk to SMR disks.
12 #include <libzbc/zbc.h>
16 #include "zbd_types.h"
20 struct zbc_device *zdev;
21 enum zbc_dev_model model;
23 uint32_t max_open_seq_req;
26 static int libzbc_get_dev_info(struct libzbc_data *ld, struct fio_file *f)
28 struct zbc_device_info *zinfo;
30 zinfo = calloc(1, sizeof(*zinfo));
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;
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));
49 static int libzbc_open_dev(struct thread_data *td, struct fio_file *f,
50 struct libzbc_data **p_ld)
52 struct libzbc_data *ld = td->io_ops_data;
53 int ret, flags = OS_O_DIRECT;
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");
67 if (td_write(td) || td_trim(td)) {
70 } else if (td_read(td)) {
71 if (f->filetype == FIO_TYPE_CHAR && !read_only)
78 td_verror(td, EINVAL, "libzbc does not support O_ATOMIC");
79 log_err("%s: libzbc does not support O_ATOMIC\n", f->file_name);
83 ld = calloc(1, sizeof(*ld));
87 ret = zbc_open(f->file_name,
88 flags | ZBC_O_DRV_SCSI | ZBC_O_DRV_ATA,
91 log_err("%s: zbc_open() failed, err=%d\n",
96 ret = libzbc_get_dev_info(ld, f);
100 td->io_ops_data = ld;
114 static int libzbc_close_dev(struct thread_data *td)
116 struct libzbc_data *ld = td->io_ops_data;
119 td->io_ops_data = NULL;
122 ret = zbc_close(ld->zdev);
128 static int libzbc_open_file(struct thread_data *td, struct fio_file *f)
130 return libzbc_open_dev(td, f, NULL);
133 static int libzbc_close_file(struct thread_data *td, struct fio_file *f)
137 ret = libzbc_close_dev(td);
139 log_err("%s: close device failed err %d\n",
145 static void libzbc_cleanup(struct thread_data *td)
147 libzbc_close_dev(td);
150 static int libzbc_invalidate(struct thread_data *td, struct fio_file *f)
152 /* Passthrough IO do not cache data. Nothing to do */
156 static int libzbc_get_file_size(struct thread_data *td, struct fio_file *f)
158 struct libzbc_data *ld;
161 if (fio_file_size_known(f))
164 ret = libzbc_open_dev(td, f, &ld);
168 f->real_file_size = ld->nr_sectors << 9;
169 fio_file_set_size_known(f);
174 static int libzbc_get_zoned_model(struct thread_data *td, struct fio_file *f,
175 enum zbd_zoned_model *model)
177 struct libzbc_data *ld;
180 if (f->filetype != FIO_TYPE_BLOCK && f->filetype != FIO_TYPE_CHAR)
183 ret = libzbc_open_dev(td, f, &ld);
188 case ZBC_DM_HOST_AWARE:
189 *model = ZBD_HOST_AWARE;
191 case ZBC_DM_HOST_MANAGED:
192 *model = ZBD_HOST_MANAGED;
202 static int libzbc_report_zones(struct thread_data *td, struct fio_file *f,
203 uint64_t offset, struct zbd_zone *zbdz,
204 unsigned int nr_zones)
206 struct libzbc_data *ld;
207 uint64_t sector = offset >> 9;
208 struct zbc_zone *zones;
212 ret = libzbc_open_dev(td, f, &ld);
216 if (sector >= ld->nr_sectors)
219 zones = calloc(nr_zones, sizeof(struct zbc_zone));
225 ret = zbc_report_zones(ld->zdev, sector, ZBC_RO_ALL, zones, &nr_zones);
227 log_err("%s: zbc_report_zones failed, err=%d\n",
232 for (i = 0; i < nr_zones; i++, zbdz++) {
233 zbdz->start = zones[i].zbz_start << 9;
234 zbdz->len = zones[i].zbz_length << 9;
235 zbdz->wp = zones[i].zbz_write_pointer << 9;
237 * ZBC/ZAC do not define zone capacity, so use the zone size as
240 zbdz->capacity = zbdz->len;
242 switch (zones[i].zbz_type) {
243 case ZBC_ZT_CONVENTIONAL:
244 zbdz->type = ZBD_ZONE_TYPE_CNV;
246 case ZBC_ZT_SEQUENTIAL_REQ:
247 zbdz->type = ZBD_ZONE_TYPE_SWR;
249 case ZBC_ZT_SEQUENTIAL_PREF:
250 zbdz->type = ZBD_ZONE_TYPE_SWP;
253 td_verror(td, errno, "invalid zone type");
254 log_err("%s: invalid type for zone at sector %llu.\n",
255 f->file_name, (unsigned long long)zbdz->start);
260 switch (zones[i].zbz_condition) {
262 zbdz->cond = ZBD_ZONE_COND_NOT_WP;
265 zbdz->cond = ZBD_ZONE_COND_EMPTY;
267 case ZBC_ZC_IMP_OPEN:
268 zbdz->cond = ZBD_ZONE_COND_IMP_OPEN;
270 case ZBC_ZC_EXP_OPEN:
271 zbdz->cond = ZBD_ZONE_COND_EXP_OPEN;
274 zbdz->cond = ZBD_ZONE_COND_CLOSED;
277 zbdz->cond = ZBD_ZONE_COND_FULL;
282 /* Treat all these conditions as offline (don't use!) */
283 zbdz->cond = ZBD_ZONE_COND_OFFLINE;
284 zbdz->wp = zbdz->start;
294 static int libzbc_reset_wp(struct thread_data *td, struct fio_file *f,
295 uint64_t offset, uint64_t length)
297 struct libzbc_data *ld = td->io_ops_data;
298 uint64_t sector = offset >> 9;
299 uint64_t end_sector = (offset + length) >> 9;
300 unsigned int nr_zones;
301 struct zbc_errno err;
307 nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
308 if (!sector && end_sector >= ld->nr_sectors) {
309 /* Reset all zones */
310 ret = zbc_reset_zone(ld->zdev, 0, ZBC_OP_ALL_ZONES);
317 for (i = 0; i < nr_zones; i++, sector += td->o.zone_size >> 9) {
318 ret = zbc_reset_zone(ld->zdev, sector, 0);
326 zbc_errno(ld->zdev, &err);
327 td_verror(td, errno, "zbc_reset_zone failed");
329 log_err("%s: reset wp failed %s:%s\n",
331 zbc_sk_str(err.sk), zbc_asc_ascq_str(err.asc_ascq));
335 static int libzbc_finish_zone(struct thread_data *td, struct fio_file *f,
336 uint64_t offset, uint64_t length)
338 struct libzbc_data *ld = td->io_ops_data;
339 uint64_t sector = offset >> 9;
340 unsigned int nr_zones;
341 struct zbc_errno err;
347 nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
348 assert(nr_zones > 0);
350 for (i = 0; i < nr_zones; i++, sector += td->o.zone_size >> 9) {
351 ret = zbc_finish_zone(ld->zdev, sector, 0);
359 zbc_errno(ld->zdev, &err);
360 td_verror(td, errno, "zbc_finish_zone failed");
362 log_err("%s: finish zone failed %s:%s\n",
364 zbc_sk_str(err.sk), zbc_asc_ascq_str(err.asc_ascq));
368 static int libzbc_get_max_open_zones(struct thread_data *td, struct fio_file *f,
369 unsigned int *max_open_zones)
371 struct libzbc_data *ld;
374 ret = libzbc_open_dev(td, f, &ld);
378 if (ld->max_open_seq_req == ZBC_NO_LIMIT)
381 *max_open_zones = ld->max_open_seq_req;
386 ssize_t libzbc_rw(struct thread_data *td, struct io_u *io_u)
388 struct libzbc_data *ld = td->io_ops_data;
389 struct fio_file *f = io_u->file;
390 uint64_t sector = io_u->offset >> 9;
391 size_t count = io_u->xfer_buflen >> 9;
392 struct zbc_errno err;
395 if (io_u->ddir == DDIR_WRITE)
396 ret = zbc_pwrite(ld->zdev, io_u->xfer_buf, count, sector);
398 ret = zbc_pread(ld->zdev, io_u->xfer_buf, count, sector);
403 log_err("Short %s, len=%zu, ret=%zd\n",
404 io_u->ddir == DDIR_READ ? "read" : "write",
405 count << 9, ret << 9);
410 zbc_errno(ld->zdev, &err);
411 td_verror(td, errno, "libzbc i/o failed");
413 log_err("%s: op %u offset %llu+%llu failed (%s:%s), err %zd\n",
414 f->file_name, io_u->ddir,
415 io_u->offset, io_u->xfer_buflen,
417 zbc_asc_ascq_str(err.asc_ascq), ret);
419 log_err("%s: op %u offset %llu+%llu failed, err %zd\n",
420 f->file_name, io_u->ddir,
421 io_u->offset, io_u->xfer_buflen, ret);
427 static enum fio_q_status libzbc_queue(struct thread_data *td, struct io_u *io_u)
429 struct libzbc_data *ld = td->io_ops_data;
430 struct fio_file *f = io_u->file;
433 fio_ro_check(td, io_u);
435 dprint(FD_ZBD, "%p:%s: libzbc queue %llu\n",
436 td, f->file_name, io_u->offset);
438 if (io_u->ddir == DDIR_READ || io_u->ddir == DDIR_WRITE) {
439 ret = libzbc_rw(td, io_u);
440 } else if (ddir_sync(io_u->ddir)) {
441 ret = zbc_flush(ld->zdev);
443 log_err("zbc_flush error %zd\n", ret);
444 } else if (io_u->ddir == DDIR_TRIM) {
445 ret = zbd_do_io_u_trim(td, io_u);
449 log_err("Unsupported operation %u\n", io_u->ddir);
455 return FIO_Q_COMPLETED;
458 FIO_STATIC struct ioengine_ops ioengine = {
460 .version = FIO_IOOPS_VERSION,
461 .open_file = libzbc_open_file,
462 .close_file = libzbc_close_file,
463 .cleanup = libzbc_cleanup,
464 .invalidate = libzbc_invalidate,
465 .get_file_size = libzbc_get_file_size,
466 .get_zoned_model = libzbc_get_zoned_model,
467 .report_zones = libzbc_report_zones,
468 .reset_wp = libzbc_reset_wp,
469 .get_max_open_zones = libzbc_get_max_open_zones,
470 .finish_zone = libzbc_finish_zone,
471 .queue = libzbc_queue,
472 .flags = FIO_SYNCIO | FIO_NOEXTEND | FIO_RAWIO,
475 static void fio_init fio_libzbc_register(void)
477 register_ioengine(&ioengine);
480 static void fio_exit fio_libzbc_unregister(void)
482 unregister_ioengine(&ioengine);