Add zone device specific ioengine_ops for I/O engine io_uring_cmd. * get_zoned_model * report_zones * reset_wp * get_max_open_zones These engine_ops are guarded by CONFIG_LIBNVME flag. For write workload iodepth must be set to 1 as there is no IO scheduler Signed-off-by: Ankit Kumar <ankit.kumar@xxxxxxxxxxx> --- engines/io_uring.c | 229 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) diff --git a/engines/io_uring.c b/engines/io_uring.c index 75248624..14e54566 100644 --- a/engines/io_uring.c +++ b/engines/io_uring.c @@ -1297,6 +1297,229 @@ static int fio_ioring_cmd_get_file_size(struct thread_data *td, } #endif +#ifdef CONFIG_LIBNVME +static int fio_ioring_cmd_get_zoned_model(struct thread_data *td, + struct fio_file *f, + enum zbd_zoned_model *model) +{ + struct nvme_data *data = FILE_ENG_DATA(f); + struct nvme_id_ctrl ctrl; + struct nvme_id_ns ns; + int fd, ret = 0; + + if (f->filetype != FIO_TYPE_CHAR) + return -EINVAL; + + /* File is not yet opened */ + fd = open(f->file_name, O_RDONLY | O_LARGEFILE); + if (fd < 0) + return -errno; + + ret = nvme_identify_ctrl_csi(fd, NVME_CSI_ZNS, &ctrl); + if (ret) { + *model = ZBD_NONE; + goto out; + } + + ret = nvme_identify_ns_csi(fd, data->nsid, NVME_UUID_NONE, + NVME_CSI_ZNS, &ns); + if (ret) { + *model = ZBD_NONE; + goto out; + } + + *model = ZBD_HOST_MANAGED; +out: + close(fd); + return 0; +} + +static int fio_ioring_cmd_report_zones(struct thread_data *td, + struct fio_file *f, uint64_t offset, + struct zbd_zone *zbdz, + unsigned int nr_zones) +{ + struct nvme_data *data = FILE_ENG_DATA(f); + struct nvme_zone_report *zr; + struct nvme_zns_id_ns zns_ns; + struct nvme_id_ns ns; + unsigned int i, lba_size; + int fd, ret = 0; + int zr_len; + + /* File is not yet opened */ + fd = open(f->file_name, O_RDONLY | O_LARGEFILE); + if (fd < 0) + return -errno; + + zr_len = sizeof(*zr); + zr = calloc(1, zr_len); + if (!zr) + return -ENOMEM; + + ret = nvme_identify_ns(fd, data->nsid, &ns); + if (ret) { + log_err("%s: nvme_identify_ns failed, err=%d\n", f->file_name, + ret); + goto out; + } + lba_size = data->lba_size; + + ret = nvme_zns_report_zones(fd, data->nsid, offset >> lba_size, + NVME_ZNS_ZRAS_REPORT_ALL, false, + false, zr_len, (void *)zr, + NVME_DEFAULT_IOCTL_TIMEOUT, NULL); + if (ret) { + log_err("%s: nvme_zns_report_zones failed, err=%d\n", + f->file_name, ret); + goto out; + } + nr_zones = zr->nr_zones; + + ret = nvme_zns_identify_ns(fd, data->nsid, &zns_ns); + if (ret) { + log_err("%s: nvme_zns_identify_ns failed, err=%d\n", + f->file_name, ret); + goto out; + } + + zr_len = sizeof(*zr) + (nr_zones * sizeof(struct nvme_zns_desc)); + zr = realloc(zr, zr_len); + memset(zr, 0, zr_len); + + ret = nvme_zns_report_zones(fd, data->nsid, offset >> lba_size, + NVME_ZNS_ZRAS_REPORT_ALL, false, + true, zr_len, (void *)zr, + NVME_DEFAULT_IOCTL_TIMEOUT, NULL); + if (ret) { + log_err("%s: nvme_zns_report_zones failed, err=%d\n", + f->file_name, ret); + goto out; + } + + /* Transform the zone-report */ + for (i = 0; i < zr->nr_zones; i++) { + struct nvme_zns_desc *desc = (struct nvme_zns_desc *)&(zr->entries[i]); + + zbdz[i].start = desc->zslba * lba_size; + zbdz[i].len = zns_ns.lbafe[ns.flbas & 0x0f].zsze * lba_size; + zbdz[i].wp = desc->wp * lba_size; + zbdz[i].capacity = desc->zcap * lba_size; + + /* Zone Type is stored in first 4 bits. */ + switch (desc->zt & 0x0f) { + case NVME_ZONE_TYPE_SEQWRITE_REQ: + zbdz[i].type = ZBD_ZONE_TYPE_SWR; + break; + default: + log_err("%s: invalid type for zone at offset %llu.\n", + f->file_name, desc->zslba); + ret = -EIO; + goto out; + } + + /* Zone State is stored in last 4 bits. */ + switch (desc->zs >> 4) { + case NVME_ZNS_ZS_EMPTY: + zbdz[i].cond = ZBD_ZONE_COND_EMPTY; + break; + case NVME_ZNS_ZS_IMPL_OPEN: + zbdz[i].cond = ZBD_ZONE_COND_IMP_OPEN; + break; + case NVME_ZNS_ZS_EXPL_OPEN: + zbdz[i].cond = ZBD_ZONE_COND_EXP_OPEN; + break; + case NVME_ZNS_ZS_CLOSED: + zbdz[i].cond = ZBD_ZONE_COND_CLOSED; + break; + case NVME_ZNS_ZS_FULL: + zbdz[i].cond = ZBD_ZONE_COND_FULL; + break; + case NVME_ZNS_ZS_READ_ONLY: + case NVME_ZNS_ZS_OFFLINE: + default: + /* Treat all these conditions as offline (don't use!) */ + zbdz[i].cond = ZBD_ZONE_COND_OFFLINE; + zbdz[i].wp = zbdz[i].start; + } + } + + ret = zr->nr_zones; +out: + free(zr); + close(fd); + + return ret; +} + +static int fio_ioring_cmd_reset_wp(struct thread_data *td, struct fio_file *f, + uint64_t offset, uint64_t length) +{ + struct nvme_data *data = FILE_ENG_DATA(f); + unsigned int nr_zones, lba_size; + unsigned long long zslba; + int i, fd, ret = 0; + + /* If the file is not yet opened, open it for this function. */ + fd = f->fd; + if (fd < 0) { + fd = open(f->file_name, O_RDWR | O_LARGEFILE); + if (fd < 0) + return -errno; + } + + lba_size = data->lba_size; + zslba = offset / lba_size; + nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size; + + for (i = 0; i < nr_zones; i++, zslba += (td->o.zone_size / lba_size)) { + struct nvme_zns_mgmt_send_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = data->nsid, + .slba = zslba, + .zsa = NVME_ZNS_ZSA_RESET, + .select_all = 0, + .zsaso = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + ret = nvme_zns_mgmt_send(&args); + } + + if (f->fd < 0) + close(fd); + return -ret; +} + +static int fio_ioring_cmd_get_max_open_zones(struct thread_data *td, + struct fio_file *f, + unsigned int *max_open_zones) +{ + struct nvme_data *data = FILE_ENG_DATA(f); + struct nvme_zns_id_ns zns_ns; + int fd, ret = 0; + + fd = open(f->file_name, O_RDONLY | O_LARGEFILE); + if (fd < 0) + return -errno; + + ret = nvme_zns_identify_ns(fd, data->nsid, &zns_ns); + if (ret) { + log_err("%s: nvme_zns_identify_ns failed, err=%d\n", + f->file_name, ret); + goto out; + } + + *max_open_zones = zns_ns.mor + 1; +out: + close(fd); + return ret; +} +#endif + static struct ioengine_ops ioengine_uring = { .name = "io_uring", .version = FIO_IOOPS_VERSION, @@ -1334,6 +1557,12 @@ static struct ioengine_ops ioengine_uring_cmd = { .open_file = fio_ioring_cmd_open_file, .close_file = fio_ioring_cmd_close_file, .get_file_size = fio_ioring_cmd_get_file_size, +#ifdef CONFIG_LIBNVME + .get_zoned_model = fio_ioring_cmd_get_zoned_model, + .report_zones = fio_ioring_cmd_report_zones, + .reset_wp = fio_ioring_cmd_reset_wp, + .get_max_open_zones = fio_ioring_cmd_get_max_open_zones, +#endif .options = options, .option_struct_size = sizeof(struct ioring_options), }; -- 2.17.1