From 64fe514efadca0d4ad1390785c139d0c6b945657 Mon Sep 17 00:00:00 2001 From: Konrad Sztyber Date: Mon, 30 Sep 2019 15:04:08 +0200 Subject: [PATCH] bdev/ocssd: parallel unit ranges This patch adds the ability to create bdevs on specified parallel unit ranges on one OCSSD controller. It allows the user to create multiple isolated bdevs, each operating on a separate set of parallel units. To create a bdev on a specified range, a new parameter -r|--range was added. For example: rpc.py bdev_ocssd_create -c nvme0 -b nvme0n1 -r 0-4 will create a bdev on parallel units 0 through 4 (inclusive). If the parameter is ommited, the bdev will span across all available units. Change-Id: Icd94cf1e22fcc72806ef1ce1efd2d7031010009f Signed-off-by: Konrad Sztyber Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/469898 Community-CI: Broadcom SPDK FC-NVMe CI Tested-by: SPDK CI Jenkins Reviewed-by: Shuhei Matsumoto Reviewed-by: Jim Harris --- module/bdev/nvme/bdev_ocssd.c | 126 +++++++++++--- module/bdev/nvme/bdev_ocssd.h | 6 + module/bdev/nvme/bdev_ocssd_rpc.c | 30 +++- scripts/rpc.py | 5 +- scripts/rpc/bdev.py | 6 +- .../lib/bdev/bdev_ocssd.c/bdev_ocssd_ut.c | 162 +++++++++++++++++- 6 files changed, 294 insertions(+), 41 deletions(-) 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();