diff --git a/module/bdev/nvme/bdev_ocssd.c b/module/bdev/nvme/bdev_ocssd.c index 76f3eff9d..c8295f8dd 100644 --- a/module/bdev/nvme/bdev_ocssd.c +++ b/module/bdev/nvme/bdev_ocssd.c @@ -52,11 +52,17 @@ struct bdev_ocssd_lba_offsets { }; struct bdev_ocssd_io { - struct { - size_t iov_pos; - size_t iov_off; - uint64_t lba[SPDK_NVME_OCSSD_MAX_LBAL_ENTRIES]; - } io; + union { + struct { + size_t iov_pos; + size_t iov_off; + uint64_t lba[SPDK_NVME_OCSSD_MAX_LBAL_ENTRIES]; + } io; + struct { + size_t chunk_offset; + struct spdk_ocssd_chunk_information_entry chunk_info; + } zone_info; + }; }; struct ocssd_bdev { @@ -74,6 +80,12 @@ bdev_ocssd_get_ns_from_nvme(struct nvme_bdev_ns *nvme_ns) return nvme_ns->type_ctx; } +static struct bdev_ocssd_ns * +bdev_ocssd_get_ns_from_bdev(struct ocssd_bdev *ocssd_bdev) +{ + return bdev_ocssd_get_ns_from_nvme(ocssd_bdev->nvme_bdev.nvme_ns); +} + static int bdev_ocssd_library_init(void) { @@ -130,14 +142,13 @@ bdev_ocssd_destruct(void *ctx) return 0; } -static uint64_t -bdev_ocssd_to_disk_lba(struct ocssd_bdev *ocssd_bdev, uint64_t lba) +static void +bdev_ocssd_translate_lba(struct ocssd_bdev *ocssd_bdev, uint64_t lba, uint64_t *grp, + uint64_t *pu, uint64_t *chk, uint64_t *lbk) { - struct nvme_bdev_ns *nvme_ns = ocssd_bdev->nvme_bdev.nvme_ns; - struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_nvme(nvme_ns); + struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_bdev(ocssd_bdev); const struct spdk_ocssd_geometry_data *geo = &ocssd_ns->geometry; - const struct bdev_ocssd_lba_offsets *offsets = &ocssd_ns->lba_offsets; - uint64_t addr_shift, lbk, chk, pu, grp; + uint64_t addr_shift; /* To achieve best performance, we need to make sure that adjacent zones can be accessed * in parallel. We accomplish this by having the following addressing scheme: @@ -147,16 +158,43 @@ bdev_ocssd_to_disk_lba(struct ocssd_bdev *ocssd_bdev, uint64_t lba) * * which means that neighbouring zones are placed in a different group and parallel unit. */ - lbk = lba % geo->clba; + *lbk = lba % geo->clba; addr_shift = geo->clba; - pu = (lba / addr_shift) % geo->num_pu; + *pu = (lba / addr_shift) % geo->num_pu; addr_shift *= geo->num_pu; - grp = (lba / addr_shift) % geo->num_grp; + *grp = (lba / addr_shift) % geo->num_grp; addr_shift *= geo->num_grp; - chk = (lba / addr_shift) % geo->num_chk; + *chk = (lba / addr_shift) % geo->num_chk; +} + +static uint64_t +bdev_ocssd_from_disk_lba(struct ocssd_bdev *ocssd_bdev, uint64_t lba) +{ + struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_bdev(ocssd_bdev); + const struct spdk_ocssd_geometry_data *geometry = &ocssd_ns->geometry; + const struct bdev_ocssd_lba_offsets *offsets = &ocssd_ns->lba_offsets; + uint64_t lbk, chk, pu, grp; + + lbk = (lba >> offsets->lbk) & ((1 << geometry->lbaf.lbk_len) - 1); + chk = (lba >> offsets->chk) & ((1 << geometry->lbaf.chk_len) - 1); + pu = (lba >> offsets->pu) & ((1 << geometry->lbaf.pu_len) - 1); + grp = (lba >> offsets->grp) & ((1 << geometry->lbaf.grp_len) - 1); + + return lbk + pu * geometry->clba + grp * geometry->num_pu * geometry->clba + + chk * geometry->num_pu * geometry->num_grp * geometry->clba; +} + +static uint64_t +bdev_ocssd_to_disk_lba(struct ocssd_bdev *ocssd_bdev, uint64_t lba) +{ + struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_bdev(ocssd_bdev); + const struct bdev_ocssd_lba_offsets *offsets = &ocssd_ns->lba_offsets; + uint64_t lbk, chk, pu, grp; + + bdev_ocssd_translate_lba(ocssd_bdev, lba, &grp, &pu, &chk, &lbk); return (lbk << offsets->lbk) | (chk << offsets->chk) | @@ -333,6 +371,115 @@ bdev_ocssd_reset_zone(struct spdk_io_channel *ioch, struct spdk_bdev_io *bdev_io ocdev_io->io.lba, num_zones, NULL, bdev_ocssd_reset_zone_cb, bdev_io); } +static int _bdev_ocssd_get_zone_info(struct spdk_bdev_io *bdev_io); + +static void +bdev_ocssd_fill_zone_info(struct ocssd_bdev *ocssd_bdev, struct spdk_bdev_zone_info *zone_info, + const struct spdk_ocssd_chunk_information_entry *chunk_info) +{ + struct nvme_bdev *nvme_bdev = &ocssd_bdev->nvme_bdev; + + zone_info->zone_id = bdev_ocssd_from_disk_lba(ocssd_bdev, chunk_info->slba); + zone_info->write_pointer = zone_info->zone_id; + + if (chunk_info->cs.free) { + zone_info->state = SPDK_BDEV_ZONE_STATE_EMPTY; + } else if (chunk_info->cs.closed) { + zone_info->state = SPDK_BDEV_ZONE_STATE_FULL; + } else if (chunk_info->cs.open) { + zone_info->state = SPDK_BDEV_ZONE_STATE_OPEN; + zone_info->write_pointer += chunk_info->wp % nvme_bdev->disk.zone_size; + } else if (chunk_info->cs.offline) { + zone_info->state = SPDK_BDEV_ZONE_STATE_OFFLINE; + } else { + SPDK_ERRLOG("Unknown chunk state, assuming offline\n"); + zone_info->state = SPDK_BDEV_ZONE_STATE_OFFLINE; + } + + if (chunk_info->ct.size_deviate) { + zone_info->capacity = chunk_info->cnlb; + } else { + zone_info->capacity = nvme_bdev->disk.zone_size; + } +} + +static void +bdev_ocssd_zone_info_cb(void *ctx, const struct spdk_nvme_cpl *cpl) +{ + struct spdk_bdev_io *bdev_io = ctx; + struct ocssd_bdev *ocssd_bdev = bdev_io->bdev->ctxt; + struct bdev_ocssd_io *ocdev_io = (struct bdev_ocssd_io *)bdev_io->driver_ctx; + struct spdk_ocssd_chunk_information_entry *chunk_info = &ocdev_io->zone_info.chunk_info; + struct spdk_bdev_zone_info *zone_info; + int rc; + + if (spdk_unlikely(spdk_nvme_cpl_is_error(cpl))) { + spdk_bdev_io_complete_nvme_status(bdev_io, 0, cpl->status.sct, cpl->status.sc); + return; + } + + zone_info = ((struct spdk_bdev_zone_info *)bdev_io->u.zone_mgmt.buf) + + ocdev_io->zone_info.chunk_offset; + bdev_ocssd_fill_zone_info(ocssd_bdev, zone_info, chunk_info); + + if (++ocdev_io->zone_info.chunk_offset == bdev_io->u.zone_mgmt.num_zones) { + spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS); + } else { + rc = _bdev_ocssd_get_zone_info(bdev_io); + if (spdk_unlikely(rc != 0)) { + if (rc == -ENOMEM) { + spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM); + } else { + spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED); + } + } + } +} + +static int +_bdev_ocssd_get_zone_info(struct spdk_bdev_io *bdev_io) +{ + struct ocssd_bdev *ocssd_bdev = bdev_io->bdev->ctxt; + struct nvme_bdev *nvme_bdev = &ocssd_bdev->nvme_bdev; + struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_bdev(ocssd_bdev); + const struct spdk_ocssd_geometry_data *geo = &ocssd_ns->geometry; + struct bdev_ocssd_io *ocdev_io = (struct bdev_ocssd_io *)bdev_io->driver_ctx; + uint64_t lba, grp, pu, chk, lbk, offset; + + lba = bdev_io->u.zone_mgmt.zone_id + ocdev_io->zone_info.chunk_offset * + nvme_bdev->disk.zone_size; + bdev_ocssd_translate_lba(ocssd_bdev, lba, &grp, &pu, &chk, &lbk); + offset = grp * geo->num_pu * geo->num_chk + pu * geo->num_chk + chk; + + return spdk_nvme_ctrlr_cmd_get_log_page(nvme_bdev->nvme_bdev_ctrlr->ctrlr, + SPDK_OCSSD_LOG_CHUNK_INFO, + spdk_nvme_ns_get_id(nvme_bdev->nvme_ns->ns), + &ocdev_io->zone_info.chunk_info, + sizeof(ocdev_io->zone_info.chunk_info), + offset * sizeof(ocdev_io->zone_info.chunk_info), + bdev_ocssd_zone_info_cb, (void *)bdev_io); +} + +static int +bdev_ocssd_get_zone_info(struct spdk_io_channel *ioch, struct spdk_bdev_io *bdev_io) +{ + struct bdev_ocssd_io *ocdev_io = (struct bdev_ocssd_io *)bdev_io->driver_ctx; + + if (bdev_io->u.zone_mgmt.num_zones < 1) { + SPDK_ERRLOG("Invalid number of zones: %"PRIu32"\n", bdev_io->u.zone_mgmt.num_zones); + return -EINVAL; + } + + if (bdev_io->u.zone_mgmt.zone_id % bdev_io->bdev->zone_size != 0) { + SPDK_ERRLOG("Unaligned zone LBA: %"PRIu64"\n", bdev_io->u.zone_mgmt.zone_id); + return -EINVAL; + } + + ocdev_io->zone_info.chunk_offset = 0; + + return _bdev_ocssd_get_zone_info(bdev_io); +} + static int bdev_ocssd_zone_management(struct spdk_io_channel *ioch, struct spdk_bdev_io *bdev_io) { @@ -364,6 +511,10 @@ bdev_ocssd_submit_request(struct spdk_io_channel *ioch, struct spdk_bdev_io *bde rc = bdev_ocssd_zone_management(ioch, bdev_io); break; + case SPDK_BDEV_IO_TYPE_GET_ZONE_INFO: + rc = bdev_ocssd_get_zone_info(ioch, bdev_io); + break; + default: rc = -EINVAL; break; diff --git a/test/unit/lib/bdev/bdev_ocssd.c/bdev_ocssd_ut.c b/test/unit/lib/bdev/bdev_ocssd.c/bdev_ocssd_ut.c index 452bdab81..62e020e16 100644 --- a/test/unit/lib/bdev/bdev_ocssd.c/bdev_ocssd_ut.c +++ b/test/unit/lib/bdev/bdev_ocssd.c/bdev_ocssd_ut.c @@ -73,6 +73,8 @@ struct spdk_nvme_ctrlr { struct spdk_nvme_qpair *admin_qpair; struct spdk_nvme_ns *ns; uint32_t ns_count; + struct spdk_ocssd_chunk_information_entry *chunk_info; + uint64_t num_chunks; LIST_ENTRY(spdk_nvme_ctrlr) list; }; @@ -101,6 +103,7 @@ free_controller(struct spdk_nvme_ctrlr *ctrlr) CU_ASSERT(!nvme_bdev_ctrlr_get(&ctrlr->trid)); LIST_REMOVE(ctrlr, list); spdk_nvme_ctrlr_free_io_qpair(ctrlr->admin_qpair); + free(ctrlr->chunk_info); free(ctrlr->ns); free(ctrlr); } @@ -120,6 +123,10 @@ create_controller(const struct spdk_nvme_transport_id *trid, uint32_t ns_count, ctrlr->ns = calloc(ns_count, sizeof(*ctrlr->ns)); SPDK_CU_ASSERT_FATAL(ctrlr->ns != NULL); + ctrlr->num_chunks = geo->num_grp * geo->num_pu * geo->num_chk; + ctrlr->chunk_info = calloc(ctrlr->num_chunks, sizeof(*ctrlr->chunk_info)); + SPDK_CU_ASSERT_FATAL(ctrlr->chunk_info != NULL); + for (nsid = 0; nsid < ns_count; ++nsid) { ctrlr->ns[nsid].nsid = nsid + 1; } @@ -218,6 +225,12 @@ spdk_nvme_ctrlr_get_num_ns(struct spdk_nvme_ctrlr *ctrlr) return ctrlr->ns_count; } +uint32_t +spdk_nvme_ns_get_id(struct spdk_nvme_ns *ns) +{ + return ns->nsid; +} + struct spdk_nvme_ns * spdk_nvme_ctrlr_get_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid) { @@ -420,6 +433,24 @@ spdk_nvme_ocssd_ns_cmd_vector_reset(struct spdk_nvme_ns *ns, return 0; } +static struct spdk_nvme_cpl g_chunk_info_cpl; +static bool g_zone_info_status = true; + +int +spdk_nvme_ctrlr_cmd_get_log_page(struct spdk_nvme_ctrlr *ctrlr, + uint8_t log_page, uint32_t nsid, + void *payload, uint32_t payload_size, + uint64_t offset, + spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + SPDK_CU_ASSERT_FATAL(offset + payload_size <= sizeof(*ctrlr->chunk_info) * ctrlr->num_chunks); + memcpy(payload, ((char *)ctrlr->chunk_info) + offset, payload_size); + + cb_fn(cb_arg, &g_chunk_info_cpl); + + return 0; +} + static void create_bdev_cb(const char *bdev_name, int status, void *ctx) { @@ -629,21 +660,27 @@ test_lba_translation(void) lba = bdev_ocssd_to_disk_lba(ocssd_bdev, 0); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 0, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), 0); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size - 1); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, bdev->zone_size - 1, 0, 0, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size - 1); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 1, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size * geometry.num_pu); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 0, 1)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size * geometry.num_pu); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size * geometry.num_pu + 68); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 68, 0, 0, 1)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size * geometry.num_pu + 68); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size + 68); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 68, 0, 1, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size + 68); delete_nvme_bdev_controller(nvme_bdev_ctrlr); free_controller(ctrlr); @@ -673,27 +710,239 @@ test_lba_translation(void) lba = bdev_ocssd_to_disk_lba(ocssd_bdev, 0); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 0, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), 0); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size - 1); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, bdev->zone_size - 1, 0, 0, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size - 1); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 1, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size); lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size * (geometry.num_pu - 1)); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, geometry.num_pu - 1, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), bdev->zone_size * (geometry.num_pu - 1)); - lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size * (geometry.num_pu)); + lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size * geometry.num_pu * geometry.num_grp); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 1, 0, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), + bdev->zone_size * geometry.num_pu * geometry.num_grp); - lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size * (geometry.num_pu) + 68); + lba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev->zone_size * geometry.num_pu * geometry.num_grp + 68); CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 68, 1, 0, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev, lba), + bdev->zone_size * geometry.num_pu * geometry.num_grp + 68); delete_nvme_bdev_controller(nvme_bdev_ctrlr); free_controller(ctrlr); } +static void +get_zone_info_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + CU_ASSERT_EQUAL(g_zone_info_status, success); +} + +static uint64_t +generate_chunk_offset(const struct spdk_ocssd_geometry_data *geo, uint64_t chk, + uint64_t pu, uint64_t grp) +{ + return grp * geo->num_pu * geo->num_chk + + pu * geo->num_chk + chk; +} + +static struct spdk_bdev_io * +alloc_ocssd_io(void) +{ + struct spdk_bdev_io *bdev_io; + + bdev_io = calloc(1, sizeof(struct spdk_bdev_io) + sizeof(struct bdev_ocssd_io)); + SPDK_CU_ASSERT_FATAL(bdev_io != NULL); + + return bdev_io; +} + +static void +set_chunk_info(struct spdk_nvme_ctrlr *ctrlr, uint64_t offset, + const struct spdk_ocssd_chunk_information_entry *chunk_info) +{ + assert(offset < ctrlr->num_chunks); + SPDK_CU_ASSERT_FATAL(offset < ctrlr->num_chunks); + ctrlr->chunk_info[offset] = *chunk_info; +} + +static void +clear_chunk_info(struct spdk_nvme_ctrlr *ctrlr) +{ + memset(ctrlr->chunk_info, 0, sizeof(*ctrlr->chunk_info) * ctrlr->num_chunks); +} + +static void +test_get_zone_info(void) +{ + struct spdk_nvme_ctrlr *ctrlr; + struct nvme_bdev_ctrlr *nvme_bdev_ctrlr; + struct spdk_nvme_transport_id trid = { .traddr = "00:00:00" }; + struct ocssd_bdev *ocssd_bdev; + const char *controller_name = "nvme0"; + const char *bdev_name = "nvme0n1"; + struct spdk_bdev *bdev; + struct spdk_bdev_io *bdev_io; +#define MAX_ZONE_INFO_COUNT 64 + struct spdk_bdev_zone_info zone_info[MAX_ZONE_INFO_COUNT]; + struct spdk_ocssd_chunk_information_entry chunk_info = {}; + struct spdk_ocssd_geometry_data geometry; + uint64_t chunk_offset; + int rc, offset; + + geometry = (struct spdk_ocssd_geometry_data) { + .clba = 512, + .num_chk = 64, + .num_pu = 8, + .num_grp = 4, + .lbaf = { + .lbk_len = 9, + .chk_len = 6, + .pu_len = 3, + .grp_len = 2, + } + }; + + ctrlr = create_controller(&trid, 1, &geometry); + nvme_bdev_ctrlr = create_nvme_bdev_controller(&trid, controller_name); + + rc = create_bdev(controller_name, bdev_name, 1); + CU_ASSERT_EQUAL(rc, 0); + + bdev = spdk_bdev_get_by_name(bdev_name); + SPDK_CU_ASSERT_FATAL(bdev != NULL); + ocssd_bdev = SPDK_CONTAINEROF(bdev, struct ocssd_bdev, nvme_bdev.disk); + + bdev_io = alloc_ocssd_io(); + bdev_io->internal.cb = get_zone_info_cb; + bdev_io->bdev = bdev; + + /* Verify empty zone */ + bdev_io->u.zone_mgmt.zone_id = 0; + bdev_io->u.zone_mgmt.num_zones = 1; + bdev_io->u.zone_mgmt.buf = &zone_info; + chunk_info.cs.free = 1; + chunk_info.wp = 0; + set_chunk_info(ctrlr, 0, &chunk_info); + + rc = bdev_ocssd_get_zone_info(NULL, bdev_io); + CU_ASSERT_EQUAL(rc, 0); + clear_chunk_info(ctrlr); + + CU_ASSERT_EQUAL(zone_info[0].state, SPDK_BDEV_ZONE_STATE_EMPTY); + CU_ASSERT_EQUAL(zone_info[0].zone_id, 0); + CU_ASSERT_EQUAL(zone_info[0].write_pointer, 0); + CU_ASSERT_EQUAL(zone_info[0].capacity, geometry.clba); + + /* Verify open zone */ + bdev_io->u.zone_mgmt.zone_id = bdev->zone_size; + bdev_io->u.zone_mgmt.num_zones = 1; + bdev_io->u.zone_mgmt.buf = &zone_info; + memset(&chunk_info, 0, sizeof(chunk_info)); + chunk_info.cs.open = 1; + chunk_info.wp = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev_io->u.zone_mgmt.zone_id + 68); + chunk_info.slba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev_io->u.zone_mgmt.zone_id); + chunk_info.cnlb = 511; + chunk_info.ct.size_deviate = 1; + set_chunk_info(ctrlr, generate_chunk_offset(&geometry, 0, 1, 0), &chunk_info); + + rc = bdev_ocssd_get_zone_info(NULL, bdev_io); + CU_ASSERT_EQUAL(rc, 0); + clear_chunk_info(ctrlr); + + CU_ASSERT_EQUAL(zone_info[0].state, SPDK_BDEV_ZONE_STATE_OPEN); + CU_ASSERT_EQUAL(zone_info[0].zone_id, bdev->zone_size); + CU_ASSERT_EQUAL(zone_info[0].write_pointer, bdev->zone_size + 68); + CU_ASSERT_EQUAL(zone_info[0].capacity, chunk_info.cnlb); + + /* Verify offline zone at 2nd chunk */ + bdev_io->u.zone_mgmt.zone_id = bdev->zone_size * geometry.num_pu * geometry.num_grp; + bdev_io->u.zone_mgmt.num_zones = 1; + bdev_io->u.zone_mgmt.buf = &zone_info; + memset(&chunk_info, 0, sizeof(chunk_info)); + chunk_info.cs.offline = 1; + chunk_info.wp = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev_io->u.zone_mgmt.zone_id); + chunk_info.slba = bdev_ocssd_to_disk_lba(ocssd_bdev, bdev_io->u.zone_mgmt.zone_id); + set_chunk_info(ctrlr, generate_chunk_offset(&geometry, 1, 0, 0), &chunk_info); + + rc = bdev_ocssd_get_zone_info(NULL, bdev_io); + CU_ASSERT_EQUAL(rc, 0); + clear_chunk_info(ctrlr); + + CU_ASSERT_EQUAL(zone_info[0].state, SPDK_BDEV_ZONE_STATE_OFFLINE); + CU_ASSERT_EQUAL(zone_info[0].zone_id, bdev_io->u.zone_mgmt.zone_id); + CU_ASSERT_EQUAL(zone_info[0].write_pointer, bdev_io->u.zone_mgmt.zone_id); + + /* Verify multiple zones at a time */ + bdev_io->u.zone_mgmt.zone_id = 0; + bdev_io->u.zone_mgmt.num_zones = MAX_ZONE_INFO_COUNT; + bdev_io->u.zone_mgmt.buf = &zone_info; + + for (offset = 0; offset < MAX_ZONE_INFO_COUNT; ++offset) { + chunk_offset = generate_chunk_offset(&geometry, + (offset / (geometry.num_grp * geometry.num_pu)) % geometry.num_chk, + offset % geometry.num_pu, + (offset / geometry.num_pu) % geometry.num_grp); + + + memset(&chunk_info, 0, sizeof(chunk_info)); + chunk_info.cs.open = 1; + chunk_info.wp = bdev_ocssd_to_disk_lba(ocssd_bdev, offset * bdev->zone_size + 68); + chunk_info.slba = bdev_ocssd_to_disk_lba(ocssd_bdev, offset * bdev->zone_size); + set_chunk_info(ctrlr, chunk_offset, &chunk_info); + } + + rc = bdev_ocssd_get_zone_info(NULL, bdev_io); + CU_ASSERT_EQUAL(rc, 0); + + for (offset = 0; offset < MAX_ZONE_INFO_COUNT; ++offset) { + CU_ASSERT_EQUAL(zone_info[offset].state, SPDK_BDEV_ZONE_STATE_OPEN); + CU_ASSERT_EQUAL(zone_info[offset].zone_id, bdev->zone_size * offset); + CU_ASSERT_EQUAL(zone_info[offset].write_pointer, bdev->zone_size * offset + 68); + CU_ASSERT_EQUAL(zone_info[offset].capacity, geometry.clba); + } + + clear_chunk_info(ctrlr); + + /* Verify misaligned start zone LBA */ + bdev_io->u.zone_mgmt.zone_id = 1; + bdev_io->u.zone_mgmt.num_zones = MAX_ZONE_INFO_COUNT; + bdev_io->u.zone_mgmt.buf = &zone_info; + + rc = bdev_ocssd_get_zone_info(NULL, bdev_io); + CU_ASSERT_EQUAL(rc, -EINVAL); + + /* Verify correct NVMe error forwarding */ + bdev_io->u.zone_mgmt.zone_id = 0; + bdev_io->u.zone_mgmt.num_zones = MAX_ZONE_INFO_COUNT; + bdev_io->u.zone_mgmt.buf = &zone_info; + + rc = bdev_ocssd_get_zone_info(NULL, bdev_io); + CU_ASSERT_EQUAL(rc, 0); + g_chunk_info_cpl = (struct spdk_nvme_cpl) { + .status = { + .sct = SPDK_NVME_SCT_GENERIC, + .sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR + } + }; + g_zone_info_status = false; + + g_chunk_info_cpl = (struct spdk_nvme_cpl) {}; + g_zone_info_status = true; + + delete_nvme_bdev_controller(nvme_bdev_ctrlr); + + free(bdev_io); + free_controller(ctrlr); +} + int main(int argc, const char **argv) { @@ -713,7 +962,8 @@ main(int argc, const char **argv) if ( CU_add_test(suite, "test_create_controller", test_create_controller) == NULL || CU_add_test(suite, "test_device_geometry", test_device_geometry) == NULL || - CU_add_test(suite, "test_lba_translation", test_lba_translation) == NULL + CU_add_test(suite, "test_lba_translation", test_lba_translation) == NULL || + CU_add_test(suite, "test_get_zone_info", test_get_zone_info) == NULL ) { CU_cleanup_registry(); return CU_get_error();