bdev/zone: Get zone info

Added handling of get zone info command

Signed-off-by: Mateusz Kozlowski <mateusz.kozlowski@intel.com>
Change-Id: I2d80885af83345c945af22a46a41abf55e1eb413
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/468036
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Broadcom SPDK FC-NVMe CI <spdk-ci.pdl@broadcom.com>
Community-CI: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
This commit is contained in:
Mateusz Kozlowski 2019-09-13 15:43:09 +02:00 committed by Tomasz Zawadzki
parent 3ba5dd2852
commit c3e3e81a50
2 changed files with 286 additions and 3 deletions

View File

@ -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 */

View File

@ -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();