text: /*-
   *   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_cunit.h"
  #include "spdk/string.h"
  
  #include "vbdev_lvol.c"
  
  #define SPDK_BS_PAGE_SIZE 0x1000
  
  int g_lvolerrno;
  int g_lvserrno;
  int g_cluster_size;
  int g_registered_bdevs;
  int g_num_lvols = 0;
  struct spdk_lvol_store *g_lvs = NULL;
  struct spdk_lvol *g_lvol = NULL;
  struct lvol_store_bdev *g_lvs_bdev = NULL;
  struct spdk_bdev *g_base_bdev = NULL;
  struct spdk_bdev_io *g_io = NULL;
  struct spdk_io_channel *g_ch = NULL;
  struct lvol_task *g_task = NULL;
  
  static struct spdk_bdev g_bdev = {};
  static struct spdk_bs_dev *g_bs_dev = NULL;
  static struct spdk_lvol_store *g_lvol_store = NULL;
  bool lvol_store_initialize_fail = false;
  bool lvol_store_initialize_cb_fail = false;
  bool lvol_already_opened = false;
  bool g_examine_done = false;
  
  void
  spdk_bdev_unregister_done(struct spdk_bdev *bdev, int bdeverrno)
  {
  }
  
  void
  spdk_lvol_open(struct spdk_lvol *lvol, spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg)
  {
  	cb_fn(cb_arg, lvol, g_lvolerrno);
  }
  
  void
  spdk_blob_close(struct spdk_blob **b, spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  }
  
  static struct spdk_lvol *_lvol_create(struct spdk_lvol_store *lvs);
  
  void
  spdk_lvs_load(struct spdk_bs_dev *dev,
  	      spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg)
  {
  	struct spdk_lvol_store *lvs;
  	int i;
  
  	if (g_lvserrno == 0) {
  		lvs = calloc(1, sizeof(*lvs));
  		SPDK_CU_ASSERT_FATAL(lvs != NULL);
  		TAILQ_INIT(&lvs->lvols);
  		g_lvol_store = lvs;
  		for (i = 0; i < g_num_lvols; i++) {
  			_lvol_create(lvs);
  		}
  	}
  
  	cb_fn(cb_arg, g_lvol_store, g_lvserrno);
  }
  
  int
  spdk_bs_bdev_claim(struct spdk_bs_dev *bs_dev, struct spdk_bdev_module_if *module)
  {
  	if (lvol_already_opened == true) {
  		return -1;
  	}
  
  	lvol_already_opened = true;
  
  	return 0;
  }
  
  void
  spdk_vbdev_unregister(struct spdk_bdev *vbdev, spdk_bdev_unregister_cb cb_fn, void *cb_arg)
  {
  	SPDK_CU_ASSERT_FATAL(vbdev != NULL);
  	vbdev->fn_table->destruct(vbdev->ctxt);
  }
  
  void
  spdk_bdev_module_finish_done(void)
  {
  	return;
  }
  
  uint64_t
  spdk_bs_get_page_size(struct spdk_blob_store *bs)
  {
  	return SPDK_BS_PAGE_SIZE;
  }
  
  static void
  bdev_blob_destroy(struct spdk_bs_dev *bs_dev)
  {
  	CU_ASSERT(g_bs_dev != NULL);
  	CU_ASSERT(bs_dev != NULL);
  	CU_ASSERT(g_bs_dev == bs_dev);
  	free(bs_dev);
  	g_bs_dev = NULL;
  	lvol_already_opened = false;
  }
  
  struct spdk_bs_dev *
  spdk_bdev_create_bs_dev(struct spdk_bdev *bdev, spdk_bdev_remove_cb_t remove_cb, void *remove_ctx)
  {
  	struct spdk_bs_dev *bs_dev;
  
  	if (lvol_already_opened == true || bdev == NULL) {
  		return NULL;
  	}
  
  	bs_dev = calloc(1, sizeof(*bs_dev));
  	SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
  	bs_dev->destroy = bdev_blob_destroy;
  
  	CU_ASSERT(g_bs_dev == NULL);
  	g_bs_dev = bs_dev;
  	return bs_dev;
  }
  
  void
  spdk_lvs_opts_init(struct spdk_lvs_opts *opts)
  {
  }
  
  int
  spdk_lvs_init(struct spdk_bs_dev *bs_dev, struct spdk_lvs_opts *o,
  	      spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg)
  {
  	struct spdk_lvol_store *lvs;
  	int error = 0;
  
  	if (lvol_store_initialize_fail) {
  		return -1;
  	}
  
  	if (lvol_store_initialize_cb_fail) {
  		bs_dev->destroy(bs_dev);
  		lvs = NULL;
  		error = -1;
  	} else {
  		lvs = calloc(1, sizeof(*lvs));
  		SPDK_CU_ASSERT_FATAL(lvs != NULL);
  		TAILQ_INIT(&lvs->lvols);
  		lvs->bs_dev = bs_dev;
  		error = 0;
  	}
  	cb_fn(cb_arg, lvs, error);
  
  	return 0;
  }
  
  int
  spdk_lvs_unload(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn, void *cb_arg)
  {
  	struct spdk_lvol *lvol, *tmp;
  
  	TAILQ_FOREACH_SAFE(lvol, &lvs->lvols, link, tmp) {
  		TAILQ_REMOVE(&lvs->lvols, lvol, link);
  		free(lvol->old_name);
  		free(lvol);
  	}
  	g_lvol_store = NULL;
  	free(lvs);
  
  	g_bs_dev->destroy(g_bs_dev);
  
  	if (cb_fn != NULL) {
  		cb_fn(cb_arg, 0);
  	}
  
  	return 0;
  }
  
  int
  spdk_lvs_destroy(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn,
  		 void *cb_arg)
  {
  	struct spdk_lvol *lvol, *tmp;
  
  	TAILQ_FOREACH_SAFE(lvol, &lvs->lvols, link, tmp) {
  		TAILQ_REMOVE(&lvs->lvols, lvol, link);
  		free(lvol->old_name);
  		free(lvol);
  	}
  	g_lvol_store = NULL;
  	free(lvs);
  
  	g_bs_dev->destroy(g_bs_dev);
  
  	if (cb_fn != NULL) {
  		cb_fn(cb_arg, 0);
  	}
  
  	return 0;
  }
  
  int
  spdk_lvol_resize(struct spdk_lvol *lvol, size_t sz,  spdk_lvol_op_complete cb_fn, void *cb_arg)
  {
  	cb_fn(cb_arg, 0);
  
  	return 0;
  }
  
  uint64_t
  spdk_bs_get_cluster_size(struct spdk_blob_store *bs)
  {
  	return g_cluster_size;
  }
  
  struct spdk_bdev *
  spdk_bdev_get_by_name(const char *bdev_name)
  {
  	if (!strcmp(g_base_bdev->name, bdev_name)) {
  		return g_base_bdev;
  	}
  
  	return NULL;
  }
  
  void
  spdk_lvol_close(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_arg)
  {
  	struct spdk_lvs_req *destruct_req;
  	struct spdk_lvol *iter_lvol, *tmp;
  	bool all_lvols_closed = true;
  
  	lvol->ref_count--;
  
  	TAILQ_FOREACH_SAFE(iter_lvol, &lvol->lvol_store->lvols, link, tmp) {
  		if (iter_lvol->ref_count != 0) {
  			all_lvols_closed = false;
  		}
  	}
  
  	destruct_req = lvol->lvol_store->destruct_req;
  	if (destruct_req && all_lvols_closed == true) {
  		if (!lvol->lvol_store->destruct) {
  			spdk_lvs_unload(lvol->lvol_store, destruct_req->cb_fn, destruct_req->cb_arg);
  			free(destruct_req);
  		}
  	}
  
  	cb_fn(cb_arg, 0);
  }
  
  void
  spdk_lvol_destroy(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_arg)
  {
  	struct spdk_lvs_req *destruct_req;
  
  	SPDK_CU_ASSERT_FATAL(lvol == g_lvol);
  
  	if (lvol->ref_count != 0) {
  		cb_fn(cb_arg, -ENODEV);
  	}
  
  	TAILQ_REMOVE(&lvol->lvol_store->lvols, lvol, link);
  
  	destruct_req = lvol->lvol_store->destruct_req;
  	if (destruct_req && TAILQ_EMPTY(&lvol->lvol_store->lvols)) {
  		if (!lvol->lvol_store->destruct) {
  			spdk_lvs_unload(lvol->lvol_store, destruct_req->cb_fn, destruct_req->cb_arg);
  		} else {
  			spdk_lvs_destroy(lvol->lvol_store, destruct_req->cb_fn, destruct_req->cb_arg);
  			free(destruct_req);
  		}
  	}
  	g_lvol = NULL;
  	free(lvol->old_name);
  	free(lvol);
  
  	cb_fn(cb_arg, 0);
  }
  
  void
  spdk_bdev_io_complete(struct spdk_bdev_io *bdev_io, enum spdk_bdev_io_status status)
  {
  }
  
  struct spdk_io_channel *spdk_lvol_get_io_channel(struct spdk_lvol *lvol)
  {
  	CU_ASSERT(lvol == g_lvol);
  	return g_ch;
  }
  
  void
  spdk_bdev_io_get_buf(struct spdk_bdev_io *bdev_io, spdk_bdev_io_get_buf_cb cb, uint64_t len)
  {
  	CU_ASSERT(cb == lvol_read);
  }
  
  void
  spdk_bs_io_read_blob(struct spdk_blob *blob, struct spdk_io_channel *channel,
  		     void *payload, uint64_t offset, uint64_t length,
  		     spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  }
  
  void
  spdk_bs_io_write_blob(struct spdk_blob *blob, struct spdk_io_channel *channel,
  		      void *payload, uint64_t offset, uint64_t length,
  		      spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  }
  
  void
  spdk_bs_io_unmap_blob(struct spdk_blob *blob, struct spdk_io_channel *channel,
  		      uint64_t offset, uint64_t length, spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  	CU_ASSERT(blob == NULL);
  	CU_ASSERT(channel == g_ch);
  	CU_ASSERT(offset == g_io->u.bdev.offset_blocks);
  	CU_ASSERT(length == g_io->u.bdev.num_blocks);
  }
  
  void
  spdk_bs_io_write_zeroes_blob(struct spdk_blob *blob, struct spdk_io_channel *channel,
  			     uint64_t offset, uint64_t length, spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  	CU_ASSERT(blob == NULL);
  	CU_ASSERT(channel == g_ch);
  	CU_ASSERT(offset == g_io->u.bdev.offset_blocks);
  	CU_ASSERT(length == g_io->u.bdev.num_blocks);
  }
  
  void
  spdk_bs_io_writev_blob(struct spdk_blob *blob, struct spdk_io_channel *channel,
  		       struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
  		       spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  	CU_ASSERT(blob == NULL);
  	CU_ASSERT(channel == g_ch);
  	CU_ASSERT(offset == g_io->u.bdev.offset_blocks);
  	CU_ASSERT(length == g_io->u.bdev.num_blocks);
  }
  
  void
  spdk_bs_io_readv_blob(struct spdk_blob *blob, struct spdk_io_channel *channel,
  		      struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
  		      spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  	CU_ASSERT(blob == NULL);
  	CU_ASSERT(channel == g_ch);
  	CU_ASSERT(offset == g_io->u.bdev.offset_blocks);
  	CU_ASSERT(length == g_io->u.bdev.num_blocks);
  }
  
  void
  spdk_bs_io_flush_channel(struct spdk_io_channel *channel, spdk_blob_op_complete cb_fn, void *cb_arg)
  {
  }
  
  void
  spdk_bdev_module_list_add(struct spdk_bdev_module_if *bdev_module)
  {
  }
  
  int
  spdk_json_write_name(struct spdk_json_write_ctx *w, const char *name)
  {
  	return 0;
  }
  
  int
  spdk_json_write_string(struct spdk_json_write_ctx *w, const char *val)
  {
  	return 0;
  }
  
  int
  spdk_json_write_object_begin(struct spdk_json_write_ctx *w)
  {
  	return 0;
  }
  
  int
  spdk_json_write_object_end(struct spdk_json_write_ctx *w)
  {
  	return 0;
  }
  
  const char *
  spdk_bdev_get_name(const struct spdk_bdev *bdev)
  {
  	return "test";
  }
  
  int
  spdk_vbdev_register(struct spdk_bdev *vbdev, struct spdk_bdev **base_bdevs, int base_bdev_count)
  {
  	g_registered_bdevs++;
  	return 0;
  }
  
  void
  spdk_bdev_module_examine_done(struct spdk_bdev_module_if *module)
  {
  	g_examine_done = true;
  }
  
  static struct spdk_lvol *
  _lvol_create(struct spdk_lvol_store *lvs)
  {
  	struct spdk_lvol *lvol = calloc(1, sizeof(*lvol));
  
  	SPDK_CU_ASSERT_FATAL(lvol != NULL);
  
  	lvol->lvol_store = lvs;
  	lvol->ref_count++;
  	lvol->old_name = spdk_sprintf_alloc("%s", "UNIT_TEST_UUID");
  	SPDK_CU_ASSERT_FATAL(lvol->old_name != NULL);
  
  	TAILQ_INSERT_TAIL(&lvol->lvol_store->lvols, lvol, link);
  
  	return lvol;
  }
  
  int
  spdk_lvol_create(struct spdk_lvol_store *lvs, const char *name, size_t sz,
  		 spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg)
  {
  	struct spdk_lvol *lvol;
  
  	lvol = _lvol_create(lvs);
  	cb_fn(cb_arg, lvol, 0);
  
  	return 0;
  }
  
  static void
  lvol_store_op_complete(void *cb_arg, int lvserrno)
  {
  	g_lvserrno = lvserrno;
  	return;
  }
  
  static void
  lvol_store_op_with_handle_complete(void *cb_arg, struct spdk_lvol_store *lvs, int lvserrno)
  {
  	g_lvserrno = lvserrno;
  	g_lvol_store = lvs;
  	return;
  }
  
  static void
  vbdev_lvol_create_complete(void *cb_arg, struct spdk_lvol *lvol, int lvolerrno)
  {
  	g_lvolerrno = lvolerrno;
  	g_lvol = lvol;
  }
  
  static void
  vbdev_lvol_resize_complete(void *cb_arg, int lvolerrno)
  {
  	g_lvolerrno = lvolerrno;
  }
  
  static void
  ut_lvs_destroy(void)
  {
  	int rc = 0;
  	int sz = 10;
  	struct spdk_lvol_store *lvs;
  
  	/* Lvol store is succesfully created */
  	rc = vbdev_lvs_create(&g_bdev, "lvs", 0, lvol_store_op_with_handle_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvserrno == 0);
  	SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL);
  	CU_ASSERT(g_bs_dev != NULL);
  
  	lvs = g_lvol_store;
  	g_lvol_store = NULL;
  
  	uuid_generate_time(lvs->uuid);
  
  	/* Suuccessfully create lvol, which should be unloaded with lvs later */
  	g_lvolerrno = -1;
  	rc = vbdev_lvol_create(lvs, "lvol", sz, vbdev_lvol_create_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvolerrno == 0);
  	SPDK_CU_ASSERT_FATAL(g_lvol != NULL);
  
  	/* Unload lvol store */
  	vbdev_lvs_destruct(lvs, lvol_store_op_complete, NULL);
  	CU_ASSERT(g_lvserrno == 0);
  	CU_ASSERT(g_lvol_store == NULL);
  }
  
  static void
  ut_lvol_init(void)
  {
  	int sz = 10;
  	int rc;
  
  	g_lvs = calloc(1, sizeof(*g_lvs));
  	SPDK_CU_ASSERT_FATAL(g_lvs != NULL);
  	TAILQ_INIT(&g_lvs->lvols);
  	g_lvs_bdev = calloc(1, sizeof(*g_lvs_bdev));
  	SPDK_CU_ASSERT_FATAL(g_lvs_bdev != NULL);
  	g_base_bdev = calloc(1, sizeof(*g_base_bdev));
  	SPDK_CU_ASSERT_FATAL(g_base_bdev != NULL);
  
  	g_lvs_bdev->lvs = g_lvs;
  	g_lvs_bdev->bdev = g_base_bdev;
  
  	uuid_generate_time(g_lvs->uuid);
  
  	TAILQ_INSERT_TAIL(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores);
  
  	/* Successful lvol create */
  	g_lvolerrno = -1;
  	rc = vbdev_lvol_create(g_lvs, "lvol", sz, vbdev_lvol_create_complete, NULL);
  	SPDK_CU_ASSERT_FATAL(rc == 0);
  	CU_ASSERT(g_lvol != NULL);
  	CU_ASSERT(g_lvolerrno == 0);
  
  	/* Successful lvol destruct */
  	vbdev_lvol_destruct(g_lvol);
  	CU_ASSERT(g_lvol == NULL);
  
  	TAILQ_REMOVE(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores);
  
  	free(g_lvs);
  	free(g_lvs_bdev);
  	free(g_base_bdev);
  
  
  }
  
  static void
  ut_lvol_hotremove(void)
  {
  	int rc = 0;
  
  	lvol_store_initialize_fail = false;
  	lvol_store_initialize_cb_fail = false;
  	lvol_already_opened = false;
  	g_bs_dev = NULL;
  
  	/* Lvol store is succesfully created */
  	rc = vbdev_lvs_create(&g_bdev, "lvs", 0, lvol_store_op_with_handle_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvserrno == 0);
  	CU_ASSERT(g_lvol_store != NULL);
  	CU_ASSERT(g_bs_dev != NULL);
  
  	/* Hot remove callback with NULL - stability check */
  	vbdev_lvs_hotremove_cb(NULL);
  
  	/* Hot remove lvs on bdev removal */
  	vbdev_lvs_hotremove_cb(&g_bdev);
  
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(TAILQ_EMPTY(&g_spdk_lvol_pairs));
  
  }
  
  static void
  ut_lvol_examine(void)
  {
  	struct spdk_bdev *bdev;
  
  	lvol_already_opened = false;
  	g_bs_dev = NULL;
  	g_lvserrno = 0;
  	g_examine_done = false;
  
  	/* Examine with NULL bdev */
  	vbdev_lvs_examine(NULL);
  	CU_ASSERT(g_bs_dev == NULL);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_examine_done == true);
  
  	/* Examine unsuccessfully - bdev already opened */
  	g_bs_dev = NULL;
  	g_examine_done = false;
  	g_lvserrno = -1;
  	lvol_already_opened = true;
  	vbdev_lvs_examine(&g_bdev);
  	CU_ASSERT(g_bs_dev == NULL);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_examine_done == true);
  
  	/* Examine unsuccessfully - fail on lvol store */
  	g_bs_dev = NULL;
  	g_examine_done = false;
  	g_lvserrno = -1;
  	lvol_already_opened = false;
  	vbdev_lvs_examine(&g_bdev);
  	CU_ASSERT(g_bs_dev != NULL);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_examine_done == true);
  	CU_ASSERT(TAILQ_EMPTY(&g_spdk_lvol_pairs));
  	free(g_bs_dev);
  
  	/* Examine unsuccesfully - fail on lvol load */
  	g_bs_dev = NULL;
  	g_lvserrno = 0;
  	g_lvolerrno = -1;
  	g_num_lvols = 1;
  	g_examine_done = false;
  	lvol_already_opened = false;
  	g_registered_bdevs = 0;
  	vbdev_lvs_examine(&g_bdev);
  	CU_ASSERT(g_bs_dev != NULL);
  	SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL);
  	CU_ASSERT(g_examine_done == true);
  	CU_ASSERT(g_registered_bdevs == 0);
  	CU_ASSERT(!TAILQ_EMPTY(&g_spdk_lvol_pairs));
  	CU_ASSERT(TAILQ_EMPTY(&g_lvol_store->lvols));
  	vbdev_lvs_destruct(g_lvol_store, lvol_store_op_complete, NULL);
  	free(g_bs_dev);
  
  	/* Examine succesfully */
  	g_bs_dev = NULL;
  	g_lvserrno = 0;
  	g_lvolerrno = 0;
  	g_examine_done = false;
  	g_registered_bdevs = 0;
  	lvol_already_opened = false;
  	vbdev_lvs_examine(&g_bdev);
  	CU_ASSERT(g_bs_dev != NULL);
  	SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL);
  	CU_ASSERT(g_examine_done == true);
  	CU_ASSERT(g_registered_bdevs != 0);
  	CU_ASSERT(!TAILQ_EMPTY(&g_spdk_lvol_pairs));
  	SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_lvol_store->lvols));
  	TAILQ_FIRST(&g_lvol_store->lvols)->ref_count--;
  	bdev = TAILQ_FIRST(&g_lvol_store->lvols)->bdev;
  	vbdev_lvs_destruct(g_lvol_store, lvol_store_op_complete, NULL);
  	free(bdev->name);
  	free(bdev);
  	free(g_bs_dev);
  	free(g_lvol_store);
  }
  
  static void
  ut_lvol_resize(void)
  {
  	int sz = 10;
  	int rc = 0;
  
  	g_lvs = calloc(1, sizeof(*g_lvs));
  	SPDK_CU_ASSERT_FATAL(g_lvs != NULL);
  
  	TAILQ_INIT(&g_lvs->lvols);
  
  	g_lvs_bdev = calloc(1, sizeof(*g_lvs_bdev));
  	SPDK_CU_ASSERT_FATAL(g_lvs_bdev != NULL);
  	g_base_bdev = calloc(1, sizeof(*g_base_bdev));
  	SPDK_CU_ASSERT_FATAL(g_base_bdev != NULL);
  	g_lvs_bdev->lvs = g_lvs;
  	g_lvs_bdev->bdev = g_base_bdev;
  
  
  	uuid_generate_time(g_lvs->uuid);
  	g_base_bdev->blocklen = 4096;
  	TAILQ_INSERT_TAIL(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores);
  
  	/* Successful lvol create */
  	g_lvolerrno = -1;
  	rc = vbdev_lvol_create(g_lvs, "lvol", sz, vbdev_lvol_create_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvolerrno == 0);
  	SPDK_CU_ASSERT_FATAL(g_lvol != NULL);
  
  	g_base_bdev->ctxt = g_lvol;
  
  	g_base_bdev->name = spdk_sprintf_alloc("%s", g_lvol->old_name);
  	SPDK_CU_ASSERT_FATAL(g_base_bdev->name != NULL);
  
  	/* Successful lvol resize */
  	rc = vbdev_lvol_resize(g_lvol->old_name, 20, vbdev_lvol_resize_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_base_bdev->blockcnt == 20 * g_cluster_size / g_base_bdev->blocklen);
  
  	/* Resize with wrong bdev name */
  	rc = vbdev_lvol_resize("wrong name", 20, vbdev_lvol_resize_complete, NULL);
  	CU_ASSERT(rc != 0);
  
  	/* Resize with correct bdev name, but wrong lvol name */
  	free(g_lvol->old_name);
  	g_lvol->old_name = strdup("wrong name");
  	SPDK_CU_ASSERT_FATAL(g_lvol->old_name != NULL);
  	rc = vbdev_lvol_resize(g_base_bdev->name, 20, vbdev_lvol_resize_complete, NULL);
  	CU_ASSERT(rc != 0);
  
  	/* Successful lvol destruct */
  	vbdev_lvol_destruct(g_lvol);
  	CU_ASSERT(g_lvol == NULL);
  
  	TAILQ_REMOVE(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores);
  	free(g_lvs);
  	free(g_lvs_bdev);
  	free(g_base_bdev->name);
  	free(g_base_bdev);
  }
  
  static void
  ut_lvs_unload(void)
  {
  	int rc = 0;
  	int sz = 10;
  	struct spdk_lvol_store *lvs;
  
  	/* Lvol store is succesfully created */
  	rc = vbdev_lvs_create(&g_bdev, "lvs", 0, lvol_store_op_with_handle_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvserrno == 0);
  	SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL);
  	CU_ASSERT(g_bs_dev != NULL);
  
  	lvs = g_lvol_store;
  	g_lvol_store = NULL;
  
  	uuid_generate_time(lvs->uuid);
  
  	/* Suuccessfully create lvol, which should be destroyed with lvs later */
  	g_lvolerrno = -1;
  	rc = vbdev_lvol_create(lvs, "lvol", sz, vbdev_lvol_create_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvolerrno == 0);
  	SPDK_CU_ASSERT_FATAL(g_lvol != NULL);
  
  	/* Unload lvol store */
  	vbdev_lvs_unload(lvs, lvol_store_op_complete, NULL);
  	CU_ASSERT(g_lvserrno == 0);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_lvol != NULL);
  }
  
  static void
  ut_lvs_init(void)
  {
  	int rc = 0;
  	struct spdk_lvol_store *lvs;
  	struct spdk_bs_dev *bs_dev_temp;
  
  	/* spdk_lvs_init() fails */
  	lvol_store_initialize_fail = true;
  
  	rc = vbdev_lvs_create(&g_bdev, "lvs", 0, lvol_store_op_with_handle_complete, NULL);
  	CU_ASSERT(rc != 0);
  	CU_ASSERT(g_lvserrno == 0);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_bs_dev == NULL);
  
  	lvol_store_initialize_fail = false;
  
  	/* spdk_lvs_init_cb() fails */
  	lvol_store_initialize_cb_fail = true;
  
  	rc = vbdev_lvs_create(&g_bdev, "lvs", 0, lvol_store_op_with_handle_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvserrno != 0);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_bs_dev == NULL);
  
  	lvol_store_initialize_cb_fail = false;
  
  	/* Lvol store is succesfully created */
  	rc = vbdev_lvs_create(&g_bdev, "lvs", 0, lvol_store_op_with_handle_complete, NULL);
  	CU_ASSERT(rc == 0);
  	CU_ASSERT(g_lvserrno == 0);
  	CU_ASSERT(g_lvol_store != NULL);
  	CU_ASSERT(g_bs_dev != NULL);
  
  	lvs = g_lvol_store;
  	g_lvol_store = NULL;
  	bs_dev_temp = g_bs_dev;
  	g_bs_dev = NULL;
  
  	/* Bdev with lvol store already claimed */
  	rc = vbdev_lvs_create(&g_bdev, "lvs", 0, lvol_store_op_with_handle_complete, NULL);
  	CU_ASSERT(rc != 0);
  	CU_ASSERT(g_lvserrno == 0);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_bs_dev == NULL);
  
  	/* Destruct lvol store */
  	g_bs_dev = bs_dev_temp;
  
  	vbdev_lvs_destruct(lvs, lvol_store_op_complete, NULL);
  	CU_ASSERT(g_lvserrno == 0);
  	CU_ASSERT(g_lvol_store == NULL);
  	CU_ASSERT(g_bs_dev == NULL);
  	free(g_bs_dev);
  
  }
  
  static void
  ut_vbdev_lvol_get_io_channel(void)
  {
  	struct spdk_io_channel *ch;
  
  	g_lvol = calloc(1, sizeof(struct spdk_lvol));
  	SPDK_CU_ASSERT_FATAL(g_lvol != NULL);
  
  	ch = vbdev_lvol_get_io_channel(g_lvol);
  	CU_ASSERT(ch == g_ch);
  
  	free(g_lvol);
  
  }
  
  static void
  ut_vbdev_lvol_io_type_supported(void)
  {
  	struct spdk_lvol *lvol = g_lvol;
  	bool ret;
  	/* Supported types */
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_READ);
  	CU_ASSERT(ret == true);
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_WRITE);
  	CU_ASSERT(ret == true);
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_FLUSH);
  	CU_ASSERT(ret == true);
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_RESET);
  	CU_ASSERT(ret == true);
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_UNMAP);
  	CU_ASSERT(ret == true);
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_WRITE_ZEROES);
  	CU_ASSERT(ret == true);
  
  	/* Unsupported types */
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_NVME_ADMIN);
  	CU_ASSERT(ret == false);
  	ret = vbdev_lvol_io_type_supported(lvol, SPDK_BDEV_IO_TYPE_NVME_IO);
  	CU_ASSERT(ret == false);
  }
  
  static void
  ut_lvol_op_comp(void)
  {
  	struct lvol_task task;
  
  	lvol_op_comp(&task, 1);
  	CU_ASSERT(task.status == SPDK_BDEV_IO_STATUS_FAILED);
  }
  
  static void
  ut_lvol_flush(void)
  {
  	g_io = calloc(1, sizeof(struct spdk_bdev_io) + sizeof(struct lvol_task));
  	SPDK_CU_ASSERT_FATAL(g_io != NULL);
  	g_base_bdev = calloc(1, sizeof(struct spdk_bdev));
  	SPDK_CU_ASSERT_FATAL(g_base_bdev != NULL);
  	g_lvol = calloc(1, sizeof(struct spdk_lvol));
  	SPDK_CU_ASSERT_FATAL(g_lvol != NULL);
  
  	g_task = (struct lvol_task *)g_io->driver_ctx;
  	g_io->bdev = g_base_bdev;
  	g_io->bdev->ctxt = g_lvol;
  
  	/* Successful flush to lvol */
  	lvol_flush(g_ch, g_io);
  
  	CU_ASSERT(g_task->status == SPDK_BDEV_IO_STATUS_SUCCESS);
  
  	free(g_io);
  	free(g_base_bdev);
  	free(g_lvol);
  }
  
  static void
  ut_lvol_read_write(void)
  {
  	g_io = calloc(1, sizeof(struct spdk_bdev_io) + sizeof(struct lvol_task));
  	SPDK_CU_ASSERT_FATAL(g_io != NULL);
  	g_base_bdev = calloc(1, sizeof(struct spdk_bdev));
  	SPDK_CU_ASSERT_FATAL(g_base_bdev != NULL);
  	g_lvol = calloc(1, sizeof(struct spdk_lvol));
  	SPDK_CU_ASSERT_FATAL(g_lvol != NULL);
  
  	g_task = (struct lvol_task *)g_io->driver_ctx;
  	g_io->bdev = g_base_bdev;
  	g_io->bdev->ctxt = g_lvol;
  	g_io->u.bdev.offset_blocks = 20;
  	g_io->u.bdev.num_blocks = 20;
  
  	lvol_read(g_ch, g_io);
  	CU_ASSERT(g_task->status == SPDK_BDEV_IO_STATUS_SUCCESS);
  
  	lvol_write(g_lvol, g_ch, g_io);
  	CU_ASSERT(g_task->status == SPDK_BDEV_IO_STATUS_SUCCESS);
  
  	free(g_io);
  	free(g_base_bdev);
  	free(g_lvol);
  }
  
  static void
  ut_vbdev_lvol_submit_request(void)
  {
  	g_io = calloc(1, sizeof(struct spdk_bdev_io) + sizeof(struct lvol_task));
  	SPDK_CU_ASSERT_FATAL(g_io != NULL);
  	g_base_bdev = calloc(1, sizeof(struct spdk_bdev));
  	SPDK_CU_ASSERT_FATAL(g_base_bdev != NULL);
  	g_task = (struct lvol_task *)g_io->driver_ctx;
  
  	g_io->bdev = g_base_bdev;
  
  	g_io->type = SPDK_BDEV_IO_TYPE_READ;
  	vbdev_lvol_submit_request(g_ch, g_io);
  
  	free(g_io);
  	free(g_base_bdev);
  }
  
  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("lvol", NULL, NULL);
  	if (suite == NULL) {
  		CU_cleanup_registry();
  		return CU_get_error();
  	}
  
  	if (
  		CU_add_test(suite, "ut_lvs_init", ut_lvs_init) == NULL ||
  		CU_add_test(suite, "ut_lvol_init", ut_lvol_init) == NULL ||
  		CU_add_test(suite, "ut_lvs_destroy", ut_lvs_destroy) == NULL ||
  		CU_add_test(suite, "ut_lvs_unload", ut_lvs_unload) == NULL ||
  		CU_add_test(suite, "ut_lvol_resize", ut_lvol_resize) == NULL ||
  		CU_add_test(suite, "lvol_hotremove", ut_lvol_hotremove) == NULL ||
  		CU_add_test(suite, "ut_vbdev_lvol_get_io_channel", ut_vbdev_lvol_get_io_channel) == NULL ||
  		CU_add_test(suite, "ut_vbdev_lvol_io_type_supported", ut_vbdev_lvol_io_type_supported) == NULL ||
  		CU_add_test(suite, "ut_lvol_op_comp", ut_lvol_op_comp) == NULL ||
  		CU_add_test(suite, "ut_lvol_flush", ut_lvol_flush) == NULL ||
  		CU_add_test(suite, "ut_lvol_read_write", ut_lvol_read_write) == NULL ||
  		CU_add_test(suite, "ut_vbdev_lvol_submit_request", ut_vbdev_lvol_submit_request) == NULL ||
  		CU_add_test(suite, "lvol_examine", ut_lvol_examine) == NULL
  	) {
  		CU_cleanup_registry();
  		return CU_get_error();
  	}
  
  	CU_basic_set_mode(CU_BRM_VERBOSE);
  	CU_basic_run_tests();
  	num_failures = CU_get_number_of_failures();
  	CU_cleanup_registry();
  	return num_failures;
  }
  