bdev/zone: Register/unregister zoned bdev

Add registration and unregistration of block zoned bdev. Attach it to the
underlying bdev during creation and unattach at deletion.

Signed-off-by: Mateusz Kozlowski <mateusz.kozlowski@intel.com>
Change-Id: I773aff6c7609952f28c02dd1794f0529a781b2e1
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/468033
Community-CI: Broadcom SPDK FC-NVMe CI <spdk-ci.pdl@broadcom.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Mateusz Kozlowski 2019-08-21 10:38:53 +02:00 committed by Tomasz Zawadzki
parent 76be5ff6f0
commit cabbe1b179
7 changed files with 1021 additions and 16 deletions

View File

@ -112,7 +112,6 @@ DEPDIRS-bdev_gpt := bdev conf json log thread util
DEPDIRS-bdev_lvol := $(BDEV_DEPS) lvol blob blob_bdev
DEPDIRS-bdev_rpc := $(BDEV_DEPS)
DEPDIRS-bdev_zone_block := $(BDEV_DEPS)
DEPDIRS-bdev_error := $(BDEV_DEPS_CONF)
DEPDIRS-bdev_malloc := $(BDEV_DEPS_CONF) copy
@ -120,6 +119,7 @@ DEPDIRS-bdev_split := $(BDEV_DEPS_CONF)
DEPDIRS-bdev_compress := $(BDEV_DEPS_THREAD) reduce
DEPDIRS-bdev_delay := $(BDEV_DEPS_THREAD)
DEPDIRS-bdev_zone_block := $(BDEV_DEPS_THREAD)
DEPDIRS-bdev_aio := $(BDEV_DEPS_CONF_THREAD)
DEPDIRS-bdev_crypto := $(BDEV_DEPS_CONF_THREAD)

View File

@ -40,6 +40,374 @@
#include "spdk_internal/log.h"
static int zone_block_init(void);
static int zone_block_get_ctx_size(void);
static void zone_block_finish(void);
static int zone_block_config_json(struct spdk_json_write_ctx *w);
static void zone_block_examine(struct spdk_bdev *bdev);
static struct spdk_bdev_module bdev_zoned_if = {
.name = "bdev_zoned_block",
.module_init = zone_block_init,
.module_fini = zone_block_finish,
.config_text = NULL,
.config_json = zone_block_config_json,
.examine_config = zone_block_examine,
.get_ctx_size = zone_block_get_ctx_size,
};
SPDK_BDEV_MODULE_REGISTER(bdev_zoned_block, &bdev_zoned_if)
/* List of block vbdev names and their base bdevs via configuration file.
* Used so we can parse the conf once at init and use this list in examine().
*/
struct bdev_zone_block_config {
char *vbdev_name;
char *bdev_name;
uint64_t zone_capacity;
uint64_t optimal_open_zones;
TAILQ_ENTRY(bdev_zone_block_config) link;
};
static TAILQ_HEAD(, bdev_zone_block_config) g_bdev_configs = TAILQ_HEAD_INITIALIZER(g_bdev_configs);
/* List of block vbdevs and associated info for each. */
struct bdev_zone_block {
struct spdk_bdev bdev; /* the block zoned bdev */
struct spdk_bdev_desc *base_desc; /* its descriptor we get from open */
uint64_t zone_capacity; /* zone capacity */
TAILQ_ENTRY(bdev_zone_block) link;
};
static TAILQ_HEAD(, bdev_zone_block) g_bdev_nodes = TAILQ_HEAD_INITIALIZER(g_bdev_nodes);
struct zone_block_io_channel {
struct spdk_io_channel *base_ch; /* IO channel of base device */
};
struct zone_block_io {
/* bdev IO was issued to */
struct bdev_zone_block *bdev_zone_block;
};
static int
zone_block_init(void)
{
return 0;
}
static void
zone_block_remove_config(struct bdev_zone_block_config *name)
{
TAILQ_REMOVE(&g_bdev_configs, name, link);
free(name->bdev_name);
free(name->vbdev_name);
free(name);
}
static void
zone_block_finish(void)
{
struct bdev_zone_block_config *name;
while ((name = TAILQ_FIRST(&g_bdev_configs))) {
zone_block_remove_config(name);
}
}
static int
zone_block_get_ctx_size(void)
{
return sizeof(struct zone_block_io);
}
static int
zone_block_config_json(struct spdk_json_write_ctx *w)
{
struct bdev_zone_block *bdev_node;
struct spdk_bdev *base_bdev = NULL;
TAILQ_FOREACH(bdev_node, &g_bdev_nodes, link) {
base_bdev = spdk_bdev_desc_get_bdev(bdev_node->base_desc);
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "method", "bdev_zone_block_create");
spdk_json_write_named_object_begin(w, "params");
spdk_json_write_named_string(w, "base_bdev", spdk_bdev_get_name(base_bdev));
spdk_json_write_named_string(w, "name", spdk_bdev_get_name(&bdev_node->bdev));
spdk_json_write_named_uint64(w, "zone_capacity", bdev_node->zone_capacity);
spdk_json_write_named_uint64(w, "optimal_open_zones", bdev_node->bdev.optimal_open_zones);
spdk_json_write_object_end(w);
spdk_json_write_object_end(w);
}
return 0;
}
/* Callback for unregistering the IO device. */
static void
_device_unregister_cb(void *io_device)
{
struct bdev_zone_block *bdev_node = io_device;
free(bdev_node->bdev.name);
free(bdev_node);
}
static int
zone_block_destruct(void *ctx)
{
struct bdev_zone_block *bdev_node = (struct bdev_zone_block *)ctx;
TAILQ_REMOVE(&g_bdev_nodes, bdev_node, link);
/* Unclaim the underlying bdev. */
spdk_bdev_module_release_bdev(spdk_bdev_desc_get_bdev(bdev_node->base_desc));
/* Close the underlying bdev. */
spdk_bdev_close(bdev_node->base_desc);
/* Unregister the io_device. */
spdk_io_device_unregister(bdev_node, _device_unregister_cb);
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);
}
static bool
zone_block_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
{
return false;
}
static struct spdk_io_channel *
zone_block_get_io_channel(void *ctx)
{
struct bdev_zone_block *bdev_node = (struct bdev_zone_block *)ctx;
return spdk_get_io_channel(bdev_node);
}
static int
zone_block_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
{
struct bdev_zone_block *bdev_node = (struct bdev_zone_block *)ctx;
struct spdk_bdev *base_bdev = spdk_bdev_desc_get_bdev(bdev_node->base_desc);
spdk_json_write_name(w, "zoned_block");
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "name", spdk_bdev_get_name(&bdev_node->bdev));
spdk_json_write_named_string(w, "base_bdev", spdk_bdev_get_name(base_bdev));
spdk_json_write_named_uint64(w, "zone_capacity", bdev_node->zone_capacity);
spdk_json_write_named_uint64(w, "optimal_open_zones", bdev_node->bdev.optimal_open_zones);
spdk_json_write_object_end(w);
return 0;
}
/* When we register our vbdev this is how we specify our entry points. */
static const struct spdk_bdev_fn_table zone_block_fn_table = {
.destruct = zone_block_destruct,
.submit_request = zone_block_submit_request,
.io_type_supported = zone_block_io_type_supported,
.get_io_channel = zone_block_get_io_channel,
.dump_info_json = zone_block_dump_info_json,
};
static void
zone_block_base_bdev_hotremove_cb(void *ctx)
{
struct bdev_zone_block *bdev_node, *tmp;
struct spdk_bdev *bdev_find = ctx;
TAILQ_FOREACH_SAFE(bdev_node, &g_bdev_nodes, link, tmp) {
if (bdev_find == spdk_bdev_desc_get_bdev(bdev_node->base_desc)) {
spdk_bdev_unregister(&bdev_node->bdev, NULL, NULL);
}
}
}
static int
_zone_block_ch_create_cb(void *io_device, void *ctx_buf)
{
struct zone_block_io_channel *bdev_ch = ctx_buf;
struct bdev_zone_block *bdev_node = io_device;
bdev_ch->base_ch = spdk_bdev_get_io_channel(bdev_node->base_desc);
if (!bdev_ch->base_ch) {
return -ENOMEM;
}
return 0;
}
static void
_zone_block_ch_destroy_cb(void *io_device, void *ctx_buf)
{
struct zone_block_io_channel *bdev_ch = ctx_buf;
spdk_put_io_channel(bdev_ch->base_ch);
}
static int
zone_block_insert_name(const char *bdev_name, const char *vbdev_name, uint64_t zone_capacity,
uint64_t optimal_open_zones)
{
struct bdev_zone_block_config *name;
TAILQ_FOREACH(name, &g_bdev_configs, link) {
if (strcmp(vbdev_name, name->vbdev_name) == 0) {
SPDK_ERRLOG("block zoned bdev %s already exists\n", vbdev_name);
return -EEXIST;
}
if (strcmp(bdev_name, name->bdev_name) == 0) {
SPDK_ERRLOG("base bdev %s already claimed\n", bdev_name);
return -EEXIST;
}
}
name = calloc(1, sizeof(*name));
if (!name) {
SPDK_ERRLOG("could not allocate bdev_names\n");
return -ENOMEM;
}
name->bdev_name = strdup(bdev_name);
if (!name->bdev_name) {
SPDK_ERRLOG("could not allocate name->bdev_name\n");
free(name);
return -ENOMEM;
}
name->vbdev_name = strdup(vbdev_name);
if (!name->vbdev_name) {
SPDK_ERRLOG("could not allocate name->vbdev_name\n");
free(name->bdev_name);
free(name);
return -ENOMEM;
}
name->zone_capacity = zone_capacity;
name->optimal_open_zones = optimal_open_zones;
TAILQ_INSERT_TAIL(&g_bdev_configs, name, link);
return 0;
}
static int
zone_block_register(struct spdk_bdev *base_bdev)
{
struct bdev_zone_block_config *name, *tmp;
struct bdev_zone_block *bdev_node;
int rc = 0;
/* Check our list of names from config versus this bdev and if
* there's a match, create the bdev_node & bdev accordingly.
*/
TAILQ_FOREACH_SAFE(name, &g_bdev_configs, link, tmp) {
if (strcmp(name->bdev_name, base_bdev->name) != 0) {
continue;
}
if (spdk_bdev_is_zoned(base_bdev)) {
SPDK_ERRLOG("Base bdev %s is already a zoned bdev\n", base_bdev->name);
rc = -EEXIST;
goto free_config;
}
bdev_node = calloc(1, sizeof(struct bdev_zone_block));
if (!bdev_node) {
rc = -ENOMEM;
SPDK_ERRLOG("could not allocate bdev_node\n");
goto free_config;
}
/* The base bdev that we're attaching to. */
bdev_node->bdev.name = strdup(name->vbdev_name);
if (!bdev_node->bdev.name) {
rc = -ENOMEM;
SPDK_ERRLOG("could not allocate bdev_node name\n");
goto strdup_failed;
}
bdev_node->bdev.product_name = "zone_block";
/* Copy some properties from the underlying base bdev. */
bdev_node->bdev.write_cache = base_bdev->write_cache;
bdev_node->bdev.required_alignment = base_bdev->required_alignment;
bdev_node->bdev.optimal_io_boundary = base_bdev->optimal_io_boundary;
bdev_node->bdev.blocklen = base_bdev->blocklen;
bdev_node->bdev.blockcnt = base_bdev->blockcnt;
bdev_node->bdev.write_unit_size = base_bdev->write_unit_size;
bdev_node->bdev.md_interleave = base_bdev->md_interleave;
bdev_node->bdev.md_len = base_bdev->md_len;
bdev_node->bdev.dif_type = base_bdev->dif_type;
bdev_node->bdev.dif_is_head_of_md = base_bdev->dif_is_head_of_md;
bdev_node->bdev.dif_check_flags = base_bdev->dif_check_flags;
bdev_node->bdev.zoned = true;
bdev_node->bdev.ctxt = bdev_node;
bdev_node->bdev.fn_table = &zone_block_fn_table;
bdev_node->bdev.module = &bdev_zoned_if;
/* bdev specific info */
bdev_node->bdev.zone_size = spdk_align64pow2(name->zone_capacity);
if (bdev_node->bdev.zone_size == 0) {
rc = -EINVAL;
SPDK_ERRLOG("invalid zone size\n");
goto roundup_failed;
}
bdev_node->zone_capacity = name->zone_capacity;
bdev_node->bdev.optimal_open_zones = name->optimal_open_zones;
bdev_node->bdev.max_open_zones = 0;
TAILQ_INSERT_TAIL(&g_bdev_nodes, bdev_node, link);
spdk_io_device_register(bdev_node, _zone_block_ch_create_cb, _zone_block_ch_destroy_cb,
sizeof(struct zone_block_io_channel),
name->vbdev_name);
rc = spdk_bdev_open(base_bdev, true, zone_block_base_bdev_hotremove_cb,
base_bdev, &bdev_node->base_desc);
if (rc) {
SPDK_ERRLOG("could not open bdev %s\n", spdk_bdev_get_name(base_bdev));
goto open_failed;
}
rc = spdk_bdev_module_claim_bdev(base_bdev, bdev_node->base_desc, bdev_node->bdev.module);
if (rc) {
SPDK_ERRLOG("could not claim bdev %s\n", spdk_bdev_get_name(base_bdev));
goto claim_failed;
}
rc = spdk_bdev_register(&bdev_node->bdev);
if (rc) {
SPDK_ERRLOG("could not register zoned bdev\n");
goto register_failed;
}
}
return rc;
register_failed:
spdk_bdev_module_release_bdev(&bdev_node->bdev);
claim_failed:
spdk_bdev_close(bdev_node->base_desc);
open_failed:
TAILQ_REMOVE(&g_bdev_nodes, bdev_node, link);
spdk_io_device_unregister(bdev_node, NULL);
roundup_failed:
free(bdev_node->bdev.name);
strdup_failed:
free(bdev_node);
free_config:
zone_block_remove_config(name);
return rc;
}
int
spdk_vbdev_zone_block_create(const char *bdev_name, const char *vbdev_name, uint64_t zone_capacity,
uint64_t optimal_open_zones)
@ -47,18 +415,6 @@ spdk_vbdev_zone_block_create(const char *bdev_name, const char *vbdev_name, uint
struct spdk_bdev *bdev = NULL;
int rc = 0;
bdev = spdk_bdev_get_by_name(bdev_name);
if (!bdev) {
SPDK_ERRLOG("Base bdev (%s) doesn't exist\n", bdev_name);
return -ENODEV;
}
if (spdk_bdev_is_zoned(bdev)) {
SPDK_ERRLOG("Base bdev %s is already a zoned bdev\n", bdev_name);
return -EEXIST;
}
if (zone_capacity == 0) {
SPDK_ERRLOG("Zone capacity can't be 0\n");
return -EINVAL;
@ -69,13 +425,53 @@ spdk_vbdev_zone_block_create(const char *bdev_name, const char *vbdev_name, uint
return -EINVAL;
}
return rc;
/* Insert the bdev into our global name list even if it doesn't exist yet,
* it may show up soon...
*/
rc = zone_block_insert_name(bdev_name, vbdev_name, zone_capacity, optimal_open_zones);
if (rc) {
return rc;
}
bdev = spdk_bdev_get_by_name(bdev_name);
if (!bdev) {
/* This is not an error, even though the bdev is not present at this time it may
* still show up later.
*/
return 0;
}
return zone_block_register(bdev);
}
void
spdk_vbdev_zone_block_delete(const char *name, spdk_bdev_unregister_cb cb_fn, void *cb_arg)
{
cb_fn(cb_arg, 0);
struct bdev_zone_block_config *name_node;
struct spdk_bdev *bdev = NULL;
bdev = spdk_bdev_get_by_name(name);
if (!bdev || bdev->module != &bdev_zoned_if) {
cb_fn(cb_arg, -ENODEV);
return;
}
TAILQ_FOREACH(name_node, &g_bdev_configs, link) {
if (strcmp(name_node->vbdev_name, bdev->name) == 0) {
zone_block_remove_config(name_node);
break;
}
}
spdk_bdev_unregister(bdev, cb_fn, cb_arg);
}
static void
zone_block_examine(struct spdk_bdev *bdev)
{
zone_block_register(bdev);
spdk_bdev_module_examine_done(&bdev_zoned_if);
}
SPDK_LOG_REGISTER_COMPONENT("vbdev_zone_block", SPDK_LOG_VBDEV_ZONE_BLOCK)

View File

@ -34,7 +34,7 @@
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
DIRS-y = bdev.c part.c scsi_nvme.c gpt vbdev_lvol.c mt bdev_raid.c bdev_zone.c
DIRS-y = bdev.c part.c scsi_nvme.c gpt vbdev_lvol.c mt bdev_raid.c bdev_zone.c vbdev_zone_block.c
DIRS-$(CONFIG_CRYPTO) += crypto.c

View File

@ -0,0 +1 @@
vbdev_zone_block_ut

View File

@ -0,0 +1,38 @@
#
# BSD LICENSE
#
# Copyright (c) Intel Corporation.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Intel Corporation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..)
TEST_FILE = vbdev_zone_block_ut.c
include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk

View File

@ -0,0 +1,569 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spdk/stdinc.h"
#include "spdk_cunit.h"
#include "spdk/env.h"
#include "spdk_internal/mock.h"
#include "spdk/thread.h"
#include "common/lib/test_env.c"
#include "bdev/zone_block/vbdev_zone_block.c"
#include "bdev/zone_block/vbdev_zone_block_rpc.c"
#define BLOCK_CNT (1024ul * 1024ul * 1024ul * 1024ul)
#define BLOCK_SIZE 4096
/* Globals */
uint32_t g_io_comp_status;
uint8_t g_rpc_err;
uint8_t g_json_decode_obj_construct;
static TAILQ_HEAD(, spdk_bdev) g_bdev_list = TAILQ_HEAD_INITIALIZER(g_bdev_list);
void *g_rpc_req = NULL;
uint32_t g_rpc_req_size = 0;
static struct spdk_thread *g_thread;
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);
DEFINE_STUB(spdk_json_decode_uint64, int, (const struct spdk_json_val *val, void *out), 0);
DEFINE_STUB_V(spdk_bdev_module_examine_done, (struct spdk_bdev_module *module));
DEFINE_STUB(spdk_json_write_name, int, (struct spdk_json_write_ctx *w, const char *name), 0);
DEFINE_STUB(spdk_json_write_object_begin, int, (struct spdk_json_write_ctx *w), 0);
DEFINE_STUB(spdk_json_write_named_string, int, (struct spdk_json_write_ctx *w,
const char *name, const char *val), 0);
DEFINE_STUB(spdk_bdev_io_type_supported, bool, (struct spdk_bdev *bdev,
enum spdk_bdev_io_type io_type), true);
DEFINE_STUB(spdk_json_write_bool, int, (struct spdk_json_write_ctx *w, bool val), 0);
DEFINE_STUB(spdk_json_write_named_object_begin, int, (struct spdk_json_write_ctx *w,
const char *name), 0);
DEFINE_STUB(spdk_json_write_object_end, int, (struct spdk_json_write_ctx *w), 0);
DEFINE_STUB_V(spdk_rpc_register_method, (const char *method, spdk_rpc_method_handler func,
uint32_t state_mask));
DEFINE_STUB_V(spdk_jsonrpc_end_result, (struct spdk_jsonrpc_request *request,
struct spdk_json_write_ctx *w));
DEFINE_STUB(spdk_bdev_get_io_channel, struct spdk_io_channel *, (struct spdk_bdev_desc *desc),
(void *)1);
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)
{
*_desc = (void *)bdev;
return 0;
}
struct spdk_bdev *
spdk_bdev_desc_get_bdev(struct spdk_bdev_desc *desc)
{
return (void *)desc;
}
int
spdk_bdev_register(struct spdk_bdev *bdev)
{
CU_ASSERT_PTR_NULL(spdk_bdev_get_by_name(bdev->name));
TAILQ_INSERT_TAIL(&g_bdev_list, bdev, internal.link);
return 0;
}
void
spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn, void *cb_arg)
{
CU_ASSERT_EQUAL(spdk_bdev_get_by_name(bdev->name), bdev);
TAILQ_REMOVE(&g_bdev_list, bdev, internal.link);
bdev->fn_table->destruct(bdev->ctxt);
if (cb_fn) {
cb_fn(cb_arg, 0);
}
}
int spdk_json_write_named_uint64(struct spdk_json_write_ctx *w, const char *name, uint64_t val)
{
struct rpc_construct_zone_block *req = g_rpc_req;
if (strcmp(name, "zone_capacity") == 0) {
CU_ASSERT(req->zone_capacity == val);
} else if (strcmp(name, "optimal_open_zones") == 0) {
CU_ASSERT(req->optimal_open_zones == val);
}
return 0;
}
const char *
spdk_bdev_get_name(const struct spdk_bdev *bdev)
{
return bdev->name;
}
bool
spdk_bdev_is_zoned(const struct spdk_bdev *bdev)
{
return bdev->zoned;
}
int
spdk_json_write_string(struct spdk_json_write_ctx *w, const char *val)
{
return 0;
}
int
spdk_bdev_module_claim_bdev(struct spdk_bdev *bdev, struct spdk_bdev_desc *desc,
struct spdk_bdev_module *module)
{
if (bdev->internal.claim_module != NULL) {
return -1;
}
bdev->internal.claim_module = module;
return 0;
}
void
spdk_bdev_module_release_bdev(struct spdk_bdev *bdev)
{
CU_ASSERT(bdev->internal.claim_module != NULL);
bdev->internal.claim_module = NULL;
}
void
spdk_bdev_io_complete(struct spdk_bdev_io *bdev_io, enum spdk_bdev_io_status status)
{
g_io_comp_status = ((status == SPDK_BDEV_IO_STATUS_SUCCESS) ? true : false);
}
int
spdk_json_decode_object(const struct spdk_json_val *values,
const struct spdk_json_object_decoder *decoders, size_t num_decoders,
void *out)
{
struct rpc_construct_zone_block *req, *_out;
if (g_json_decode_obj_construct) {
req = g_rpc_req;
_out = out;
_out->name = strdup(req->name);
SPDK_CU_ASSERT_FATAL(_out->name != NULL);
_out->base_bdev = strdup(req->base_bdev);
SPDK_CU_ASSERT_FATAL(_out->base_bdev != NULL);
_out->zone_capacity = req->zone_capacity;
_out->optimal_open_zones = req->optimal_open_zones;
} else {
memcpy(out, g_rpc_req, g_rpc_req_size);
}
return 0;
}
struct spdk_json_write_ctx *
spdk_jsonrpc_begin_result(struct spdk_jsonrpc_request *request)
{
return (void *)1;
}
static struct spdk_bdev *
create_nvme_bdev(void)
{
struct spdk_bdev *base_bdev;
char *name = "Nvme0n1";
base_bdev = calloc(1, sizeof(struct spdk_bdev));
SPDK_CU_ASSERT_FATAL(base_bdev != NULL);
base_bdev->name = strdup(name);
SPDK_CU_ASSERT_FATAL(base_bdev->name != NULL);
base_bdev->blocklen = BLOCK_SIZE;
base_bdev->blockcnt = BLOCK_CNT;
base_bdev->write_unit_size = 1;
TAILQ_INSERT_TAIL(&g_bdev_list, base_bdev, internal.link);
return base_bdev;
}
static void
base_bdevs_cleanup(void)
{
struct spdk_bdev *bdev;
struct spdk_bdev *bdev_next;
if (!TAILQ_EMPTY(&g_bdev_list)) {
TAILQ_FOREACH_SAFE(bdev, &g_bdev_list, internal.link, bdev_next) {
free(bdev->name);
TAILQ_REMOVE(&g_bdev_list, bdev, internal.link);
free(bdev);
}
}
}
struct spdk_bdev *
spdk_bdev_get_by_name(const char *bdev_name)
{
struct spdk_bdev *bdev;
if (!TAILQ_EMPTY(&g_bdev_list)) {
TAILQ_FOREACH(bdev, &g_bdev_list, internal.link) {
if (strcmp(bdev_name, bdev->name) == 0) {
return bdev;
}
}
}
return NULL;
}
void
spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_request *request,
int error_code, const char *msg)
{
g_rpc_err = 1;
}
void
spdk_jsonrpc_send_error_response_fmt(struct spdk_jsonrpc_request *request,
int error_code, const char *fmt, ...)
{
g_rpc_err = 1;
}
static void
verify_config_present(const char *name, bool presence)
{
struct bdev_zone_block_config *cfg;
bool cfg_found;
cfg_found = false;
TAILQ_FOREACH(cfg, &g_bdev_configs, link) {
if (cfg->vbdev_name != NULL) {
if (strcmp(name, cfg->vbdev_name) == 0) {
cfg_found = true;
break;
}
}
}
if (presence == true) {
CU_ASSERT(cfg_found == true);
} else {
CU_ASSERT(cfg_found == false);
}
}
static void
verify_bdev_present(const char *name, bool presence)
{
struct bdev_zone_block *bdev;
bool bdev_found;
bdev_found = false;
TAILQ_FOREACH(bdev, &g_bdev_nodes, link) {
if (strcmp(bdev->bdev.name, name) == 0) {
bdev_found = true;
break;
}
}
if (presence == true) {
CU_ASSERT(bdev_found == true);
} else {
CU_ASSERT(bdev_found == false);
}
}
static void
create_test_req(struct rpc_construct_zone_block *r, const char *vbdev_name, const char *base_name,
uint64_t zone_capacity, uint64_t optimal_open_zones, bool create_base_bdev)
{
r->name = strdup(vbdev_name);
SPDK_CU_ASSERT_FATAL(r->name != NULL);
r->base_bdev = strdup(base_name);
SPDK_CU_ASSERT_FATAL(r->base_bdev != NULL);
r->zone_capacity = zone_capacity;
r->optimal_open_zones = optimal_open_zones;
if (create_base_bdev == true) {
create_nvme_bdev();
}
g_rpc_req = r;
g_rpc_req_size = sizeof(*r);
}
static void
free_test_req(struct rpc_construct_zone_block *r)
{
free(r->name);
free(r->base_bdev);
}
static void
initialize_create_req(struct rpc_construct_zone_block *r, const char *vbdev_name,
const char *base_name,
uint64_t zone_capacity, uint64_t optimal_open_zones, bool create_base_bdev)
{
create_test_req(r, vbdev_name, base_name, zone_capacity, optimal_open_zones, create_base_bdev);
g_rpc_err = 0;
g_json_decode_obj_construct = 1;
}
static void
create_delete_req(struct rpc_delete_zone_block *r, const char *vbdev_name)
{
r->name = strdup(vbdev_name);
SPDK_CU_ASSERT_FATAL(r->name != NULL);
g_rpc_req = r;
g_rpc_req_size = sizeof(*r);
g_rpc_err = 0;
g_json_decode_obj_construct = 0;
}
static void
verify_zone_config(struct rpc_construct_zone_block *r, bool presence)
{
struct bdev_zone_block_config *cfg = NULL;
TAILQ_FOREACH(cfg, &g_bdev_configs, link) {
if (strcmp(r->name, cfg->vbdev_name) == 0) {
if (presence == false) {
break;
}
CU_ASSERT(strcmp(r->base_bdev, cfg->bdev_name) == 0);
CU_ASSERT(r->zone_capacity == cfg->zone_capacity);
CU_ASSERT(r->optimal_open_zones == cfg->optimal_open_zones);
break;
}
}
if (presence) {
CU_ASSERT(cfg != NULL);
} else {
CU_ASSERT(cfg == NULL);
}
}
static void
verify_zone_bdev(struct rpc_construct_zone_block *r, bool presence)
{
struct bdev_zone_block *bdev;
bool bdev_found;
bdev_found = false;
TAILQ_FOREACH(bdev, &g_bdev_nodes, link) {
if (strcmp(bdev->bdev.name, r->name) == 0) {
bdev_found = true;
if (presence == false) {
break;
}
CU_ASSERT(bdev->bdev.zoned == true);
CU_ASSERT(bdev->bdev.blockcnt == BLOCK_CNT);
CU_ASSERT(bdev->bdev.blocklen == BLOCK_SIZE);
CU_ASSERT(bdev->bdev.ctxt == bdev);
CU_ASSERT(bdev->bdev.fn_table == &zone_block_fn_table);
CU_ASSERT(bdev->bdev.module == &bdev_zoned_if);
CU_ASSERT(bdev->bdev.write_unit_size == 1);
CU_ASSERT(bdev->bdev.zone_size == spdk_align64pow2(r->zone_capacity));
CU_ASSERT(bdev->bdev.optimal_open_zones == r->optimal_open_zones);
CU_ASSERT(bdev->bdev.max_open_zones == 0);
break;
}
}
if (presence == true) {
CU_ASSERT(bdev_found == true);
} else {
CU_ASSERT(bdev_found == false);
}
}
static void
test_zone_block_create(void)
{
struct rpc_construct_zone_block req;
struct rpc_delete_zone_block delete_req;
struct spdk_bdev *bdev;
char *name = "Nvme0n1";
size_t num_zones = 20;
size_t zone_capacity = BLOCK_CNT / num_zones;
CU_ASSERT(zone_block_init() == 0);
/* Create zoned virtual device before nvme device */
verify_config_present("zone_dev1", false);
verify_bdev_present("zone_dev1", false);
initialize_create_req(&req, "zone_dev1", name, zone_capacity, 1, false);
rpc_zone_block_create(NULL, NULL);
CU_ASSERT(g_rpc_err == 0);
verify_zone_config(&req, true);
verify_zone_bdev(&req, false);
bdev = create_nvme_bdev();
zone_block_examine(bdev);
verify_zone_bdev(&req, true);
free_test_req(&req);
/* Delete bdev */
create_delete_req(&delete_req, "zone_dev1");
rpc_zone_block_delete(NULL, NULL);
verify_config_present("zone_dev1", false);
verify_bdev_present("zone_dev1", false);
CU_ASSERT(g_rpc_err == 0);
/* Create zoned virtual device and verify its correctness */
verify_config_present("zone_dev1", false);
verify_bdev_present("zone_dev1", false);
initialize_create_req(&req, "zone_dev1", name, zone_capacity, 1, false);
rpc_zone_block_create(NULL, NULL);
CU_ASSERT(g_rpc_err == 0);
verify_zone_config(&req, true);
verify_zone_bdev(&req, true);
free_test_req(&req);
/* Delete bdev */
create_delete_req(&delete_req, "zone_dev1");
rpc_zone_block_delete(NULL, NULL);
verify_config_present("zone_dev1", false);
verify_bdev_present("zone_dev1", false);
CU_ASSERT(g_rpc_err == 0);
while (spdk_thread_poll(g_thread, 0, 0) > 0) {}
zone_block_finish();
base_bdevs_cleanup();
SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_list));
}
static void
test_zone_block_create_invalid(void)
{
struct rpc_construct_zone_block req;
struct rpc_delete_zone_block delete_req;
char *name = "Nvme0n1";
size_t num_zones = 10;
size_t zone_capacity = BLOCK_CNT / num_zones;
CU_ASSERT(zone_block_init() == 0);
/* Create zoned virtual device and verify its correctness */
verify_config_present("zone_dev1", false);
verify_bdev_present("zone_dev1", false);
initialize_create_req(&req, "zone_dev1", name, zone_capacity, 1, true);
rpc_zone_block_create(NULL, NULL);
CU_ASSERT(g_rpc_err == 0);
verify_zone_config(&req, true);
verify_zone_bdev(&req, true);
free_test_req(&req);
/* Try to create another zoned virtual device on the same bdev */
initialize_create_req(&req, "zone_dev2", name, zone_capacity, 1, false);
rpc_zone_block_create(NULL, NULL);
CU_ASSERT(g_rpc_err == 1);
verify_config_present("zone_dev2", false);
verify_bdev_present("zone_dev2", false);
free_test_req(&req);
/* Try to create zoned virtual device on the zoned bdev */
initialize_create_req(&req, "zone_dev2", "zone_dev1", zone_capacity, 1, false);
rpc_zone_block_create(NULL, NULL);
CU_ASSERT(g_rpc_err == 1);
verify_config_present("zone_dev2", false);
verify_bdev_present("zone_dev2", false);
free_test_req(&req);
/* Unclaim the base bdev */
create_delete_req(&delete_req, "zone_dev1");
rpc_zone_block_delete(NULL, NULL);
verify_config_present("zone_dev1", false);
verify_bdev_present("zone_dev1", false);
CU_ASSERT(g_rpc_err == 0);
/* Try to create zoned virtual device with 0 zone size */
initialize_create_req(&req, "zone_dev2", name, 0, 1, false);
rpc_zone_block_create(NULL, NULL);
CU_ASSERT(g_rpc_err == 1);
verify_config_present("zone_dev2", false);
verify_bdev_present("zone_dev2", false);
free_test_req(&req);
/* Try to create zoned virtual device with 0 optimal number of zones */
initialize_create_req(&req, "zone_dev2", name, zone_capacity, 0, false);
rpc_zone_block_create(NULL, NULL);
verify_config_present("zone_dev2", false);
verify_bdev_present("zone_dev2", false);
CU_ASSERT(g_rpc_err == 1);
free_test_req(&req);
while (spdk_thread_poll(g_thread, 0, 0) > 0) {}
zone_block_finish();
base_bdevs_cleanup();
SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_list));
}
int main(int argc, char **argv)
{
CU_pSuite suite = NULL;
unsigned int num_failures;
if (CU_initialize_registry() != CUE_SUCCESS) {
return CU_get_error();
}
suite = CU_add_suite("zone_block", NULL, NULL);
if (suite == NULL) {
CU_cleanup_registry();
return CU_get_error();
}
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_cleanup_registry();
return CU_get_error();
}
g_thread = spdk_thread_create("test", NULL);
spdk_set_thread(g_thread);
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
num_failures = CU_get_number_of_failures();
spdk_thread_exit(g_thread);
spdk_thread_destroy(g_thread);
CU_cleanup_registry();
return num_failures;
}

View File

@ -61,6 +61,7 @@ $valgrind $testdir/lib/bdev/part.c/part_ut
$valgrind $testdir/lib/bdev/scsi_nvme.c/scsi_nvme_ut
$valgrind $testdir/lib/bdev/gpt/gpt.c/gpt_ut
$valgrind $testdir/lib/bdev/vbdev_lvol.c/vbdev_lvol_ut
$valgrind $testdir/lib/bdev/vbdev_zone_block.c/vbdev_zone_block_ut
if grep -q '#define SPDK_CONFIG_CRYPTO 1' $rootdir/include/spdk/config.h; then
$valgrind $testdir/lib/bdev/crypto.c/crypto_ut