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:
parent
3ba5dd2852
commit
c3e3e81a50
@ -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 */
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user