Commit c3e3e81a authored by Mateusz Kozlowski's avatar Mateusz Kozlowski Committed by Tomasz Zawadzki
Browse files

bdev/zone: Get zone info



Added handling of get zone info command

Signed-off-by: default avatarMateusz Kozlowski <mateusz.kozlowski@intel.com>
Change-Id: I2d80885af83345c945af22a46a41abf55e1eb413
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/468036


Tested-by: default avatarSPDK 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: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
parent 3ba5dd28
Loading
Loading
Loading
Loading
+72 −2
Original line number Diff line number Diff line
@@ -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,17 +179,84 @@ 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)
{
	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)
{
	switch (io_type) {
	case SPDK_BDEV_IO_TYPE_ZONE_MANAGEMENT:
		return true;
	default:
		return false;
	}
}

static struct spdk_io_channel *
zone_block_get_io_channel(void *ctx)
@@ -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 */
+214 −1
Original line number Diff line number Diff line
@@ -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();