diff options
Diffstat (limited to 'src/spdk/test/unit/lib/blobfs')
13 files changed, 2107 insertions, 0 deletions
diff --git a/src/spdk/test/unit/lib/blobfs/Makefile b/src/spdk/test/unit/lib/blobfs/Makefile new file mode 100644 index 000000000..5a2c5b3f3 --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/Makefile @@ -0,0 +1,44 @@ +# +# 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)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +DIRS-y = tree.c blobfs_async_ut blobfs_sync_ut blobfs_bdev.c + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/.gitignore b/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/.gitignore new file mode 100644 index 000000000..aea3b021d --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/.gitignore @@ -0,0 +1 @@ +blobfs_async_ut diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/Makefile b/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/Makefile new file mode 100644 index 000000000..6de0fc248 --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/Makefile @@ -0,0 +1,39 @@ +# +# 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)/../../../../..) + +SPDK_LIB_LIST = blob +TEST_FILE = blobfs_async_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/blobfs_async_ut.c b/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/blobfs_async_ut.c new file mode 100644 index 000000000..134b8bfe9 --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_async_ut/blobfs_async_ut.c @@ -0,0 +1,704 @@ +/*- + * 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 "CUnit/Basic.h" + +#include "common/lib/ut_multithread.c" + +#include "spdk_cunit.h" +#include "blobfs/blobfs.c" +#include "blobfs/tree.c" +#include "blob/blobstore.h" + +#include "spdk_internal/thread.h" + +#include "unit/lib/blob/bs_dev_common.c" + +struct spdk_filesystem *g_fs; +struct spdk_file *g_file; +int g_fserrno; +struct spdk_trace_histories *g_trace_histories; +DEFINE_STUB_V(spdk_trace_add_register_fn, (struct spdk_trace_register_fn *reg_fn)); +DEFINE_STUB_V(spdk_trace_register_description, (const char *name, + uint16_t tpoint_id, uint8_t owner_type, + uint8_t object_type, uint8_t new_object, + uint8_t arg1_is_ptr, const char *arg1_name)); +DEFINE_STUB_V(_spdk_trace_record, (uint64_t tsc, uint16_t tpoint_id, uint16_t poller_id, + uint32_t size, uint64_t object_id, uint64_t arg1)); + +/* Return NULL to test hardcoded defaults. */ +struct spdk_conf_section * +spdk_conf_find_section(struct spdk_conf *cp, const char *name) +{ + return NULL; +} + +/* Return -1 to test hardcoded defaults. */ +int +spdk_conf_section_get_intval(struct spdk_conf_section *sp, const char *key) +{ + return -1; +} + +static void +fs_op_complete(void *ctx, int fserrno) +{ + g_fserrno = fserrno; +} + +static void +fs_op_with_handle_complete(void *ctx, struct spdk_filesystem *fs, int fserrno) +{ + g_fs = fs; + g_fserrno = fserrno; +} + +static void +fs_poll_threads(void) +{ + poll_threads(); + while (spdk_thread_poll(g_cache_pool_thread, 0, 0) > 0) {} +} + +static void +fs_init(void) +{ + struct spdk_filesystem *fs; + struct spdk_bs_dev *dev; + + dev = init_dev(); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); +} + +static void +create_cb(void *ctx, int fserrno) +{ + g_fserrno = fserrno; +} + +static void +open_cb(void *ctx, struct spdk_file *f, int fserrno) +{ + g_fserrno = fserrno; + g_file = f; +} + +static void +delete_cb(void *ctx, int fserrno) +{ + g_fserrno = fserrno; +} + +static void +fs_open(void) +{ + struct spdk_filesystem *fs; + spdk_fs_iter iter; + struct spdk_bs_dev *dev; + struct spdk_file *file; + char name[257] = {'\0'}; + + dev = init_dev(); + memset(name, 'a', sizeof(name) - 1); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + g_fserrno = 0; + /* Open should fail, because the file name is too long. */ + spdk_fs_open_file_async(fs, name, SPDK_BLOBFS_OPEN_CREATE, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == -ENAMETOOLONG); + + g_fserrno = 0; + spdk_fs_open_file_async(fs, "file1", 0, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == -ENOENT); + + g_file = NULL; + g_fserrno = 1; + spdk_fs_open_file_async(fs, "file1", SPDK_BLOBFS_OPEN_CREATE, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + CU_ASSERT(!strcmp("file1", g_file->name)); + CU_ASSERT(g_file->ref_count == 1); + + iter = spdk_fs_iter_first(fs); + CU_ASSERT(iter != NULL); + file = spdk_fs_iter_get_file(iter); + SPDK_CU_ASSERT_FATAL(file != NULL); + CU_ASSERT(!strcmp("file1", file->name)); + iter = spdk_fs_iter_next(iter); + CU_ASSERT(iter == NULL); + + g_fserrno = 0; + /* Delete should successful, we will mark the file as deleted. */ + spdk_fs_delete_file_async(fs, "file1", delete_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(!TAILQ_EMPTY(&fs->files)); + + g_fserrno = 1; + spdk_file_close_async(g_file, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(TAILQ_EMPTY(&fs->files)); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); +} + +static void +fs_create(void) +{ + struct spdk_filesystem *fs; + struct spdk_bs_dev *dev; + char name[257] = {'\0'}; + + dev = init_dev(); + memset(name, 'a', sizeof(name) - 1); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + g_fserrno = 0; + /* Create should fail, because the file name is too long. */ + spdk_fs_create_file_async(fs, name, create_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == -ENAMETOOLONG); + + g_fserrno = 1; + spdk_fs_create_file_async(fs, "file1", create_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + + g_fserrno = 1; + spdk_fs_create_file_async(fs, "file1", create_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == -EEXIST); + + g_fserrno = 1; + spdk_fs_delete_file_async(fs, "file1", delete_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(TAILQ_EMPTY(&fs->files)); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); +} + +static void +fs_truncate(void) +{ + struct spdk_filesystem *fs; + struct spdk_bs_dev *dev; + + dev = init_dev(); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + g_file = NULL; + g_fserrno = 1; + spdk_fs_open_file_async(fs, "file1", SPDK_BLOBFS_OPEN_CREATE, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + g_fserrno = 1; + spdk_file_truncate_async(g_file, 18 * 1024 * 1024 + 1, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_file->length == 18 * 1024 * 1024 + 1); + + g_fserrno = 1; + spdk_file_truncate_async(g_file, 1, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_file->length == 1); + + g_fserrno = 1; + spdk_file_truncate_async(g_file, 18 * 1024 * 1024 + 1, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_file->length == 18 * 1024 * 1024 + 1); + + g_fserrno = 1; + spdk_file_close_async(g_file, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_file->ref_count == 0); + + g_fserrno = 1; + spdk_fs_delete_file_async(fs, "file1", delete_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(TAILQ_EMPTY(&fs->files)); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); +} + +static void +fs_rename(void) +{ + struct spdk_filesystem *fs; + struct spdk_file *file, *file2, *file_iter; + struct spdk_bs_dev *dev; + + dev = init_dev(); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + g_fserrno = 1; + spdk_fs_create_file_async(fs, "file1", create_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + + g_file = NULL; + g_fserrno = 1; + spdk_fs_open_file_async(fs, "file1", 0, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + CU_ASSERT(g_file->ref_count == 1); + + file = g_file; + g_file = NULL; + g_fserrno = 1; + spdk_file_close_async(file, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(file->ref_count == 0); + + g_file = NULL; + g_fserrno = 1; + spdk_fs_open_file_async(fs, "file2", SPDK_BLOBFS_OPEN_CREATE, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + CU_ASSERT(g_file->ref_count == 1); + + file2 = g_file; + g_file = NULL; + g_fserrno = 1; + spdk_file_close_async(file2, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(file2->ref_count == 0); + + /* + * Do a 3-way rename. This should delete the old "file2", then rename + * "file1" to "file2". + */ + g_fserrno = 1; + spdk_fs_rename_file_async(fs, "file1", "file2", fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(file->ref_count == 0); + CU_ASSERT(!strcmp(file->name, "file2")); + CU_ASSERT(TAILQ_FIRST(&fs->files) == file); + CU_ASSERT(TAILQ_NEXT(file, tailq) == NULL); + + g_fserrno = 0; + spdk_fs_delete_file_async(fs, "file1", delete_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == -ENOENT); + CU_ASSERT(!TAILQ_EMPTY(&fs->files)); + TAILQ_FOREACH(file_iter, &fs->files, tailq) { + if (file_iter == NULL) { + SPDK_CU_ASSERT_FATAL(false); + } + } + + g_fserrno = 1; + spdk_fs_delete_file_async(fs, "file2", delete_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(TAILQ_EMPTY(&fs->files)); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); +} + +static void +fs_rw_async(void) +{ + struct spdk_filesystem *fs; + struct spdk_bs_dev *dev; + uint8_t w_buf[4096]; + uint8_t r_buf[4096]; + + dev = init_dev(); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + g_file = NULL; + g_fserrno = 1; + spdk_fs_open_file_async(fs, "file1", SPDK_BLOBFS_OPEN_CREATE, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + /* Write file */ + CU_ASSERT(g_file->length == 0); + g_fserrno = 1; + memset(w_buf, 0x5a, sizeof(w_buf)); + spdk_file_write_async(g_file, fs->sync_target.sync_io_channel, w_buf, 0, 4096, + fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_file->length == 4096); + + /* Read file */ + g_fserrno = 1; + memset(r_buf, 0x0, sizeof(r_buf)); + spdk_file_read_async(g_file, fs->sync_target.sync_io_channel, r_buf, 0, 4096, + fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(memcmp(r_buf, w_buf, sizeof(r_buf)) == 0); + + g_fserrno = 1; + spdk_file_close_async(g_file, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); +} + +static void +fs_writev_readv_async(void) +{ + struct spdk_filesystem *fs; + struct spdk_bs_dev *dev; + struct iovec w_iov[2]; + struct iovec r_iov[2]; + uint8_t w_buf[4096]; + uint8_t r_buf[4096]; + + dev = init_dev(); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + g_file = NULL; + g_fserrno = 1; + spdk_fs_open_file_async(fs, "file1", SPDK_BLOBFS_OPEN_CREATE, open_cb, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + /* Write file */ + CU_ASSERT(g_file->length == 0); + g_fserrno = 1; + memset(w_buf, 0x5a, sizeof(w_buf)); + w_iov[0].iov_base = w_buf; + w_iov[0].iov_len = 2048; + w_iov[1].iov_base = w_buf + 2048; + w_iov[1].iov_len = 2048; + spdk_file_writev_async(g_file, fs->sync_target.sync_io_channel, + w_iov, 2, 0, 4096, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_file->length == 4096); + + /* Read file */ + g_fserrno = 1; + memset(r_buf, 0x0, sizeof(r_buf)); + r_iov[0].iov_base = r_buf; + r_iov[0].iov_len = 2048; + r_iov[1].iov_base = r_buf + 2048; + r_iov[1].iov_len = 2048; + spdk_file_readv_async(g_file, fs->sync_target.sync_io_channel, + r_iov, 2, 0, 4096, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(memcmp(r_buf, w_buf, sizeof(r_buf)) == 0); + + /* Overwrite file with block aligned */ + g_fserrno = 1; + memset(w_buf, 0x6a, sizeof(w_buf)); + w_iov[0].iov_base = w_buf; + w_iov[0].iov_len = 2048; + w_iov[1].iov_base = w_buf + 2048; + w_iov[1].iov_len = 2048; + spdk_file_writev_async(g_file, fs->sync_target.sync_io_channel, + w_iov, 2, 0, 4096, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_file->length == 4096); + + /* Read file to verify the overwritten data */ + g_fserrno = 1; + memset(r_buf, 0x0, sizeof(r_buf)); + r_iov[0].iov_base = r_buf; + r_iov[0].iov_len = 2048; + r_iov[1].iov_base = r_buf + 2048; + r_iov[1].iov_len = 2048; + spdk_file_readv_async(g_file, fs->sync_target.sync_io_channel, + r_iov, 2, 0, 4096, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(memcmp(r_buf, w_buf, sizeof(r_buf)) == 0); + + g_fserrno = 1; + spdk_file_close_async(g_file, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); +} + +static void +tree_find_buffer_ut(void) +{ + struct cache_tree *root; + struct cache_tree *level1_0; + struct cache_tree *level0_0_0; + struct cache_tree *level0_0_12; + struct cache_buffer *leaf_0_0_4; + struct cache_buffer *leaf_0_12_8; + struct cache_buffer *leaf_9_23_15; + struct cache_buffer *buffer; + + level1_0 = calloc(1, sizeof(struct cache_tree)); + SPDK_CU_ASSERT_FATAL(level1_0 != NULL); + level0_0_0 = calloc(1, sizeof(struct cache_tree)); + SPDK_CU_ASSERT_FATAL(level0_0_0 != NULL); + level0_0_12 = calloc(1, sizeof(struct cache_tree)); + SPDK_CU_ASSERT_FATAL(level0_0_12 != NULL); + leaf_0_0_4 = calloc(1, sizeof(struct cache_buffer)); + SPDK_CU_ASSERT_FATAL(leaf_0_0_4 != NULL); + leaf_0_12_8 = calloc(1, sizeof(struct cache_buffer)); + SPDK_CU_ASSERT_FATAL(leaf_0_12_8 != NULL); + leaf_9_23_15 = calloc(1, sizeof(struct cache_buffer)); + SPDK_CU_ASSERT_FATAL(leaf_9_23_15 != NULL); + + level1_0->level = 1; + level0_0_0->level = 0; + level0_0_12->level = 0; + + leaf_0_0_4->offset = CACHE_BUFFER_SIZE * 4; + level0_0_0->u.buffer[4] = leaf_0_0_4; + level0_0_0->present_mask |= (1ULL << 4); + + leaf_0_12_8->offset = CACHE_TREE_LEVEL_SIZE(1) * 12 + CACHE_BUFFER_SIZE * 8; + level0_0_12->u.buffer[8] = leaf_0_12_8; + level0_0_12->present_mask |= (1ULL << 8); + + level1_0->u.tree[0] = level0_0_0; + level1_0->present_mask |= (1ULL << 0); + level1_0->u.tree[12] = level0_0_12; + level1_0->present_mask |= (1ULL << 12); + + buffer = tree_find_buffer(NULL, 0); + CU_ASSERT(buffer == NULL); + + buffer = tree_find_buffer(level0_0_0, 0); + CU_ASSERT(buffer == NULL); + + buffer = tree_find_buffer(level0_0_0, CACHE_TREE_LEVEL_SIZE(0) + 1); + CU_ASSERT(buffer == NULL); + + buffer = tree_find_buffer(level0_0_0, leaf_0_0_4->offset); + CU_ASSERT(buffer == leaf_0_0_4); + + buffer = tree_find_buffer(level1_0, leaf_0_0_4->offset); + CU_ASSERT(buffer == leaf_0_0_4); + + buffer = tree_find_buffer(level1_0, leaf_0_12_8->offset); + CU_ASSERT(buffer == leaf_0_12_8); + + buffer = tree_find_buffer(level1_0, leaf_0_12_8->offset + CACHE_BUFFER_SIZE - 1); + CU_ASSERT(buffer == leaf_0_12_8); + + buffer = tree_find_buffer(level1_0, leaf_0_12_8->offset - 1); + CU_ASSERT(buffer == NULL); + + leaf_9_23_15->offset = CACHE_TREE_LEVEL_SIZE(2) * 9 + + CACHE_TREE_LEVEL_SIZE(1) * 23 + + CACHE_BUFFER_SIZE * 15; + root = tree_insert_buffer(level1_0, leaf_9_23_15); + CU_ASSERT(root != level1_0); + buffer = tree_find_buffer(root, leaf_9_23_15->offset); + CU_ASSERT(buffer == leaf_9_23_15); + tree_free_buffers(root); + free(root); +} + +static void +channel_ops(void) +{ + struct spdk_filesystem *fs; + struct spdk_bs_dev *dev; + struct spdk_io_channel *channel; + + dev = init_dev(); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + channel = spdk_fs_alloc_io_channel(fs); + CU_ASSERT(channel != NULL); + + spdk_fs_free_io_channel(channel); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + g_fs = NULL; +} + +static void +channel_ops_sync(void) +{ + struct spdk_filesystem *fs; + struct spdk_bs_dev *dev; + struct spdk_fs_thread_ctx *channel; + + dev = init_dev(); + + spdk_fs_init(dev, NULL, NULL, fs_op_with_handle_complete, NULL); + fs_poll_threads(); + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + CU_ASSERT(g_fserrno == 0); + fs = g_fs; + SPDK_CU_ASSERT_FATAL(fs->bs->dev == dev); + + channel = spdk_fs_alloc_thread_ctx(fs); + CU_ASSERT(channel != NULL); + + spdk_fs_free_thread_ctx(channel); + + g_fserrno = 1; + spdk_fs_unload(fs, fs_op_complete, NULL); + fs_poll_threads(); + CU_ASSERT(g_fserrno == 0); + g_fs = NULL; +} + +int main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("blobfs_async_ut", NULL, NULL); + + CU_ADD_TEST(suite, fs_init); + CU_ADD_TEST(suite, fs_open); + CU_ADD_TEST(suite, fs_create); + CU_ADD_TEST(suite, fs_truncate); + CU_ADD_TEST(suite, fs_rename); + CU_ADD_TEST(suite, fs_rw_async); + CU_ADD_TEST(suite, fs_writev_readv_async); + CU_ADD_TEST(suite, tree_find_buffer_ut); + CU_ADD_TEST(suite, channel_ops); + CU_ADD_TEST(suite, channel_ops_sync); + + allocate_threads(1); + set_thread(0); + + g_dev_buffer = calloc(1, DEV_BUFFER_SIZE); + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + free(g_dev_buffer); + + free_threads(); + + return num_failures; +} diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/.gitignore b/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/.gitignore new file mode 100644 index 000000000..0d29199be --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/.gitignore @@ -0,0 +1 @@ +blobfs_bdev_ut diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/Makefile b/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/Makefile new file mode 100644 index 000000000..b2d666b1b --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/Makefile @@ -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 = blobfs_bdev_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c b/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c new file mode 100644 index 000000000..425b29882 --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c @@ -0,0 +1,348 @@ +/*- + * 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 "spdk/stdinc.h" + +#include "blobfs/bdev/blobfs_bdev.c" + +int g_fserrno; + +bool g_bdev_open_ext_fail = false; +bool g_bdev_create_bs_dev_from_desc_fail = false; +bool g_fs_load_fail = false; +bool g_fs_unload_fail = false; +bool g_bs_bdev_claim_fail = false; +bool g_blobfs_fuse_start_fail = false; +struct blobfs_bdev_operation_ctx *g_fs_ctx; + +const char *g_bdev_name = "ut_bdev"; + +int +spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event_cb, + void *event_ctx, struct spdk_bdev_desc **_desc) +{ + if (g_bdev_open_ext_fail) { + return -1; + } + + return 0; +} + +static void +bs_dev_destroy(struct spdk_bs_dev *dev) +{ +} + +struct spdk_bs_dev * +spdk_bdev_create_bs_dev_from_desc(struct spdk_bdev_desc *desc) +{ + static struct spdk_bs_dev bs_dev; + + if (g_bdev_create_bs_dev_from_desc_fail) { + return NULL; + } + + bs_dev.destroy = bs_dev_destroy; + return &bs_dev; +} + +void +spdk_fs_load(struct spdk_bs_dev *dev, fs_send_request_fn send_request_fn, + spdk_fs_op_with_handle_complete cb_fn, void *cb_arg) +{ + int rc = 0; + + if (g_fs_load_fail) { + rc = -1; + } + + cb_fn(cb_arg, NULL, rc); + + return; +} + +void +spdk_fs_unload(struct spdk_filesystem *fs, spdk_fs_op_complete cb_fn, void *cb_arg) +{ + int rc = 0; + + if (g_fs_unload_fail) { + rc = -1; + } + + cb_fn(cb_arg, rc); + return; +} + +void +spdk_fs_init(struct spdk_bs_dev *dev, struct spdk_blobfs_opts *opt, + fs_send_request_fn send_request_fn, + spdk_fs_op_with_handle_complete cb_fn, void *cb_arg) +{ + int rc = 0; + + if (g_fs_load_fail) { + rc = -1; + } + + cb_fn(cb_arg, NULL, rc); + return; +} + +int +spdk_bs_bdev_claim(struct spdk_bs_dev *bs_dev, struct spdk_bdev_module *module) +{ + if (g_bs_bdev_claim_fail) { + return -1; + } + + return 0; +} + +int +blobfs_fuse_start(const char *bdev_name, const char *mountpoint, struct spdk_filesystem *fs, + blobfs_fuse_unmount_cb cb_fn, void *cb_arg, struct spdk_blobfs_fuse **_bfuse) +{ + if (g_blobfs_fuse_start_fail) { + return -1; + } + + /* store the ctx for unmount operation */ + g_fs_ctx = cb_arg; + + return 0; +} + +void +spdk_bdev_close(struct spdk_bdev_desc *desc) +{ +} + +int +spdk_thread_send_msg(const struct spdk_thread *thread, spdk_msg_fn fn, void *ctx) +{ + fn(ctx); + return 0; +} + +struct spdk_thread * +spdk_get_thread(void) +{ + struct spdk_thread *thd = (struct spdk_thread *)0x1; + + return thd; +} + +const char * +spdk_bdev_get_name(const struct spdk_bdev *bdev) +{ + return g_bdev_name; +} + +void +spdk_fs_opts_init(struct spdk_blobfs_opts *opts) +{ +} + +void +blobfs_fuse_send_request(fs_request_fn fn, void *arg) +{ +} + +void +blobfs_fuse_stop(struct spdk_blobfs_fuse *bfuse) +{ +} + +static void +blobfs_bdev_op_complete(void *cb_arg, int fserrno) +{ + g_fserrno = fserrno; +} + +static void +spdk_blobfs_bdev_detect_test(void) +{ + /* spdk_bdev_open_ext() fails */ + g_bdev_open_ext_fail = true; + spdk_blobfs_bdev_detect(g_bdev_name, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_open_ext_fail = false; + + /* spdk_bdev_create_bs_dev_from_desc() fails */ + g_bdev_create_bs_dev_from_desc_fail = true; + spdk_blobfs_bdev_detect(g_bdev_name, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_create_bs_dev_from_desc_fail = false; + + /* spdk_fs_load() fails */ + g_fs_load_fail = true; + spdk_blobfs_bdev_detect(g_bdev_name, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_fs_load_fail = false; + + /* spdk_fs_unload() fails */ + g_fs_unload_fail = true; + spdk_blobfs_bdev_detect(g_bdev_name, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_fs_unload_fail = false; + + /* no fail */ + spdk_blobfs_bdev_detect(g_bdev_name, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno == 0); +} + +static void +spdk_blobfs_bdev_create_test(void) +{ + uint32_t cluster_sz = 1024 * 1024; + + /* spdk_bdev_open_ext() fails */ + g_bdev_open_ext_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_open_ext_fail = false; + + /* spdk_bdev_create_bs_dev_from_desc() fails */ + g_bdev_create_bs_dev_from_desc_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_create_bs_dev_from_desc_fail = false; + + /* spdk_bs_bdev_claim() fails */ + g_bs_bdev_claim_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bs_bdev_claim_fail = false; + + /* spdk_fs_init() fails */ + g_fs_load_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_fs_load_fail = false; + + /* spdk_fs_unload() fails */ + g_fs_unload_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_fs_unload_fail = false; + + /* no fail */ + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno == 0); +} + +static void +spdk_blobfs_bdev_mount_test(void) +{ +#ifdef SPDK_CONFIG_FUSE + const char *mountpoint = "/mnt"; + + /* spdk_bdev_open_ext() fails */ + g_bdev_open_ext_fail = true; + spdk_blobfs_bdev_mount(g_bdev_name, mountpoint, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_open_ext_fail = false; + + /* spdk_bdev_create_bs_dev_from_desc() fails */ + g_bdev_create_bs_dev_from_desc_fail = true; + spdk_blobfs_bdev_mount(g_bdev_name, mountpoint, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_create_bs_dev_from_desc_fail = false; + + /* spdk_bs_bdev_claim() fails */ + g_bs_bdev_claim_fail = true; + spdk_blobfs_bdev_mount(g_bdev_name, mountpoint, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bs_bdev_claim_fail = false; + + /* spdk_fs_load() fails */ + g_fs_load_fail = true; + spdk_blobfs_bdev_mount(g_bdev_name, mountpoint, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_fs_load_fail = false; + + /* blobfs_fuse_start() fails */ + g_blobfs_fuse_start_fail = true; + spdk_blobfs_bdev_mount(g_bdev_name, mountpoint, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_blobfs_fuse_start_fail = false; + + /* no fail */ + spdk_blobfs_bdev_mount(g_bdev_name, mountpoint, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno == 0); + CU_ASSERT(g_fs_ctx != NULL); + + /* after mount operation success , we need make sure unmount operation success */ + blobfs_bdev_unmount(g_fs_ctx); + CU_ASSERT(g_fserrno == 0); +#endif +} + +int main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("blobfs_bdev_ut", NULL, NULL); + + CU_ADD_TEST(suite, spdk_blobfs_bdev_detect_test); + CU_ADD_TEST(suite, spdk_blobfs_bdev_create_test); + CU_ADD_TEST(suite, spdk_blobfs_bdev_mount_test); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/.gitignore b/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/.gitignore new file mode 100644 index 000000000..93ef643ff --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/.gitignore @@ -0,0 +1 @@ +blobfs_sync_ut diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/Makefile b/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/Makefile new file mode 100644 index 000000000..31961be12 --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/Makefile @@ -0,0 +1,39 @@ +# +# 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)/../../../../..) + +SPDK_LIB_LIST = blob +TEST_FILE = blobfs_sync_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/blobfs_sync_ut.c b/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/blobfs_sync_ut.c new file mode 100644 index 000000000..f9d00226c --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/blobfs_sync_ut/blobfs_sync_ut.c @@ -0,0 +1,703 @@ +/*- + * 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/blobfs.h" +#include "spdk/env.h" +#include "spdk/log.h" +#include "spdk/thread.h" +#include "spdk/barrier.h" +#include "spdk_internal/thread.h" + +#include "spdk_cunit.h" +#include "unit/lib/blob/bs_dev_common.c" +#include "common/lib/test_env.c" +#include "blobfs/blobfs.c" +#include "blobfs/tree.c" + +struct spdk_filesystem *g_fs; +struct spdk_file *g_file; +int g_fserrno; +struct spdk_thread *g_dispatch_thread = NULL; +struct spdk_trace_histories *g_trace_histories; +DEFINE_STUB_V(spdk_trace_add_register_fn, (struct spdk_trace_register_fn *reg_fn)); +DEFINE_STUB_V(spdk_trace_register_description, (const char *name, + uint16_t tpoint_id, uint8_t owner_type, + uint8_t object_type, uint8_t new_object, + uint8_t arg1_is_ptr, const char *arg1_name)); +DEFINE_STUB_V(_spdk_trace_record, (uint64_t tsc, uint16_t tpoint_id, uint16_t poller_id, + uint32_t size, uint64_t object_id, uint64_t arg1)); + +/* Return NULL to test hardcoded defaults. */ +struct spdk_conf_section * +spdk_conf_find_section(struct spdk_conf *cp, const char *name) +{ + return NULL; +} + +/* Return -1 to test hardcoded defaults. */ +int +spdk_conf_section_get_intval(struct spdk_conf_section *sp, const char *key) +{ + return -1; +} + +struct ut_request { + fs_request_fn fn; + void *arg; + volatile int done; +}; + +static void +send_request(fs_request_fn fn, void *arg) +{ + spdk_thread_send_msg(g_dispatch_thread, (spdk_msg_fn)fn, arg); +} + +static void +ut_call_fn(void *arg) +{ + struct ut_request *req = arg; + + req->fn(req->arg); + req->done = 1; +} + +static void +ut_send_request(fs_request_fn fn, void *arg) +{ + struct ut_request req; + + req.fn = fn; + req.arg = arg; + req.done = 0; + + spdk_thread_send_msg(g_dispatch_thread, ut_call_fn, &req); + + /* Wait for this to finish */ + while (req.done == 0) { } +} + +static void +fs_op_complete(void *ctx, int fserrno) +{ + g_fserrno = fserrno; +} + +static void +fs_op_with_handle_complete(void *ctx, struct spdk_filesystem *fs, int fserrno) +{ + g_fs = fs; + g_fserrno = fserrno; +} + +static void +fs_thread_poll(void) +{ + struct spdk_thread *thread; + + thread = spdk_get_thread(); + while (spdk_thread_poll(thread, 0, 0) > 0) {} + while (spdk_thread_poll(g_cache_pool_thread, 0, 0) > 0) {} +} + +static void +_fs_init(void *arg) +{ + struct spdk_bs_dev *dev; + + g_fs = NULL; + g_fserrno = -1; + dev = init_dev(); + spdk_fs_init(dev, NULL, send_request, fs_op_with_handle_complete, NULL); + + fs_thread_poll(); + + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + SPDK_CU_ASSERT_FATAL(g_fs->bdev == dev); + CU_ASSERT(g_fserrno == 0); +} + +static void +_fs_load(void *arg) +{ + struct spdk_bs_dev *dev; + + g_fs = NULL; + g_fserrno = -1; + dev = init_dev(); + spdk_fs_load(dev, send_request, fs_op_with_handle_complete, NULL); + + fs_thread_poll(); + + SPDK_CU_ASSERT_FATAL(g_fs != NULL); + SPDK_CU_ASSERT_FATAL(g_fs->bdev == dev); + CU_ASSERT(g_fserrno == 0); +} + +static void +_fs_unload(void *arg) +{ + g_fserrno = -1; + spdk_fs_unload(g_fs, fs_op_complete, NULL); + + fs_thread_poll(); + + CU_ASSERT(g_fserrno == 0); + g_fs = NULL; +} + +static void +_nop(void *arg) +{ +} + +static void +cache_read_after_write(void) +{ + uint64_t length; + int rc; + char w_buf[100], r_buf[100]; + struct spdk_fs_thread_ctx *channel; + struct spdk_file_stat stat = {0}; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + length = (4 * 1024 * 1024); + rc = spdk_file_truncate(g_file, channel, length); + CU_ASSERT(rc == 0); + + memset(w_buf, 0x5a, sizeof(w_buf)); + spdk_file_write(g_file, channel, w_buf, 0, sizeof(w_buf)); + + CU_ASSERT(spdk_file_get_length(g_file) == length); + + rc = spdk_file_truncate(g_file, channel, sizeof(w_buf)); + CU_ASSERT(rc == 0); + + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); + CU_ASSERT(rc == 0); + CU_ASSERT(sizeof(w_buf) == stat.size); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + memset(r_buf, 0, sizeof(r_buf)); + spdk_file_read(g_file, channel, r_buf, 0, sizeof(r_buf)); + CU_ASSERT(memcmp(w_buf, r_buf, sizeof(r_buf)) == 0); + + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == -ENOENT); + + spdk_fs_free_thread_ctx(channel); + + ut_send_request(_fs_unload, NULL); +} + +static void +file_length(void) +{ + int rc; + char *buf; + uint64_t buf_length; + volatile uint64_t *length_flushed; + struct spdk_fs_thread_ctx *channel; + struct spdk_file_stat stat = {0}; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + + g_file = NULL; + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + /* Write one CACHE_BUFFER. Filling at least one cache buffer triggers + * a flush to disk. + */ + buf_length = CACHE_BUFFER_SIZE; + buf = calloc(1, buf_length); + spdk_file_write(g_file, channel, buf, 0, buf_length); + free(buf); + + /* Spin until all of the data has been flushed to the SSD. There's been no + * sync operation yet, so the xattr on the file is still 0. + * + * length_flushed: This variable is modified by a different thread in this unit + * test. So we need to dereference it as a volatile to ensure the value is always + * re-read. + */ + length_flushed = &g_file->length_flushed; + while (*length_flushed != buf_length) {} + + /* Close the file. This causes an implicit sync which should write the + * length_flushed value as the "length" xattr on the file. + */ + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); + CU_ASSERT(rc == 0); + CU_ASSERT(buf_length == stat.size); + + spdk_fs_free_thread_ctx(channel); + + /* Unload and reload the filesystem. The file length will be + * read during load from the length xattr. We want to make sure + * it matches what was written when the file was originally + * written and closed. + */ + ut_send_request(_fs_unload, NULL); + + ut_send_request(_fs_load, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + + rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); + CU_ASSERT(rc == 0); + CU_ASSERT(buf_length == stat.size); + + g_file = NULL; + rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + + spdk_fs_free_thread_ctx(channel); + + ut_send_request(_fs_unload, NULL); +} + +static void +append_write_to_extend_blob(void) +{ + uint64_t blob_size, buf_length; + char *buf, append_buf[64]; + int rc; + struct spdk_fs_thread_ctx *channel; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + + /* create a file and write the file with blob_size - 1 data length */ + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + blob_size = __file_get_blob_size(g_file); + + buf_length = blob_size - 1; + buf = calloc(1, buf_length); + rc = spdk_file_write(g_file, channel, buf, 0, buf_length); + CU_ASSERT(rc == 0); + free(buf); + + spdk_file_close(g_file, channel); + fs_thread_poll(); + spdk_fs_free_thread_ctx(channel); + ut_send_request(_fs_unload, NULL); + + /* load existing file and write extra 2 bytes to cross blob boundary */ + ut_send_request(_fs_load, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + g_file = NULL; + rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + CU_ASSERT(g_file->length == buf_length); + CU_ASSERT(g_file->last == NULL); + CU_ASSERT(g_file->append_pos == buf_length); + + rc = spdk_file_write(g_file, channel, append_buf, buf_length, 2); + CU_ASSERT(rc == 0); + CU_ASSERT(2 * blob_size == __file_get_blob_size(g_file)); + spdk_file_close(g_file, channel); + fs_thread_poll(); + CU_ASSERT(g_file->length == buf_length + 2); + + spdk_fs_free_thread_ctx(channel); + ut_send_request(_fs_unload, NULL); +} + +static void +partial_buffer(void) +{ + int rc; + char *buf; + uint64_t buf_length; + struct spdk_fs_thread_ctx *channel; + struct spdk_file_stat stat = {0}; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + + g_file = NULL; + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + /* Write one CACHE_BUFFER plus one byte. Filling at least one cache buffer triggers + * a flush to disk. We want to make sure the extra byte is not implicitly flushed. + * It should only get flushed once we sync or close the file. + */ + buf_length = CACHE_BUFFER_SIZE + 1; + buf = calloc(1, buf_length); + spdk_file_write(g_file, channel, buf, 0, buf_length); + free(buf); + + /* Send some nop messages to the dispatch thread. This will ensure any of the + * pending write operations are completed. A well-functioning blobfs should only + * issue one write for the filled CACHE_BUFFER - a buggy one might try to write + * the extra byte. So do a bunch of _nops to make sure all of them (even the buggy + * ones) get a chance to run. Note that we can't just send a message to the + * dispatch thread to call spdk_thread_poll() because the messages are themselves + * run in the context of spdk_thread_poll(). + */ + ut_send_request(_nop, NULL); + ut_send_request(_nop, NULL); + ut_send_request(_nop, NULL); + ut_send_request(_nop, NULL); + ut_send_request(_nop, NULL); + ut_send_request(_nop, NULL); + + CU_ASSERT(g_file->length_flushed == CACHE_BUFFER_SIZE); + + /* Close the file. This causes an implicit sync which should write the + * length_flushed value as the "length" xattr on the file. + */ + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); + CU_ASSERT(rc == 0); + CU_ASSERT(buf_length == stat.size); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + + spdk_fs_free_thread_ctx(channel); + + ut_send_request(_fs_unload, NULL); +} + +static void +cache_write_null_buffer(void) +{ + uint64_t length; + int rc; + struct spdk_fs_thread_ctx *channel; + struct spdk_thread *thread; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + length = 0; + rc = spdk_file_truncate(g_file, channel, length); + CU_ASSERT(rc == 0); + + rc = spdk_file_write(g_file, channel, NULL, 0, 0); + CU_ASSERT(rc == 0); + + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + + spdk_fs_free_thread_ctx(channel); + + thread = spdk_get_thread(); + while (spdk_thread_poll(thread, 0, 0) > 0) {} + + ut_send_request(_fs_unload, NULL); +} + +static void +fs_create_sync(void) +{ + int rc; + struct spdk_fs_thread_ctx *channel; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + CU_ASSERT(channel != NULL); + + rc = spdk_fs_create_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + + /* Create should fail, because the file already exists. */ + rc = spdk_fs_create_file(g_fs, channel, "testfile"); + CU_ASSERT(rc != 0); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + + spdk_fs_free_thread_ctx(channel); + + fs_thread_poll(); + + ut_send_request(_fs_unload, NULL); +} + +static void +fs_rename_sync(void) +{ + int rc; + struct spdk_fs_thread_ctx *channel; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + CU_ASSERT(channel != NULL); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + CU_ASSERT(strcmp(spdk_file_get_name(g_file), "testfile") == 0); + + rc = spdk_fs_rename_file(g_fs, channel, "testfile", "newtestfile"); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(spdk_file_get_name(g_file), "newtestfile") == 0); + + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + spdk_fs_free_thread_ctx(channel); + + ut_send_request(_fs_unload, NULL); +} + +static void +cache_append_no_cache(void) +{ + int rc; + char buf[100]; + struct spdk_fs_thread_ctx *channel; + + ut_send_request(_fs_init, NULL); + + channel = spdk_fs_alloc_thread_ctx(g_fs); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + spdk_file_write(g_file, channel, buf, 0 * sizeof(buf), sizeof(buf)); + CU_ASSERT(spdk_file_get_length(g_file) == 1 * sizeof(buf)); + spdk_file_write(g_file, channel, buf, 1 * sizeof(buf), sizeof(buf)); + CU_ASSERT(spdk_file_get_length(g_file) == 2 * sizeof(buf)); + spdk_file_sync(g_file, channel); + + fs_thread_poll(); + + spdk_file_write(g_file, channel, buf, 2 * sizeof(buf), sizeof(buf)); + CU_ASSERT(spdk_file_get_length(g_file) == 3 * sizeof(buf)); + spdk_file_write(g_file, channel, buf, 3 * sizeof(buf), sizeof(buf)); + CU_ASSERT(spdk_file_get_length(g_file) == 4 * sizeof(buf)); + spdk_file_write(g_file, channel, buf, 4 * sizeof(buf), sizeof(buf)); + CU_ASSERT(spdk_file_get_length(g_file) == 5 * sizeof(buf)); + + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + + spdk_fs_free_thread_ctx(channel); + + ut_send_request(_fs_unload, NULL); +} + +static void +fs_delete_file_without_close(void) +{ + int rc; + struct spdk_fs_thread_ctx *channel; + struct spdk_file *file; + + ut_send_request(_fs_init, NULL); + channel = spdk_fs_alloc_thread_ctx(g_fs); + CU_ASSERT(channel != NULL); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(g_file != NULL); + + rc = spdk_fs_delete_file(g_fs, channel, "testfile"); + CU_ASSERT(rc == 0); + CU_ASSERT(g_file->ref_count != 0); + CU_ASSERT(g_file->is_deleted == true); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); + CU_ASSERT(rc != 0); + + spdk_file_close(g_file, channel); + + fs_thread_poll(); + + rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); + CU_ASSERT(rc != 0); + + spdk_fs_free_thread_ctx(channel); + + ut_send_request(_fs_unload, NULL); + +} + +static bool g_thread_exit = false; + +static void +terminate_spdk_thread(void *arg) +{ + g_thread_exit = true; +} + +static void * +spdk_thread(void *arg) +{ + struct spdk_thread *thread = arg; + + spdk_set_thread(thread); + + while (!g_thread_exit) { + spdk_thread_poll(thread, 0, 0); + } + + return NULL; +} + +int main(int argc, char **argv) +{ + struct spdk_thread *thread; + CU_pSuite suite = NULL; + pthread_t spdk_tid; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("blobfs_sync_ut", NULL, NULL); + + CU_ADD_TEST(suite, cache_read_after_write); + CU_ADD_TEST(suite, file_length); + CU_ADD_TEST(suite, append_write_to_extend_blob); + CU_ADD_TEST(suite, partial_buffer); + CU_ADD_TEST(suite, cache_write_null_buffer); + CU_ADD_TEST(suite, fs_create_sync); + CU_ADD_TEST(suite, fs_rename_sync); + CU_ADD_TEST(suite, cache_append_no_cache); + CU_ADD_TEST(suite, fs_delete_file_without_close); + + spdk_thread_lib_init(NULL, 0); + + thread = spdk_thread_create("test_thread", NULL); + spdk_set_thread(thread); + + g_dispatch_thread = spdk_thread_create("dispatch_thread", NULL); + pthread_create(&spdk_tid, NULL, spdk_thread, g_dispatch_thread); + + g_dev_buffer = calloc(1, DEV_BUFFER_SIZE); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + free(g_dev_buffer); + + ut_send_request(terminate_spdk_thread, NULL); + pthread_join(spdk_tid, NULL); + + while (spdk_thread_poll(g_dispatch_thread, 0, 0) > 0) {} + while (spdk_thread_poll(thread, 0, 0) > 0) {} + + spdk_set_thread(thread); + spdk_thread_exit(thread); + while (!spdk_thread_is_exited(thread)) { + spdk_thread_poll(thread, 0, 0); + } + spdk_thread_destroy(thread); + + spdk_set_thread(g_dispatch_thread); + spdk_thread_exit(g_dispatch_thread); + while (!spdk_thread_is_exited(g_dispatch_thread)) { + spdk_thread_poll(g_dispatch_thread, 0, 0); + } + spdk_thread_destroy(g_dispatch_thread); + + spdk_thread_lib_fini(); + + return num_failures; +} diff --git a/src/spdk/test/unit/lib/blobfs/tree.c/.gitignore b/src/spdk/test/unit/lib/blobfs/tree.c/.gitignore new file mode 100644 index 000000000..57e77bf71 --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/tree.c/.gitignore @@ -0,0 +1 @@ +tree_ut diff --git a/src/spdk/test/unit/lib/blobfs/tree.c/Makefile b/src/spdk/test/unit/lib/blobfs/tree.c/Makefile new file mode 100644 index 000000000..b3d57e873 --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/tree.c/Makefile @@ -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 = tree_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/blobfs/tree.c/tree_ut.c b/src/spdk/test/unit/lib/blobfs/tree.c/tree_ut.c new file mode 100644 index 000000000..70f1d692a --- /dev/null +++ b/src/spdk/test/unit/lib/blobfs/tree.c/tree_ut.c @@ -0,0 +1,150 @@ +/*- + * 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 "blobfs/tree.c" + +void +cache_buffer_free(struct cache_buffer *cache_buffer) +{ + free(cache_buffer); +} + +static void +blobfs_tree_op_test(void) +{ + struct cache_tree *tree; + struct cache_buffer *buffer[5]; + struct cache_buffer *tmp_buffer; + int i; + + for (i = 0; i < 5; i ++) { + buffer[i] = calloc(1, sizeof(struct cache_buffer)); + SPDK_CU_ASSERT_FATAL(buffer[i]); + } + + tree = calloc(1, sizeof(*tree)); + SPDK_CU_ASSERT_FATAL(tree != NULL); + + /* insert buffer[0] */ + buffer[0]->offset = 0; + tree = tree_insert_buffer(tree, buffer[0]); + SPDK_CU_ASSERT_FATAL(tree != NULL); + CU_ASSERT(tree->level == 0); + tmp_buffer = tree_find_buffer(tree, buffer[0]->offset); + CU_ASSERT(tmp_buffer == buffer[0]); + + /* insert buffer[1] */ + buffer[1]->offset = CACHE_BUFFER_SIZE; + /* set the bytes_filled equal = bytes_filled with same non zero value, e.g., 32 */ + buffer[1]->bytes_filled = buffer[1]->bytes_flushed = 32; + tree = tree_insert_buffer(tree, buffer[1]); + SPDK_CU_ASSERT_FATAL(tree != NULL); + CU_ASSERT(tree->level == 0); + tmp_buffer = tree_find_filled_buffer(tree, buffer[1]->offset); + CU_ASSERT(tmp_buffer == buffer[1]); + + /* insert buffer[2] */ + buffer[2]->offset = (CACHE_TREE_WIDTH - 1) * CACHE_BUFFER_SIZE; + tree = tree_insert_buffer(tree, buffer[2]); + SPDK_CU_ASSERT_FATAL(tree != NULL); + CU_ASSERT(tree->level == 0); + tmp_buffer = tree_find_buffer(tree, buffer[2]->offset); + CU_ASSERT(tmp_buffer == buffer[2]); + tmp_buffer = tree_find_filled_buffer(tree, buffer[2]->offset); + CU_ASSERT(tmp_buffer == NULL); + + /* insert buffer[3], set an offset which can not be fit level 0 */ + buffer[3]->offset = CACHE_TREE_LEVEL_SIZE(1); + tree = tree_insert_buffer(tree, buffer[3]); + SPDK_CU_ASSERT_FATAL(tree != NULL); + CU_ASSERT(tree->level == 1); + tmp_buffer = tree_find_buffer(tree, buffer[3]->offset); + CU_ASSERT(tmp_buffer == buffer[3]); + + /* insert buffer[4], set an offset which can not be fit level 1 */ + buffer[4]->offset = CACHE_TREE_LEVEL_SIZE(2); + tree = tree_insert_buffer(tree, buffer[4]); + SPDK_CU_ASSERT_FATAL(tree != NULL); + CU_ASSERT(tree->level == 2); + tmp_buffer = tree_find_buffer(tree, buffer[4]->offset); + CU_ASSERT(tmp_buffer == buffer[4]); + + /* delete buffer[0] */ + tree_remove_buffer(tree, buffer[0]); + /* check whether buffer[0] is still existed or not */ + tmp_buffer = tree_find_buffer(tree, 0); + CU_ASSERT(tmp_buffer == NULL); + + /* delete buffer[3] */ + tree_remove_buffer(tree, buffer[3]); + /* check whether buffer[3] is still existed or not */ + tmp_buffer = tree_find_buffer(tree, CACHE_TREE_LEVEL_SIZE(1)); + CU_ASSERT(tmp_buffer == NULL); + + /* free all buffers in the tree */ + tree_free_buffers(tree); + + /* check whether buffer[1] is still existed or not */ + tmp_buffer = tree_find_buffer(tree, CACHE_BUFFER_SIZE); + CU_ASSERT(tmp_buffer == NULL); + /* check whether buffer[2] is still existed or not */ + tmp_buffer = tree_find_buffer(tree, (CACHE_TREE_WIDTH - 1) * CACHE_BUFFER_SIZE); + CU_ASSERT(tmp_buffer == NULL); + /* check whether buffer[4] is still existed or not */ + tmp_buffer = tree_find_buffer(tree, CACHE_TREE_LEVEL_SIZE(2)); + CU_ASSERT(tmp_buffer == NULL); + + /* According to tree_free_buffers, root will not be freed */ + free(tree); +} + +int main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("tree", NULL, NULL); + CU_ADD_TEST(suite, blobfs_tree_op_test); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} |