diff --git a/lib/ftl/ftl_core.c b/lib/ftl/ftl_core.c index b099fafc5..c830605cf 100644 --- a/lib/ftl/ftl_core.c +++ b/lib/ftl/ftl_core.c @@ -1340,7 +1340,7 @@ _spdk_ftl_write(struct ftl_io *io) rc = ftl_io_write(io); if (rc == -EAGAIN) { - spdk_thread_send_msg(spdk_io_channel_get_thread(io->ch), + spdk_thread_send_msg(spdk_io_channel_get_thread(io->ioch), _ftl_write, io); return 0; } diff --git a/lib/ftl/ftl_io.c b/lib/ftl/ftl_io.c index 4b952b04d..f4979efb6 100644 --- a/lib/ftl/ftl_io.c +++ b/lib/ftl/ftl_io.c @@ -33,6 +33,7 @@ #include "spdk/stdinc.h" #include "spdk/ftl.h" +#include "spdk/likely.h" #include "ftl_io.h" #include "ftl_core.h" @@ -190,7 +191,12 @@ ftl_io_init_internal(const struct ftl_io_init_opts *opts) struct spdk_ftl_dev *dev = opts->dev; if (!io) { - io = ftl_io_alloc(dev->ioch); + if (opts->parent) { + io = ftl_io_alloc_child(opts->parent); + } else { + io = ftl_io_alloc(dev->ioch); + } + if (!io) { return NULL; } @@ -289,19 +295,87 @@ ftl_io_user_init(struct spdk_ftl_dev *dev, struct ftl_io *io, uint64_t lba, size ftl_trace_lba_io_init(io->dev, io); } +static void +_ftl_io_free(struct ftl_io *io) +{ + struct ftl_io_channel *ioch; + + assert(LIST_EMPTY(&io->children)); + + if ((io->flags & FTL_IO_INTERNAL) && io->iov_cnt > 1) { + free(io->iovs); + } + + if (pthread_spin_destroy(&io->lock)) { + SPDK_ERRLOG("pthread_spin_destroy failed\n"); + } + + ioch = spdk_io_channel_get_ctx(io->ioch); + spdk_mempool_put(ioch->io_pool, io); +} + +static bool +ftl_io_remove_child(struct ftl_io *io) +{ + struct ftl_io *parent = io->parent; + bool parent_done; + + pthread_spin_lock(&parent->lock); + LIST_REMOVE(io, child_entry); + parent_done = parent->done && LIST_EMPTY(&parent->children); + parent->status = parent->status ? : io->status; + pthread_spin_unlock(&parent->lock); + + return parent_done; +} + void ftl_io_complete(struct ftl_io *io) { - int keep_alive = io->flags & FTL_IO_KEEP_ALIVE; + struct ftl_io *parent = io->parent; + bool complete, keep_alive = io->flags & FTL_IO_KEEP_ALIVE; io->flags &= ~FTL_IO_INITIALIZED; - io->cb.fn(io->cb.ctx, io->status); - if (!keep_alive) { - ftl_io_free(io); + pthread_spin_lock(&io->lock); + complete = LIST_EMPTY(&io->children); + io->done = true; + pthread_spin_unlock(&io->lock); + + if (complete) { + if (io->cb.fn) { + io->cb.fn(io->cb.ctx, io->status); + } + + if (parent && ftl_io_remove_child(io)) { + ftl_io_complete(parent); + } + + if (!keep_alive) { + _ftl_io_free(io); + } } } +struct ftl_io * +ftl_io_alloc_child(struct ftl_io *parent) +{ + struct ftl_io *io; + + io = ftl_io_alloc(parent->ioch); + if (spdk_unlikely(!io)) { + return NULL; + } + + io->parent = parent; + + pthread_spin_lock(&parent->lock); + LIST_INSERT_HEAD(&parent->children, io, child_entry); + pthread_spin_unlock(&parent->lock); + + return io; +} + void ftl_io_process_error(struct ftl_io *io, const struct spdk_nvme_cpl *status) { @@ -336,7 +410,14 @@ ftl_io_alloc(struct spdk_io_channel *ch) } memset(io, 0, ioch->elem_size); - io->ch = ch; + io->ioch = ch; + + if (pthread_spin_init(&io->lock, PTHREAD_PROCESS_PRIVATE)) { + SPDK_ERRLOG("pthread_spin_init failed\n"); + spdk_mempool_put(ioch->io_pool, io); + return NULL; + } + return io; } @@ -362,16 +443,15 @@ ftl_io_clear(struct ftl_io *io) void ftl_io_free(struct ftl_io *io) { - struct ftl_io_channel *ioch; + struct ftl_io *parent = io->parent; if (!io) { return; } - if ((io->flags & FTL_IO_INTERNAL) && io->iov_cnt > 1) { - free(io->iovs); + if (parent && ftl_io_remove_child(io)) { + ftl_io_complete(parent); } - ioch = spdk_io_channel_get_ctx(io->ch); - spdk_mempool_put(ioch->io_pool, io); + _ftl_io_free(io); } diff --git a/lib/ftl/ftl_io.h b/lib/ftl/ftl_io.h index 5f5519e87..3a61003fd 100644 --- a/lib/ftl/ftl_io.h +++ b/lib/ftl/ftl_io.h @@ -84,6 +84,9 @@ struct ftl_io_init_opts { /* IO descriptor */ struct ftl_io *io; + /* Parent request */ + struct ftl_io *parent; + /* Size of IO descriptor */ size_t size; @@ -137,7 +140,7 @@ struct ftl_io { struct spdk_ftl_dev *dev; /* IO channel */ - struct spdk_io_channel *ch; + struct spdk_io_channel *ioch; union { /* LBA table */ @@ -197,6 +200,18 @@ struct ftl_io { /* IO type */ enum ftl_io_type type; + /* Done flag */ + bool done; + + /* Parent request */ + struct ftl_io *parent; + /* Child requests list */ + LIST_HEAD(, ftl_io) children; + /* Child list link */ + LIST_ENTRY(ftl_io) child_entry; + /* Children lock */ + pthread_spinlock_t lock; + /* Trace group id */ uint64_t trace; @@ -240,6 +255,7 @@ ftl_io_done(const struct ftl_io *io) } struct ftl_io *ftl_io_alloc(struct spdk_io_channel *ch); +struct ftl_io *ftl_io_alloc_child(struct ftl_io *parent); void ftl_io_free(struct ftl_io *io); struct ftl_io *ftl_io_init_internal(const struct ftl_io_init_opts *opts); void ftl_io_reinit(struct ftl_io *io, spdk_ftl_fn cb, diff --git a/test/unit/lib/ftl/Makefile b/test/unit/lib/ftl/Makefile index 02aafbd22..9037dd901 100644 --- a/test/unit/lib/ftl/Makefile +++ b/test/unit/lib/ftl/Makefile @@ -34,7 +34,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y = ftl_rwb.c ftl_ppa ftl_band.c ftl_reloc.c ftl_wptr ftl_md +DIRS-y = ftl_rwb.c ftl_ppa ftl_band.c ftl_reloc.c ftl_wptr ftl_md ftl_io.c .PHONY: all clean $(DIRS-y) diff --git a/test/unit/lib/ftl/ftl_io.c/.gitignore b/test/unit/lib/ftl/ftl_io.c/.gitignore new file mode 100644 index 000000000..c5e09253e --- /dev/null +++ b/test/unit/lib/ftl/ftl_io.c/.gitignore @@ -0,0 +1 @@ +ftl_io_ut diff --git a/test/unit/lib/ftl/ftl_io.c/Makefile b/test/unit/lib/ftl/ftl_io.c/Makefile new file mode 100644 index 000000000..e06a186b1 --- /dev/null +++ b/test/unit/lib/ftl/ftl_io.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 = ftl_io_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c b/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c new file mode 100644 index 000000000..455b410bc --- /dev/null +++ b/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c @@ -0,0 +1,544 @@ +/*- + * 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 "common/lib/test_env.c" + +#include "ftl/ftl_io.c" + +DEFINE_STUB(ftl_trace_alloc_id, uint64_t, (struct spdk_ftl_dev *dev), 0); +DEFINE_STUB_V(ftl_band_acquire_md, (struct ftl_band *band)); +DEFINE_STUB_V(ftl_band_release_md, (struct ftl_band *band)); + +static struct spdk_ftl_dev * +setup_device(void) +{ + struct spdk_ftl_dev *dev; + struct ftl_io_channel *ioch; + + dev = calloc(1, sizeof(*dev)); + SPDK_CU_ASSERT_FATAL(dev != NULL); + dev->ioch = calloc(1, sizeof(*ioch) + sizeof(struct spdk_io_channel)); + SPDK_CU_ASSERT_FATAL(dev->ioch != NULL); + + ioch = spdk_io_channel_get_ctx(dev->ioch); + + ioch->elem_size = sizeof(struct ftl_md_io); + ioch->io_pool = spdk_mempool_create("io-pool", 4096, ioch->elem_size, 0, 0); + + SPDK_CU_ASSERT_FATAL(ioch->io_pool != NULL); + + return dev; +} + +static void +free_device(struct spdk_ftl_dev *dev) +{ + struct ftl_io_channel *ioch; + + ioch = spdk_io_channel_get_ctx(dev->ioch); + spdk_mempool_free(ioch->io_pool); + + free(dev->ioch); + free(dev); +} + +static void +setup_io(struct ftl_io *io, struct spdk_ftl_dev *dev, spdk_ftl_fn cb, void *ctx) +{ + io->dev = dev; + io->cb.fn = cb; + io->cb.ctx = ctx; +} + +static struct ftl_io * +alloc_io(struct spdk_ftl_dev *dev, spdk_ftl_fn cb, void *ctx) +{ + struct ftl_io *io; + + io = ftl_io_alloc(dev->ioch); + SPDK_CU_ASSERT_FATAL(io != NULL); + setup_io(io, dev, cb, ctx); + + return io; +} + +static void +io_complete_cb(void *ctx, int status) +{ + *(int *)ctx = status; +} + +static void +test_completion(void) +{ + struct spdk_ftl_dev *dev; + struct ftl_io_channel *ioch; + struct ftl_io *io; + int req, status = 0; + size_t pool_size; + + dev = setup_device(); + ioch = spdk_io_channel_get_ctx(dev->ioch); + pool_size = spdk_mempool_count(ioch->io_pool); + + io = alloc_io(dev, io_complete_cb, &status); + io->status = -EIO; + +#define NUM_REQUESTS 16 + for (req = 0; req < NUM_REQUESTS; ++req) { + ftl_io_inc_req(io); + CU_ASSERT_FALSE(ftl_io_done(io)); + } + + CU_ASSERT_EQUAL(io->req_cnt, NUM_REQUESTS); + + for (req = 0; req < (NUM_REQUESTS - 1); ++req) { + ftl_io_dec_req(io); + CU_ASSERT_FALSE(ftl_io_done(io)); + } + + CU_ASSERT_EQUAL(io->req_cnt, 1); + + ftl_io_dec_req(io); + CU_ASSERT_TRUE(ftl_io_done(io)); + + ftl_io_complete(io); + CU_ASSERT_EQUAL(status, -EIO); + + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + free_device(dev); +} + +static void +test_alloc_free(void) +{ + struct spdk_ftl_dev *dev; + struct ftl_io_channel *ioch; + struct ftl_io *parent, *child; + int parent_status = -1; + size_t pool_size; + + dev = setup_device(); + ioch = spdk_io_channel_get_ctx(dev->ioch); + pool_size = spdk_mempool_count(ioch->io_pool); + + parent = alloc_io(dev, io_complete_cb, &parent_status); + SPDK_CU_ASSERT_FATAL(parent != NULL); + child = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child != NULL); + + ftl_io_free(child); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - 1); + + child = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child != NULL); + ftl_io_complete(child); + CU_ASSERT_EQUAL(parent_status, -1); + ftl_io_complete(parent); + CU_ASSERT_EQUAL(parent_status, 0); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + parent_status = -1; + parent = alloc_io(dev, io_complete_cb, &parent_status); + SPDK_CU_ASSERT_FATAL(parent != NULL); + child = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child != NULL); + + ftl_io_free(child); + CU_ASSERT_EQUAL(parent_status, -1); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - 1); + ftl_io_complete(parent); + CU_ASSERT_EQUAL(parent_status, 0); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + free_device(dev); +} + +static void +test_child_requests(void) +{ + struct spdk_ftl_dev *dev; + struct ftl_io_channel *ioch; +#define MAX_CHILDREN 16 + struct ftl_io *parent, *child[MAX_CHILDREN]; + int status[MAX_CHILDREN + 1], i; + size_t pool_size; + + dev = setup_device(); + ioch = spdk_io_channel_get_ctx(dev->ioch); + pool_size = spdk_mempool_count(ioch->io_pool); + + /* Verify correct behaviour when children finish first */ + parent = alloc_io(dev, io_complete_cb, &status[0]); + parent->status = 0; + + ftl_io_inc_req(parent); + status[0] = -1; + + for (i = 0; i < MAX_CHILDREN; ++i) { + status[i + 1] = -1; + + child[i] = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child[i] != NULL); + setup_io(child[i], dev, io_complete_cb, &status[i + 1]); + child[i]->status = 0; + + ftl_io_inc_req(child[i]); + } + + CU_ASSERT_FALSE(ftl_io_done(parent)); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1); + + for (i = 0; i < MAX_CHILDREN; ++i) { + CU_ASSERT_FALSE(ftl_io_done(child[i])); + ftl_io_dec_req(child[i]); + CU_ASSERT_TRUE(ftl_io_done(child[i])); + CU_ASSERT_FALSE(ftl_io_done(parent)); + + ftl_io_complete(child[i]); + CU_ASSERT_FALSE(ftl_io_done(parent)); + CU_ASSERT_EQUAL(status[i + 1], 0); + } + + CU_ASSERT_EQUAL(status[0], -1); + + ftl_io_dec_req(parent); + CU_ASSERT_EQUAL(parent->req_cnt, 0); + CU_ASSERT_TRUE(ftl_io_done(parent)); + + ftl_io_complete(parent); + CU_ASSERT_EQUAL(status[0], 0); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + + /* Verify correct behaviour when parent finishes first */ + parent = alloc_io(dev, io_complete_cb, &status[0]); + parent->status = 0; + + ftl_io_inc_req(parent); + status[0] = -1; + + for (i = 0; i < MAX_CHILDREN; ++i) { + status[i + 1] = -1; + + child[i] = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child[i] != NULL); + setup_io(child[i], dev, io_complete_cb, &status[i + 1]); + child[i]->status = 0; + + ftl_io_inc_req(child[i]); + } + + CU_ASSERT_FALSE(ftl_io_done(parent)); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1); + + ftl_io_dec_req(parent); + CU_ASSERT_TRUE(ftl_io_done(parent)); + CU_ASSERT_EQUAL(parent->req_cnt, 0); + + ftl_io_complete(parent); + CU_ASSERT_EQUAL(status[0], -1); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1); + + for (i = 0; i < MAX_CHILDREN; ++i) { + CU_ASSERT_FALSE(ftl_io_done(child[i])); + ftl_io_dec_req(child[i]); + CU_ASSERT_TRUE(ftl_io_done(child[i])); + + ftl_io_complete(child[i]); + CU_ASSERT_EQUAL(status[i + 1], 0); + } + + CU_ASSERT_EQUAL(status[0], 0); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + free_device(dev); +} + +static void +test_child_status(void) +{ + struct spdk_ftl_dev *dev; + struct ftl_io_channel *ioch; + struct ftl_io *parent, *child[2]; + int parent_status, child_status[2]; + size_t pool_size, i; + + dev = setup_device(); + ioch = spdk_io_channel_get_ctx(dev->ioch); + pool_size = spdk_mempool_count(ioch->io_pool); + + /* Verify the first error is returned by the parent */ + parent = alloc_io(dev, io_complete_cb, &parent_status); + parent->status = 0; + + for (i = 0; i < 2; ++i) { + child[i] = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child[i] != NULL); + setup_io(child[i], dev, io_complete_cb, &child_status[i]); + } + + child[0]->status = -3; + child[1]->status = -4; + + ftl_io_complete(child[1]); + ftl_io_complete(child[0]); + ftl_io_complete(parent); + + CU_ASSERT_EQUAL(child_status[0], -3); + CU_ASSERT_EQUAL(child_status[1], -4); + CU_ASSERT_EQUAL(parent_status, -4); + + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + /* Verify parent's status is kept if children finish successfully */ + parent = alloc_io(dev, io_complete_cb, &parent_status); + parent->status = -1; + + for (i = 0; i < 2; ++i) { + child[i] = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child[i] != NULL); + setup_io(child[i], dev, io_complete_cb, &child_status[i]); + } + + child[0]->status = 0; + child[1]->status = 0; + + ftl_io_complete(parent); + ftl_io_complete(child[1]); + ftl_io_complete(child[0]); + + CU_ASSERT_EQUAL(child_status[0], 0); + CU_ASSERT_EQUAL(child_status[1], 0); + CU_ASSERT_EQUAL(parent_status, -1); + + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + /* Verify parent's status is kept if children fail too */ + parent = alloc_io(dev, io_complete_cb, &parent_status); + parent->status = -1; + + for (i = 0; i < 2; ++i) { + child[i] = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child[i] != NULL); + setup_io(child[i], dev, io_complete_cb, &child_status[i]); + } + + child[0]->status = -3; + child[1]->status = -4; + + ftl_io_complete(parent); + ftl_io_complete(child[1]); + ftl_io_complete(child[0]); + + CU_ASSERT_EQUAL(child_status[0], -3); + CU_ASSERT_EQUAL(child_status[1], -4); + CU_ASSERT_EQUAL(parent_status, -1); + + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + free_device(dev); +} + +static void +test_multi_generation(void) +{ + struct spdk_ftl_dev *dev; + struct ftl_io_channel *ioch; +#define MAX_GRAND_CHILDREN 32 + struct ftl_io *parent, *child[MAX_CHILDREN], *gchild[MAX_CHILDREN * MAX_GRAND_CHILDREN]; + int parent_status, child_status[MAX_CHILDREN], gchild_status[MAX_CHILDREN * MAX_GRAND_CHILDREN]; + size_t pool_size; + int i, j; + + dev = setup_device(); + ioch = spdk_io_channel_get_ctx(dev->ioch); + pool_size = spdk_mempool_count(ioch->io_pool); + + /* Verify correct behaviour when children finish first */ + parent = alloc_io(dev, io_complete_cb, &parent_status); + parent->status = 0; + + ftl_io_inc_req(parent); + parent_status = -1; + + for (i = 0; i < MAX_CHILDREN; ++i) { + child_status[i] = -1; + + child[i] = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child[i] != NULL); + setup_io(child[i], dev, io_complete_cb, &child_status[i]); + child[i]->status = 0; + + + for (j = 0; j < MAX_GRAND_CHILDREN; ++j) { + struct ftl_io *io = ftl_io_alloc_child(child[i]); + SPDK_CU_ASSERT_FATAL(io != NULL); + + gchild[i * MAX_GRAND_CHILDREN + j] = io; + gchild_status[i * MAX_GRAND_CHILDREN + j] = -1; + setup_io(io, dev, io_complete_cb, &gchild_status[i * MAX_GRAND_CHILDREN + j]); + io->status = 0; + + ftl_io_inc_req(io); + } + + ftl_io_inc_req(child[i]); + } + + for (i = 0; i < MAX_CHILDREN; ++i) { + CU_ASSERT_FALSE(ftl_io_done(child[i])); + ftl_io_dec_req(child[i]); + CU_ASSERT_TRUE(ftl_io_done(child[i])); + + ftl_io_complete(child[i]); + CU_ASSERT_FALSE(ftl_io_done(parent)); + CU_ASSERT_EQUAL(child_status[i], -1); + + for (j = 0; j < MAX_GRAND_CHILDREN; ++j) { + struct ftl_io *io = gchild[i * MAX_GRAND_CHILDREN + j]; + + CU_ASSERT_FALSE(ftl_io_done(io)); + ftl_io_dec_req(io); + CU_ASSERT_TRUE(ftl_io_done(io)); + ftl_io_complete(io); + CU_ASSERT_EQUAL(gchild_status[i * MAX_GRAND_CHILDREN + j], 0); + } + + CU_ASSERT_EQUAL(child_status[i], 0); + } + + ftl_io_dec_req(parent); + CU_ASSERT_TRUE(ftl_io_done(parent)); + ftl_io_complete(parent); + CU_ASSERT_EQUAL(parent_status, 0); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + /* Verify correct behaviour when parents finish first */ + parent = alloc_io(dev, io_complete_cb, &parent_status); + parent->status = 0; + parent_status = -1; + + for (i = 0; i < MAX_CHILDREN; ++i) { + child_status[i] = -1; + + child[i] = ftl_io_alloc_child(parent); + SPDK_CU_ASSERT_FATAL(child[i] != NULL); + setup_io(child[i], dev, io_complete_cb, &child_status[i]); + child[i]->status = 0; + + for (j = 0; j < MAX_GRAND_CHILDREN; ++j) { + struct ftl_io *io = ftl_io_alloc_child(child[i]); + SPDK_CU_ASSERT_FATAL(io != NULL); + + gchild[i * MAX_GRAND_CHILDREN + j] = io; + gchild_status[i * MAX_GRAND_CHILDREN + j] = -1; + setup_io(io, dev, io_complete_cb, &gchild_status[i * MAX_GRAND_CHILDREN + j]); + io->status = 0; + + ftl_io_inc_req(io); + } + + CU_ASSERT_TRUE(ftl_io_done(child[i])); + ftl_io_complete(child[i]); + CU_ASSERT_EQUAL(child_status[i], -1); + } + + CU_ASSERT_TRUE(ftl_io_done(parent)); + ftl_io_complete(parent); + CU_ASSERT_EQUAL(parent_status, -1); + + for (i = 0; i < MAX_CHILDREN; ++i) { + for (j = 0; j < MAX_GRAND_CHILDREN; ++j) { + struct ftl_io *io = gchild[i * MAX_GRAND_CHILDREN + j]; + + CU_ASSERT_FALSE(ftl_io_done(io)); + ftl_io_dec_req(io); + CU_ASSERT_TRUE(ftl_io_done(io)); + ftl_io_complete(io); + CU_ASSERT_EQUAL(gchild_status[i * MAX_GRAND_CHILDREN + j], 0); + } + + CU_ASSERT_EQUAL(child_status[i], 0); + } + + CU_ASSERT_EQUAL(parent_status, 0); + CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size); + + free_device(dev); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("ftl_io_suite", NULL, NULL); + if (!suite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "test_completion", + test_completion) == NULL + || CU_add_test(suite, "test_alloc_free", + test_alloc_free) == NULL + || CU_add_test(suite, "test_child_requests", + test_child_requests) == NULL + || CU_add_test(suite, "test_child_status", + test_child_status) == NULL + || CU_add_test(suite, "test_multi_generation", + test_multi_generation) == 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; +} diff --git a/test/unit/unittest.sh b/test/unit/unittest.sh index 59dac554c..8f98156a6 100755 --- a/test/unit/unittest.sh +++ b/test/unit/unittest.sh @@ -162,6 +162,7 @@ $valgrind $testdir/lib/ftl/ftl_band.c/ftl_band_ut $valgrind $testdir/lib/ftl/ftl_reloc.c/ftl_reloc_ut $valgrind $testdir/lib/ftl/ftl_wptr/ftl_wptr_ut $valgrind $testdir/lib/ftl/ftl_md/ftl_md_ut +$valgrind $testdir/lib/ftl/ftl_io.c/ftl_io_ut fi # local unit test coverage