diff --git a/module/bdev/nvme/bdev_ocssd.c b/module/bdev/nvme/bdev_ocssd.c index 2e6df7b97..a8403fd4f 100644 --- a/module/bdev/nvme/bdev_ocssd.c +++ b/module/bdev/nvme/bdev_ocssd.c @@ -82,6 +82,7 @@ struct ocssd_io_channel { struct ocssd_bdev { struct nvme_bdev nvme_bdev; struct bdev_ocssd_zone *zones; + struct bdev_ocssd_range range; }; struct bdev_ocssd_ns { @@ -101,6 +102,18 @@ 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 uint64_t +bdev_ocssd_num_parallel_units(const struct ocssd_bdev *ocssd_bdev) +{ + return ocssd_bdev->range.end - ocssd_bdev->range.begin + 1; +} + +static uint64_t +bdev_ocssd_num_zones(const struct ocssd_bdev *ocssd_bdev) +{ + return ocssd_bdev->nvme_bdev.disk.blockcnt / ocssd_bdev->nvme_bdev.disk.zone_size; +} + static int bdev_ocssd_library_init(void) { @@ -123,9 +136,20 @@ bdev_ocssd_namespace_config_json(struct spdk_json_write_ctx *w, struct nvme_bdev { struct nvme_bdev_ctrlr *nvme_bdev_ctrlr; struct nvme_bdev *nvme_bdev; + struct ocssd_bdev *ocssd_bdev; + char range_buf[128]; + int rc; TAILQ_FOREACH(nvme_bdev, &ns->bdevs, tailq) { nvme_bdev_ctrlr = nvme_bdev->nvme_bdev_ctrlr; + ocssd_bdev = SPDK_CONTAINEROF(nvme_bdev, struct ocssd_bdev, nvme_bdev); + + rc = snprintf(range_buf, sizeof(range_buf), "%"PRIu64"-%"PRIu64, + ocssd_bdev->range.begin, ocssd_bdev->range.end); + if (rc < 0 || rc >= (int)sizeof(range_buf)) { + SPDK_ERRLOG("Failed to convert parallel unit range\n"); + continue; + } spdk_json_write_object_begin(w); spdk_json_write_named_string(w, "method", "bdev_ocssd_create"); @@ -134,6 +158,7 @@ bdev_ocssd_namespace_config_json(struct spdk_json_write_ctx *w, struct nvme_bdev spdk_json_write_named_string(w, "ctrlr_name", nvme_bdev_ctrlr->name); spdk_json_write_named_string(w, "bdev_name", nvme_bdev->disk.name); spdk_json_write_named_uint32(w, "nsid", nvme_bdev->nvme_ns->id); + spdk_json_write_named_string(w, "range", range_buf); spdk_json_write_object_end(w); spdk_json_write_object_end(w); @@ -211,7 +236,8 @@ bdev_ocssd_translate_lba(struct ocssd_bdev *ocssd_bdev, uint64_t lba, uint64_t * { struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_bdev(ocssd_bdev); const struct spdk_ocssd_geometry_data *geo = &ocssd_ns->geometry; - uint64_t addr_shift; + const struct bdev_ocssd_range *range = &ocssd_bdev->range; + uint64_t addr_shift, punit; /* 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: @@ -224,11 +250,12 @@ bdev_ocssd_translate_lba(struct ocssd_bdev *ocssd_bdev, uint64_t lba, uint64_t * *lbk = lba % geo->clba; addr_shift = geo->clba; - *pu = (lba / addr_shift) % geo->num_pu; - addr_shift *= geo->num_pu; + punit = range->begin + (lba / addr_shift) % bdev_ocssd_num_parallel_units(ocssd_bdev); - *grp = (lba / addr_shift) % geo->num_grp; - addr_shift *= geo->num_grp; + *pu = punit % geo->num_pu; + *grp = punit / geo->num_pu; + + addr_shift *= bdev_ocssd_num_parallel_units(ocssd_bdev); *chk = (lba / addr_shift) % geo->num_chk; } @@ -239,15 +266,18 @@ 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; + const struct bdev_ocssd_range *range = &ocssd_bdev->range; + uint64_t lbk, chk, pu, grp, punit; 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; + punit = grp * geometry->num_pu + pu - range->begin; + + return lbk + punit * geometry->clba + chk * geometry->clba * + bdev_ocssd_num_parallel_units(ocssd_bdev); } static uint64_t @@ -773,8 +803,9 @@ struct bdev_ocssd_create_ctx { struct ocssd_bdev *ocssd_bdev; bdev_ocssd_create_cb cb_fn; void *cb_arg; + const struct bdev_ocssd_range *range; uint64_t chunk_offset; - uint64_t num_total_chunks; + uint64_t end_chunk_offset; uint64_t num_chunks; #define OCSSD_BDEV_CHUNK_INFO_COUNT 128 struct spdk_ocssd_chunk_information_entry chunk_info[OCSSD_BDEV_CHUNK_INFO_COUNT]; @@ -850,7 +881,7 @@ bdev_occsd_init_zone_cb(void *ctx, const struct spdk_nvme_cpl *cpl) } create_ctx->chunk_offset += create_ctx->num_chunks; - if (create_ctx->chunk_offset < create_ctx->num_total_chunks) { + if (create_ctx->chunk_offset < create_ctx->end_chunk_offset) { rc = bdev_ocssd_init_zone(create_ctx); if (spdk_unlikely(rc != 0)) { SPDK_ERRLOG("Failed to send chunk info log page\n"); @@ -858,7 +889,7 @@ bdev_occsd_init_zone_cb(void *ctx, const struct spdk_nvme_cpl *cpl) } } else { /* Make sure all zones have been processed */ - for (offset = 0; offset < create_ctx->num_total_chunks; ++offset) { + for (offset = 0; offset < bdev_ocssd_num_zones(ocssd_bdev); ++offset) { assert(!ocssd_bdev->zones[offset].busy); } @@ -875,7 +906,7 @@ bdev_ocssd_init_zone(struct bdev_ocssd_create_ctx *create_ctx) struct ocssd_bdev *ocssd_bdev = create_ctx->ocssd_bdev; struct nvme_bdev *nvme_bdev = &ocssd_bdev->nvme_bdev; - create_ctx->num_chunks = spdk_min(create_ctx->num_total_chunks - create_ctx->chunk_offset, + create_ctx->num_chunks = spdk_min(create_ctx->end_chunk_offset - create_ctx->chunk_offset, OCSSD_BDEV_CHUNK_INFO_COUNT); assert(create_ctx->num_chunks > 0); @@ -894,28 +925,69 @@ static int bdev_ocssd_init_zones(struct bdev_ocssd_create_ctx *create_ctx) { struct ocssd_bdev *ocssd_bdev = create_ctx->ocssd_bdev; + struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_bdev(ocssd_bdev); struct spdk_bdev *bdev = &ocssd_bdev->nvme_bdev.disk; uint64_t offset; - ocssd_bdev->zones = calloc(bdev->blockcnt / bdev->zone_size, sizeof(*ocssd_bdev->zones)); + ocssd_bdev->zones = calloc(bdev_ocssd_num_zones(ocssd_bdev), sizeof(*ocssd_bdev->zones)); if (!ocssd_bdev->zones) { return -ENOMEM; } - create_ctx->num_total_chunks = bdev->blockcnt / bdev->zone_size; - create_ctx->chunk_offset = 0; + create_ctx->chunk_offset = ocssd_bdev->range.begin * ocssd_ns->geometry.num_chk; + create_ctx->end_chunk_offset = create_ctx->chunk_offset + bdev->blockcnt / bdev->zone_size; /* Mark all zones as busy and clear it as their info is filled */ - for (offset = 0; offset < create_ctx->num_total_chunks; ++offset) { + for (offset = 0; offset < bdev_ocssd_num_zones(ocssd_bdev); ++offset) { ocssd_bdev->zones[offset].busy = true; } return bdev_ocssd_init_zone(create_ctx); } +static bool +bdev_ocssd_verify_range(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr, uint32_t nsid, + const struct bdev_ocssd_range *range) +{ + struct nvme_bdev_ns *nvme_ns = nvme_bdev_ctrlr->namespaces[nsid - 1]; + struct bdev_ocssd_ns *ocssd_ns = bdev_ocssd_get_ns_from_nvme(nvme_ns); + const struct spdk_ocssd_geometry_data *geometry = &ocssd_ns->geometry; + struct ocssd_bdev *ocssd_bdev; + struct nvme_bdev *nvme_bdev; + size_t num_punits = geometry->num_pu * geometry->num_grp; + + /* First verify the range is within the geometry */ + if (range != NULL && (range->begin > range->end || range->end >= num_punits)) { + return false; + } + + TAILQ_FOREACH(nvme_bdev, &nvme_ns->bdevs, tailq) { + ocssd_bdev = SPDK_CONTAINEROF(nvme_bdev, struct ocssd_bdev, nvme_bdev); + + /* Only verify bdevs created on the same namespace */ + if (spdk_nvme_ns_get_id(nvme_bdev->nvme_ns->ns) != nsid) { + continue; + } + + /* Empty range means whole namespace should be used */ + if (range == NULL) { + return false; + } + + /* Make sure the range doesn't overlap with any other range */ + if (range->begin <= ocssd_bdev->range.end && + range->end >= ocssd_bdev->range.begin) { + return false; + } + } + + return true; +} + void bdev_ocssd_create_bdev(const char *ctrlr_name, const char *bdev_name, uint32_t nsid, - bdev_ocssd_create_cb cb_fn, void *cb_arg) + const struct bdev_ocssd_range *range, bdev_ocssd_create_cb cb_fn, + void *cb_arg) { struct nvme_bdev_ctrlr *nvme_bdev_ctrlr; struct bdev_ocssd_create_ctx *create_ctx = NULL; @@ -968,11 +1040,9 @@ bdev_ocssd_create_bdev(const char *ctrlr_name, const char *bdev_name, uint32_t n goto error; } - /* Only allow one bdev per namespace for now */ - if (!TAILQ_EMPTY(&nvme_ns->bdevs)) { - SPDK_ERRLOG("Namespace %"PRIu32" was already claimed by bdev %s\n", - nsid, TAILQ_FIRST(&nvme_ns->bdevs)->disk.name); - rc = -EEXIST; + if (!bdev_ocssd_verify_range(nvme_bdev_ctrlr, nsid, range)) { + SPDK_ERRLOG("Invalid parallel unit range\n"); + rc = -EINVAL; goto error; } @@ -991,12 +1061,20 @@ bdev_ocssd_create_bdev(const char *ctrlr_name, const char *bdev_name, uint32_t n create_ctx->ocssd_bdev = ocssd_bdev; create_ctx->cb_fn = cb_fn; create_ctx->cb_arg = cb_arg; + create_ctx->range = range; nvme_bdev = &ocssd_bdev->nvme_bdev; nvme_bdev->nvme_ns = nvme_ns; nvme_bdev->nvme_bdev_ctrlr = nvme_bdev_ctrlr; geometry = &ocssd_ns->geometry; + if (range != NULL) { + ocssd_bdev->range = *range; + } else { + ocssd_bdev->range.begin = 0; + ocssd_bdev->range.end = geometry->num_grp * geometry->num_pu - 1; + } + nvme_bdev->disk.name = strdup(bdev_name); if (!nvme_bdev->disk.name) { rc = -ENOMEM; @@ -1009,11 +1087,11 @@ bdev_ocssd_create_bdev(const char *ctrlr_name, const char *bdev_name, uint32_t n nvme_bdev->disk.module = &ocssd_if; nvme_bdev->disk.blocklen = spdk_nvme_ns_get_extended_sector_size(ns); nvme_bdev->disk.zoned = true; - nvme_bdev->disk.blockcnt = geometry->num_grp * geometry->num_pu * + nvme_bdev->disk.blockcnt = bdev_ocssd_num_parallel_units(ocssd_bdev) * geometry->num_chk * geometry->clba; nvme_bdev->disk.zone_size = geometry->clba; nvme_bdev->disk.max_open_zones = geometry->maxoc; - nvme_bdev->disk.optimal_open_zones = geometry->num_grp * geometry->num_pu; + nvme_bdev->disk.optimal_open_zones = bdev_ocssd_num_parallel_units(ocssd_bdev); nvme_bdev->disk.write_unit_size = geometry->ws_opt; if (geometry->maxocpu != 0 && geometry->maxocpu != geometry->maxoc) { diff --git a/module/bdev/nvme/bdev_ocssd.h b/module/bdev/nvme/bdev_ocssd.h index a444d4e8d..66219a85b 100644 --- a/module/bdev/nvme/bdev_ocssd.h +++ b/module/bdev/nvme/bdev_ocssd.h @@ -37,10 +37,16 @@ #include "spdk/stdinc.h" #include "common.h" +struct bdev_ocssd_range { + uint64_t begin; + uint64_t end; +}; + typedef void (*bdev_ocssd_create_cb)(const char *bdev_name, int status, void *ctx); typedef void (*bdev_ocssd_delete_cb)(int status, void *ctx); void bdev_ocssd_create_bdev(const char *ctrlr_name, const char *bdev_name, uint32_t nsid, + const struct bdev_ocssd_range *range, bdev_ocssd_create_cb cb_fn, void *cb_arg); void bdev_ocssd_delete_bdev(const char *bdev_name, bdev_ocssd_delete_cb cb_fn, void *cb_arg); diff --git a/module/bdev/nvme/bdev_ocssd_rpc.c b/module/bdev/nvme/bdev_ocssd_rpc.c index 8271b1d3a..3d97dde11 100644 --- a/module/bdev/nvme/bdev_ocssd_rpc.c +++ b/module/bdev/nvme/bdev_ocssd_rpc.c @@ -45,12 +45,14 @@ struct rpc_create_ocssd_bdev { char *ctrlr_name; char *bdev_name; uint32_t nsid; + const char *range; }; static const struct spdk_json_object_decoder rpc_create_ocssd_bdev_decoders[] = { {"ctrlr_name", offsetof(struct rpc_create_ocssd_bdev, ctrlr_name), spdk_json_decode_string}, {"bdev_name", offsetof(struct rpc_create_ocssd_bdev, bdev_name), spdk_json_decode_string}, {"nsid", offsetof(struct rpc_create_ocssd_bdev, nsid), spdk_json_decode_uint32, true}, + {"range", offsetof(struct rpc_create_ocssd_bdev, range), spdk_json_decode_string, true}, }; static void @@ -61,8 +63,9 @@ free_rpc_create_ocssd_bdev(struct rpc_create_ocssd_bdev *rpc) } struct rpc_bdev_ocssd_create_ctx { - struct spdk_jsonrpc_request *request; - struct rpc_create_ocssd_bdev rpc; + struct spdk_jsonrpc_request *request; + struct rpc_create_ocssd_bdev rpc; + struct bdev_ocssd_range range; }; static void @@ -88,6 +91,8 @@ static void rpc_bdev_ocssd_create(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_ocssd_create_ctx *ctx; + struct bdev_ocssd_range *range = NULL; + int rc; ctx = calloc(1, sizeof(*ctx)); if (!ctx) { @@ -102,13 +107,26 @@ rpc_bdev_ocssd_create(struct spdk_jsonrpc_request *request, const struct spdk_js SPDK_COUNTOF(rpc_create_ocssd_bdev_decoders), &ctx->rpc)) { spdk_jsonrpc_send_error_response(request, -EINVAL, "Failed to parse the request"); - free_rpc_create_ocssd_bdev(&ctx->rpc); - free(ctx); - return; + goto out; + } + + if (ctx->rpc.range != NULL) { + rc = sscanf(ctx->rpc.range, "%"PRIu64"-%"PRIu64, + &ctx->range.begin, &ctx->range.end); + if (rc != 2) { + spdk_jsonrpc_send_error_response(request, -EINVAL, "Failed to parse range"); + goto out; + } + + range = &ctx->range; } bdev_ocssd_create_bdev(ctx->rpc.ctrlr_name, ctx->rpc.bdev_name, ctx->rpc.nsid, - rpc_bdev_ocssd_create_done, ctx); + range, rpc_bdev_ocssd_create_done, ctx); + return; +out: + free_rpc_create_ocssd_bdev(&ctx->rpc); + free(ctx); } SPDK_RPC_REGISTER("bdev_ocssd_create", rpc_bdev_ocssd_create, SPDK_RPC_RUNTIME) diff --git a/scripts/rpc.py b/scripts/rpc.py index 9692a915d..5ac8b313a 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2072,13 +2072,16 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse print_json(rpc.bdev.bdev_ocssd_create(args.client, ctrlr_name=args.ctrlr_name, bdev_name=args.name, - nsid=nsid)) + nsid=nsid, + range=args.range)) p = subparsers.add_parser('bdev_ocssd_create', help='Creates zoned bdev on specified Open Channel controller') p.add_argument('-c', '--ctrlr_name', help='Name of the OC NVMe controller', required=True) p.add_argument('-b', '--name', help='Name of the bdev to create', required=True) p.add_argument('-n', '--nsid', help='Namespace ID', required=False) + p.add_argument('-r', '--range', help='Parallel unit range (in the form of BEGIN-END (inclusive))', + required=False) p.set_defaults(func=bdev_ocssd_create) def bdev_ocssd_delete(args): diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py index fe33406b2..9f11fa51a 100644 --- a/scripts/rpc/bdev.py +++ b/scripts/rpc/bdev.py @@ -917,13 +917,14 @@ def bdev_ftl_delete(client, name): return client.call('bdev_ftl_delete', params) -def bdev_ocssd_create(client, ctrlr_name, bdev_name, nsid=None): +def bdev_ocssd_create(client, ctrlr_name, bdev_name, nsid=None, range=None): """Creates Open Channel zoned bdev on specified Open Channel controller Args: ctrlr_name: name of the OC NVMe controller bdev_name: name of the bdev to create nsid: namespace ID + range: parallel unit range """ params = {'ctrlr_name': ctrlr_name, 'bdev_name': bdev_name} @@ -931,6 +932,9 @@ def bdev_ocssd_create(client, ctrlr_name, bdev_name, nsid=None): if nsid is not None: params['nsid'] = nsid + if range is not None: + params['range'] = range + return client.call('bdev_ocssd_create', params) 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 bb91b279b..0669d4eee 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 @@ -487,11 +487,12 @@ create_bdev_cb(const char *bdev_name, int status, void *ctx) } static int -create_bdev(const char *ctrlr_name, const char *bdev_name, uint32_t nsid) +create_bdev(const char *ctrlr_name, const char *bdev_name, uint32_t nsid, + const struct bdev_ocssd_range *range) { int status = EFAULT; - bdev_ocssd_create_bdev(ctrlr_name, bdev_name, nsid, create_bdev_cb, &status); + bdev_ocssd_create_bdev(ctrlr_name, bdev_name, nsid, range, create_bdev_cb, &status); while (spdk_thread_poll(g_thread, 0, 0) > 0) {} @@ -539,6 +540,7 @@ test_create_controller(void) struct spdk_nvme_transport_id trid = { .traddr = "00:00:00" }; struct spdk_ocssd_geometry_data geometry = {}; struct spdk_bdev *bdev; + struct bdev_ocssd_range range; const char *controller_name = "nvme0"; const size_t ns_count = 16; char namebuf[128]; @@ -566,7 +568,7 @@ test_create_controller(void) for (nsid = 1; nsid <= ns_count; ++nsid) { snprintf(namebuf, sizeof(namebuf), "%sn%"PRIu32, controller_name, nsid); - rc = create_bdev(controller_name, namebuf, nsid); + rc = create_bdev(controller_name, namebuf, nsid, NULL); CU_ASSERT_EQUAL(rc, 0); bdev = spdk_bdev_get_by_name(namebuf); @@ -581,7 +583,7 @@ test_create_controller(void) for (nsid = 1; nsid <= ns_count; ++nsid) { snprintf(namebuf, sizeof(namebuf), "%sn%"PRIu32, controller_name, nsid); - rc = create_bdev(controller_name, namebuf, nsid); + rc = create_bdev(controller_name, namebuf, nsid, NULL); CU_ASSERT_EQUAL(rc, 0); bdev = spdk_bdev_get_by_name(namebuf); @@ -594,11 +596,43 @@ test_create_controller(void) nvme_bdev_ctrlr = create_nvme_bdev_controller(&trid, controller_name); /* Verify it's not possible to create a bdev on non-existent namespace */ - rc = create_bdev(controller_name, "invalid", ns_count + 1); + rc = create_bdev(controller_name, "invalid", ns_count + 1, NULL); CU_ASSERT_EQUAL(rc, -ENODEV); delete_nvme_bdev_controller(nvme_bdev_ctrlr); + /* Verify the correctness of parallel unit range validation */ + nvme_bdev_ctrlr = create_nvme_bdev_controller(&trid, controller_name); + + range.begin = 0; + range.end = geometry.num_grp * geometry.num_pu; + + rc = create_bdev(controller_name, "invalid", 1, &range); + CU_ASSERT_EQUAL(rc, -EINVAL); + + /* Verify it's not possible for the bdevs to overlap */ + range.begin = 0; + range.end = 16; + rc = create_bdev(controller_name, "valid", 1, &range); + CU_ASSERT_EQUAL(rc, 0); + bdev = spdk_bdev_get_by_name("valid"); + CU_ASSERT_PTR_NOT_NULL(bdev); + + range.begin = 16; + range.end = 31; + rc = create_bdev(controller_name, "invalid", 1, &range); + CU_ASSERT_EQUAL(rc, -EINVAL); + + /* But it is possible to create them without overlap */ + range.begin = 17; + range.end = 31; + rc = create_bdev(controller_name, "valid2", 1, &range); + CU_ASSERT_EQUAL(rc, 0); + bdev = spdk_bdev_get_by_name("valid2"); + CU_ASSERT_PTR_NOT_NULL(bdev); + + delete_nvme_bdev_controller(nvme_bdev_ctrlr); + free_controller(ctrlr); } @@ -633,7 +667,7 @@ test_device_geometry(void) ctrlr = create_controller(&trid, 1, &geometry); nvme_bdev_ctrlr = create_nvme_bdev_controller(&trid, controller_name); - rc = create_bdev(controller_name, bdev_name, 1); + rc = create_bdev(controller_name, bdev_name, 1, NULL); CU_ASSERT_EQUAL(rc, 0); bdev = spdk_bdev_get_by_name(bdev_name); @@ -704,7 +738,7 @@ test_lba_translation(void) ctrlr = create_controller(&trid, 1, &geometry); nvme_bdev_ctrlr = create_nvme_bdev_controller(&trid, controller_name); - rc = create_bdev(controller_name, bdev_name, 1); + rc = create_bdev(controller_name, bdev_name, 1, NULL); CU_ASSERT_EQUAL(rc, 0); bdev = spdk_bdev_get_by_name(bdev_name); @@ -754,7 +788,7 @@ test_lba_translation(void) ctrlr = create_controller(&trid, 1, &geometry); nvme_bdev_ctrlr = create_nvme_bdev_controller(&trid, controller_name); - rc = create_bdev(controller_name, bdev_name, 1); + rc = create_bdev(controller_name, bdev_name, 1, NULL); CU_ASSERT_EQUAL(rc, 0); bdev = spdk_bdev_get_by_name(bdev_name); @@ -792,6 +826,115 @@ test_lba_translation(void) free_controller(ctrlr); } +static void +punit_range_to_addr(const struct spdk_nvme_ctrlr *ctrlr, uint64_t punit, + uint64_t *grp, uint64_t *pu) +{ + const struct spdk_ocssd_geometry_data *geo = &ctrlr->geometry; + + *grp = punit / geo->num_pu; + *pu = punit % geo->num_pu; + + CU_ASSERT(*grp < geo->num_grp); +} + +static void +test_parallel_unit_range(void) +{ + struct spdk_nvme_ctrlr *ctrlr; + struct nvme_bdev_ctrlr *nvme_bdev_ctrlr; + struct spdk_nvme_transport_id trid = { .traddr = "00:00:00" }; + const char *controller_name = "nvme0"; + const char *bdev_name[] = { "nvme0n1", "nvme0n2", "nvme0n3" }; + const struct bdev_ocssd_range range[3] = { { 0, 5 }, { 6, 18 }, { 19, 23 } }; + struct ocssd_bdev *ocssd_bdev[3]; + struct spdk_ocssd_geometry_data geometry = {}; + struct spdk_bdev *bdev[3]; + uint64_t lba, i, offset, grp, pu, zone_size; + int rc; + + geometry = (struct spdk_ocssd_geometry_data) { + .clba = 500, + .num_chk = 60, + .num_pu = 8, + .num_grp = 3, + .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); + + for (i = 0; i < SPDK_COUNTOF(range); ++i) { + rc = create_bdev(controller_name, bdev_name[i], 1, &range[i]); + CU_ASSERT_EQUAL(rc, 0); + + bdev[i] = spdk_bdev_get_by_name(bdev_name[i]); + SPDK_CU_ASSERT_FATAL(bdev[i] != NULL); + ocssd_bdev[i] = SPDK_CONTAINEROF(bdev[i], struct ocssd_bdev, nvme_bdev.disk); + } + + zone_size = bdev[0]->zone_size; + CU_ASSERT_EQUAL(zone_size, bdev[1]->zone_size); + CU_ASSERT_EQUAL(zone_size, bdev[2]->zone_size); + + /* Verify the first addresses are correct */ + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[0], 0); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 0, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[0], lba), 0); + + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[1], 0); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 6, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[1], lba), 0); + + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[2], 0); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 0, 0, 3, 2)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[2], lba), 0); + + /* Verify last address correctness */ + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[0], bdev[0]->blockcnt - 1); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, geometry.clba - 1, geometry.num_chk - 1, 5, 0)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[0], lba), bdev[0]->blockcnt - 1); + + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[1], bdev[1]->blockcnt - 1); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, geometry.clba - 1, geometry.num_chk - 1, 2, 2)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[1], lba), bdev[1]->blockcnt - 1); + + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[2], bdev[2]->blockcnt - 1); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, geometry.clba - 1, geometry.num_chk - 1, 7, 2)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[2], lba), bdev[2]->blockcnt - 1); + + /* Verify correct jumps across parallel units / groups */ + for (i = 0; i < SPDK_COUNTOF(range); ++i) { + for (offset = 0; offset < bdev_ocssd_num_parallel_units(ocssd_bdev[i]); ++offset) { + punit_range_to_addr(ctrlr, range[i].begin + offset, &grp, &pu); + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[i], offset * zone_size + 68); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 68, 0, pu, grp)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[i], lba), + offset * zone_size + 68); + } + } + + /* Verify correct address wrapping */ + for (i = 0; i < SPDK_COUNTOF(range); ++i) { + punit_range_to_addr(ctrlr, range[i].begin, &grp, &pu); + + offset = bdev_ocssd_num_parallel_units(ocssd_bdev[i]) * zone_size + 68; + lba = bdev_ocssd_to_disk_lba(ocssd_bdev[i], offset); + CU_ASSERT_EQUAL(lba, generate_lba(&geometry, 68, 1, pu, grp)); + assert(lba == generate_lba(&geometry, 68, 1, pu, grp)); + CU_ASSERT_EQUAL(bdev_ocssd_from_disk_lba(ocssd_bdev[i], lba), offset); + } + + 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) { @@ -887,7 +1030,7 @@ test_get_zone_info(void) ctrlr = create_controller(&trid, 1, &geometry); nvme_bdev_ctrlr = create_nvme_bdev_controller(&trid, controller_name); - rc = create_bdev(controller_name, bdev_name, 1); + rc = create_bdev(controller_name, bdev_name, 1, NULL); CU_ASSERT_EQUAL(rc, 0); bdev = spdk_bdev_get_by_name(bdev_name); @@ -1028,6 +1171,7 @@ main(int argc, const char **argv) 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_parallel_unit_range", test_parallel_unit_range) == NULL || CU_add_test(suite, "test_get_zone_info", test_get_zone_info) == NULL ) { CU_cleanup_registry();