From c3e3e81a507c75559b85b418dfa23710b15d26ec Mon Sep 17 00:00:00 2001 From: Mateusz Kozlowski Date: Fri, 13 Sep 2019 15:43:09 +0200 Subject: [PATCH] bdev/zone: Get zone info Added handling of get zone info command Signed-off-by: Mateusz Kozlowski Change-Id: I2d80885af83345c945af22a46a41abf55e1eb413 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/468036 Tested-by: SPDK CI Jenkins Community-CI: Broadcom SPDK FC-NVMe CI Community-CI: SPDK CI Jenkins Reviewed-by: Ben Walker Reviewed-by: Jim Harris Reviewed-by: Shuhei Matsumoto Reviewed-by: Konrad Sztyber --- module/bdev/zone_block/vbdev_zone_block.c | 74 +++++- .../vbdev_zone_block.c/vbdev_zone_block_ut.c | 215 +++++++++++++++++- 2 files changed, 286 insertions(+), 3 deletions(-) diff --git a/module/bdev/zone_block/vbdev_zone_block.c b/module/bdev/zone_block/vbdev_zone_block.c index da04eec9b..ae5e972de 100644 --- a/module/bdev/zone_block/vbdev_zone_block.c +++ b/module/bdev/zone_block/vbdev_zone_block.c @@ -82,6 +82,7 @@ struct bdev_zone_block { struct block_zone *zones; /* array of zones */ uint64_t num_zones; /* number of zones */ uint64_t zone_capacity; /* zone capacity */ + uint64_t zone_shift; /* log2 of zone_size */ TAILQ_ENTRY(bdev_zone_block) link; }; static TAILQ_HEAD(, bdev_zone_block) g_bdev_nodes = TAILQ_HEAD_INITIALIZER(g_bdev_nodes); @@ -178,16 +179,83 @@ zone_block_destruct(void *ctx) return 0; } +static struct block_zone * +zone_block_get_zone_by_slba(struct bdev_zone_block *bdev_node, uint64_t start_lba) +{ + struct block_zone *zone = NULL; + size_t index = start_lba >> bdev_node->zone_shift; + + if (index >= bdev_node->num_zones) { + return NULL; + } + + zone = &bdev_node->zones[index]; + if (zone->zone_info.zone_id == start_lba) { + return zone; + } else { + return NULL; + } +} + +static int +zone_block_get_zone_info(struct bdev_zone_block *bdev_node, struct spdk_bdev_io *bdev_io) +{ + struct block_zone *zone; + struct spdk_bdev_zone_info *zone_info = bdev_io->u.zone_mgmt.buf; + uint64_t zone_id = bdev_io->u.zone_mgmt.zone_id; + size_t i; + + /* User can request info for more zones than exist, need to check both internal and user + * boundaries + */ + for (i = 0; i < bdev_io->u.zone_mgmt.num_zones; i++, zone_id += bdev_node->bdev.zone_size) { + zone = zone_block_get_zone_by_slba(bdev_node, zone_id); + if (!zone) { + return -EINVAL; + } + memcpy(&zone_info[i], &zone->zone_info, sizeof(*zone_info)); + } + + spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS); + return 0; +} + static void zone_block_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io) { - spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED); + struct bdev_zone_block *bdev_node = SPDK_CONTAINEROF(bdev_io->bdev, struct bdev_zone_block, bdev); + int rc = 0; + + switch (bdev_io->type) { + case SPDK_BDEV_IO_TYPE_GET_ZONE_INFO: + rc = zone_block_get_zone_info(bdev_node, bdev_io); + break; + default: + SPDK_ERRLOG("vbdev_block: unknown I/O type %u\n", bdev_io->type); + rc = -ENOTSUP; + break; + } + + if (rc != 0) { + if (rc == -ENOMEM) { + SPDK_WARNLOG("ENOMEM, start to queue io for vbdev.\n"); + spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM); + } else { + SPDK_ERRLOG("ERROR on bdev_io submission!\n"); + spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED); + } + } } static bool zone_block_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type) { - return false; + switch (io_type) { + case SPDK_BDEV_IO_TYPE_ZONE_MANAGEMENT: + return true; + default: + return false; + } } static struct spdk_io_channel * @@ -363,6 +431,8 @@ zone_block_register(struct spdk_bdev *base_bdev) SPDK_ERRLOG("invalid zone size\n"); goto roundup_failed; } + + bdev_node->zone_shift = spdk_u64log2(zone_size); bdev_node->num_zones = base_bdev->blockcnt / zone_size; /* Align num_zones to optimal_open_zones */ diff --git a/test/unit/lib/bdev/vbdev_zone_block.c/vbdev_zone_block_ut.c b/test/unit/lib/bdev/vbdev_zone_block.c/vbdev_zone_block_ut.c index 681e2e1db..909df23df 100644 --- a/test/unit/lib/bdev/vbdev_zone_block.c/vbdev_zone_block_ut.c +++ b/test/unit/lib/bdev/vbdev_zone_block.c/vbdev_zone_block_ut.c @@ -44,6 +44,9 @@ #define BLOCK_SIZE 4096 /* Globals */ +struct io_output *g_io_output = NULL; +uint32_t g_max_io_size; +uint32_t g_io_output_index; uint32_t g_io_comp_status; uint8_t g_rpc_err; uint8_t g_json_decode_obj_construct; @@ -51,6 +54,16 @@ static TAILQ_HEAD(, spdk_bdev) g_bdev_list = TAILQ_HEAD_INITIALIZER(g_bdev_list) void *g_rpc_req = NULL; static struct spdk_thread *g_thread; +struct io_output { + struct spdk_bdev_desc *desc; + struct spdk_io_channel *ch; + uint64_t offset_blocks; + uint64_t num_blocks; + spdk_bdev_io_completion_cb cb; + void *cb_arg; + enum spdk_bdev_io_type iotype; +}; + DEFINE_STUB_V(spdk_bdev_module_list_add, (struct spdk_bdev_module *bdev_module)); DEFINE_STUB_V(spdk_bdev_close, (struct spdk_bdev_desc *desc)); DEFINE_STUB(spdk_json_decode_string, int, (const struct spdk_json_val *val, void *out), 0); @@ -73,6 +86,21 @@ DEFINE_STUB_V(spdk_jsonrpc_end_result, (struct spdk_jsonrpc_request *request, DEFINE_STUB(spdk_bdev_get_io_channel, struct spdk_io_channel *, (struct spdk_bdev_desc *desc), (void *)1); +static void +init_test_globals(void) +{ + g_io_output = calloc(g_max_io_size, sizeof(struct io_output)); + SPDK_CU_ASSERT_FATAL(g_io_output != NULL); + g_io_output_index = 0; +} + +static void +free_test_globals(void) +{ + free(g_io_output); + g_io_output = NULL; +} + int spdk_bdev_open(struct spdk_bdev *bdev, bool write, spdk_bdev_remove_cb_t remove_cb, void *remove_ctx, struct spdk_bdev_desc **_desc) @@ -465,6 +493,7 @@ test_cleanup(void) CU_ASSERT(spdk_thread_is_idle(g_thread)); zone_block_finish(); base_bdevs_cleanup(); + free_test_globals(); } static void @@ -535,6 +564,188 @@ test_zone_block_create_invalid(void) test_cleanup(); } +static void +bdev_io_zone_info_initialize(struct spdk_bdev_io *bdev_io, struct spdk_bdev *bdev, + uint64_t zone_id, uint32_t num_zones) +{ + bdev_io->bdev = bdev; + bdev_io->type = SPDK_BDEV_IO_TYPE_GET_ZONE_INFO; + + bdev_io->u.zone_mgmt.zone_id = zone_id; + + bdev_io->u.zone_mgmt.num_zones = num_zones; + if (num_zones) { + bdev_io->u.zone_mgmt.buf = calloc(num_zones, sizeof(struct spdk_bdev_zone_info)); + SPDK_CU_ASSERT_FATAL(bdev_io->u.zone_mgmt.buf != NULL); + } +} + +static void +bdev_io_zone_cleanup(struct spdk_bdev_io *bdev_io) +{ + free(bdev_io->u.zone_mgmt.buf); + free(bdev_io); +} + +static struct bdev_zone_block * +create_and_get_vbdev(char *vdev_name, char *name, uint64_t num_zones, uint64_t optimal_open_zones, + bool create_bdev) +{ + size_t zone_size = BLOCK_CNT / num_zones; + struct bdev_zone_block *bdev = NULL; + + send_create_vbdev(vdev_name, name, zone_size, optimal_open_zones, create_bdev, true); + + TAILQ_FOREACH(bdev, &g_bdev_nodes, link) { + if (strcmp(bdev->bdev.name, vdev_name) == 0) { + break; + } + } + + SPDK_CU_ASSERT_FATAL(bdev != NULL); + return bdev; +} + +static void +test_supported_io_types(void) +{ + struct bdev_zone_block *bdev; + char *name = "Nvme0n1"; + uint32_t num_zones = 8; + + init_test_globals(); + CU_ASSERT(zone_block_init() == 0); + + /* Create zone dev */ + bdev = create_and_get_vbdev("zone_dev1", name, num_zones, 1, true); + + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_ZONE_MANAGEMENT) == true); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_ZONE_APPEND) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_READ) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_WRITE) == false); + + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_NVME_ADMIN) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_NVME_IO) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_NVME_IO_MD) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_FLUSH) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_RESET) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_WRITE_ZEROES) == false); + CU_ASSERT(zone_block_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_ZCOPY) == false); + + send_delete_vbdev("zone_dev1", true); + while (spdk_thread_poll(g_thread, 0, 0) > 0) {} + test_cleanup(); +} + +static void +send_zone_info(struct bdev_zone_block *bdev, struct spdk_io_channel *ch, uint64_t zone_id, + uint64_t wp, + enum spdk_bdev_zone_state state, uint32_t output_index, bool success) +{ + struct spdk_bdev_io *bdev_io; + struct spdk_bdev_zone_info *info; + + bdev_io = calloc(1, sizeof(struct spdk_bdev_io)); + SPDK_CU_ASSERT_FATAL(bdev_io != NULL); + bdev_io_zone_info_initialize(bdev_io, &bdev->bdev, zone_id, 1); + memset(g_io_output, 0, (g_max_io_size * sizeof(struct io_output))); + g_io_output_index = output_index; + + g_io_comp_status = !success; + zone_block_submit_request(ch, bdev_io); + CU_ASSERT(g_io_comp_status == success); + + if (success) { + info = (struct spdk_bdev_zone_info *)bdev_io->u.zone_mgmt.buf; + CU_ASSERT(info->zone_id == zone_id); + CU_ASSERT(info->capacity == bdev->zone_capacity); + CU_ASSERT(info->write_pointer == wp); + CU_ASSERT(info->state == state); + } + + bdev_io_zone_cleanup(bdev_io); +} + +static void +test_get_zone_info(void) +{ + struct spdk_io_channel *ch; + struct bdev_zone_block *bdev; + struct spdk_bdev_io *bdev_io; + char *name = "Nvme0n1"; + uint32_t num_zones = 8, i; + struct spdk_bdev_zone_info *info; + + init_test_globals(); + CU_ASSERT(zone_block_init() == 0); + + /* Create zone dev */ + bdev = create_and_get_vbdev("zone_dev1", name, num_zones, 1, true); + + ch = calloc(1, sizeof(struct spdk_io_channel) + sizeof(struct zone_block_io_channel)); + SPDK_CU_ASSERT_FATAL(ch != NULL); + + /* Get info about each zone */ + for (i = 0; i < num_zones; i++) { + send_zone_info(bdev, ch, i * bdev->bdev.zone_size, + i * bdev->bdev.zone_size + bdev->zone_capacity, SPDK_BDEV_ZONE_STATE_FULL, 0, true); + } + + /* Send info asking for 0 zones */ + bdev_io = calloc(1, sizeof(struct spdk_bdev_io)); + SPDK_CU_ASSERT_FATAL(bdev_io != NULL); + bdev_io_zone_info_initialize(bdev_io, &bdev->bdev, 0, 0); + memset(g_io_output, 0, (g_max_io_size * sizeof(struct io_output))); + g_io_output_index = 0; + zone_block_submit_request(ch, bdev_io); + CU_ASSERT(g_io_comp_status); + bdev_io_zone_cleanup(bdev_io); + + /* Send info asking for all zones */ + bdev_io = calloc(1, sizeof(struct spdk_bdev_io)); + SPDK_CU_ASSERT_FATAL(bdev_io != NULL); + bdev_io_zone_info_initialize(bdev_io, &bdev->bdev, 0, num_zones); + memset(g_io_output, 0, (g_max_io_size * sizeof(struct io_output))); + g_io_output_index = 0; + zone_block_submit_request(ch, bdev_io); + CU_ASSERT(g_io_comp_status); + + for (i = 0; i < num_zones; i++) { + info = &(((struct spdk_bdev_zone_info *)bdev_io->u.zone_mgmt.buf)[i]); + CU_ASSERT(info->zone_id == i * bdev->bdev.zone_size); + CU_ASSERT(info->capacity == bdev->zone_capacity); + CU_ASSERT(info->write_pointer == i * bdev->bdev.zone_size + bdev->zone_capacity); + CU_ASSERT(info->state == SPDK_BDEV_ZONE_STATE_FULL); + } + bdev_io_zone_cleanup(bdev_io); + + /* Send info asking for too many zones */ + bdev_io = calloc(1, sizeof(struct spdk_bdev_io)); + SPDK_CU_ASSERT_FATAL(bdev_io != NULL); + bdev_io_zone_info_initialize(bdev_io, &bdev->bdev, 0, num_zones + 1); + memset(g_io_output, 0, (g_max_io_size * sizeof(struct io_output))); + g_io_output_index = 0; + zone_block_submit_request(ch, bdev_io); + CU_ASSERT(!g_io_comp_status); + bdev_io_zone_cleanup(bdev_io); + + /* Send info with misaligned start LBA */ + send_zone_info(bdev, ch, 1, 0, SPDK_BDEV_ZONE_STATE_FULL, 0, false); + + /* Send info with too high LBA */ + send_zone_info(bdev, ch, num_zones * bdev->bdev.zone_size, 0, SPDK_BDEV_ZONE_STATE_FULL, 0, + false); + + /* Delete zone dev */ + send_delete_vbdev("zone_dev1", true); + + while (spdk_thread_poll(g_thread, 0, 0) > 0) {} + free(ch); + + test_cleanup(); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -552,7 +763,9 @@ int main(int argc, char **argv) if ( CU_add_test(suite, "test_zone_block_create", test_zone_block_create) == NULL || - CU_add_test(suite, "test_zone_block_create_invalid", test_zone_block_create_invalid) == NULL + CU_add_test(suite, "test_zone_block_create_invalid", test_zone_block_create_invalid) == NULL || + CU_add_test(suite, "test_get_zone_info", test_get_zone_info) == NULL || + CU_add_test(suite, "test_supported_io_types", test_supported_io_types) == NULL ) { CU_cleanup_registry(); return CU_get_error();