ut/thread: Test creating and destroying nested I/O channels

This patch creates a test case to simulate nested I/O channels by
generic bdev layer and bdev modules. The nested level of I/O
channels is two at a maximum now but the nested level of the test
case is three.

Besides, any poller registered on the nested I/O channel is not
visible to the upper layer. So include such pollers into the test
case.

By combining thread termination, test the fix for the github issue
is correct.

Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Change-Id: I6a04d753e462cdaba7364678b5118c45b09708ca
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/1390
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Community-CI: Broadcom CI
This commit is contained in:
Shuhei Matsumoto 2020-04-05 08:52:43 +09:00 committed by Tomasz Zawadzki
parent a12aae4c85
commit b32411ddaa

View File

@ -1042,6 +1042,201 @@ thread_update_stats(void)
free_threads();
}
struct ut_nested_ch {
struct spdk_io_channel *child;
struct spdk_poller *poller;
};
struct ut_nested_dev {
struct ut_nested_dev *child;
};
static struct io_device *
ut_get_io_device(void *dev)
{
struct io_device *tmp;
TAILQ_FOREACH(tmp, &g_io_devices, tailq) {
if (tmp->io_device == dev) {
return tmp;
}
}
return NULL;
}
static int
ut_null_poll(void *ctx)
{
return -1;
}
static int
ut_nested_ch_create_cb(void *io_device, void *ctx_buf)
{
struct ut_nested_ch *_ch = ctx_buf;
struct ut_nested_dev *_dev = io_device;
struct ut_nested_dev *_child;
_child = _dev->child;
if (_child != NULL) {
_ch->child = spdk_get_io_channel(_child);
SPDK_CU_ASSERT_FATAL(_ch->child != NULL);
} else {
_ch->child = NULL;
}
_ch->poller = spdk_poller_register(ut_null_poll, NULL, 0);
SPDK_CU_ASSERT_FATAL(_ch->poller != NULL);
return 0;
}
static void
ut_nested_ch_destroy_cb(void *io_device, void *ctx_buf)
{
struct ut_nested_ch *_ch = ctx_buf;
struct spdk_io_channel *child;
child = _ch->child;
if (child != NULL) {
spdk_put_io_channel(child);
}
spdk_poller_unregister(&_ch->poller);
}
static void
ut_check_nested_ch_create(struct spdk_io_channel *ch, struct io_device *dev)
{
CU_ASSERT(ch->ref == 1);
CU_ASSERT(ch->dev == dev);
CU_ASSERT(dev->refcnt == 1);
}
static void
ut_check_nested_ch_destroy_pre(struct spdk_io_channel *ch, struct io_device *dev)
{
CU_ASSERT(ch->ref == 0);
CU_ASSERT(ch->destroy_ref == 1);
CU_ASSERT(dev->refcnt == 1);
}
static void
ut_check_nested_ch_destroy_post(struct io_device *dev)
{
CU_ASSERT(dev->refcnt == 0);
}
static void
ut_check_nested_poller_register(struct spdk_poller *poller)
{
SPDK_CU_ASSERT_FATAL(poller != NULL);
}
static void
nested_channel(void)
{
struct ut_nested_dev _dev1, _dev2, _dev3;
struct ut_nested_ch *_ch1, *_ch2, *_ch3;
struct io_device *dev1, *dev2, *dev3;
struct spdk_io_channel *ch1, *ch2, *ch3;
struct spdk_poller *poller;
struct spdk_thread *thread;
allocate_threads(1);
set_thread(0);
thread = spdk_get_thread();
SPDK_CU_ASSERT_FATAL(thread != NULL);
_dev1.child = &_dev2;
_dev2.child = &_dev3;
_dev3.child = NULL;
spdk_io_device_register(&_dev1, ut_nested_ch_create_cb, ut_nested_ch_destroy_cb,
sizeof(struct ut_nested_ch), "dev1");
spdk_io_device_register(&_dev2, ut_nested_ch_create_cb, ut_nested_ch_destroy_cb,
sizeof(struct ut_nested_ch), "dev2");
spdk_io_device_register(&_dev3, ut_nested_ch_create_cb, ut_nested_ch_destroy_cb,
sizeof(struct ut_nested_ch), "dev3");
dev1 = ut_get_io_device(&_dev1);
SPDK_CU_ASSERT_FATAL(dev1 != NULL);
dev2 = ut_get_io_device(&_dev2);
SPDK_CU_ASSERT_FATAL(dev2 != NULL);
dev3 = ut_get_io_device(&_dev3);
SPDK_CU_ASSERT_FATAL(dev3 != NULL);
/* A single call spdk_get_io_channel() to dev1 will also create channels
* to dev2 and dev3 continuously. Pollers will be registered together.
*/
ch1 = spdk_get_io_channel(&_dev1);
SPDK_CU_ASSERT_FATAL(ch1 != NULL);
_ch1 = spdk_io_channel_get_ctx(ch1);
ch2 = _ch1->child;
SPDK_CU_ASSERT_FATAL(ch2 != NULL);
_ch2 = spdk_io_channel_get_ctx(ch2);
ch3 = _ch2->child;
SPDK_CU_ASSERT_FATAL(ch3 != NULL);
_ch3 = spdk_io_channel_get_ctx(ch3);
CU_ASSERT(_ch3->child == NULL);
ut_check_nested_ch_create(ch1, dev1);
ut_check_nested_ch_create(ch2, dev2);
ut_check_nested_ch_create(ch3, dev3);
poller = spdk_poller_register(ut_null_poll, NULL, 0);
ut_check_nested_poller_register(poller);
ut_check_nested_poller_register(_ch1->poller);
ut_check_nested_poller_register(_ch2->poller);
ut_check_nested_poller_register(_ch3->poller);
spdk_poller_unregister(&poller);
poll_thread_times(0, 1);
/* A single call spdk_put_io_channel() to dev1 will also destroy channels
* to dev2 and dev3 continuously. Pollers will be unregistered together.
*/
spdk_put_io_channel(ch1);
/* Start exiting the current thread after unregistering the non-nested
* I/O channel.
*/
spdk_thread_exit(thread);
ut_check_nested_ch_destroy_pre(ch1, dev1);
poll_thread_times(0, 1);
ut_check_nested_ch_destroy_post(dev1);
CU_ASSERT(spdk_thread_is_exited(thread) == false);
ut_check_nested_ch_destroy_pre(ch2, dev2);
poll_thread_times(0, 1);
ut_check_nested_ch_destroy_post(dev2);
CU_ASSERT(spdk_thread_is_exited(thread) == false);
ut_check_nested_ch_destroy_pre(ch3, dev3);
poll_thread_times(0, 1);
ut_check_nested_ch_destroy_post(dev3);
CU_ASSERT(spdk_thread_is_exited(thread) == true);
spdk_io_device_unregister(&_dev1, NULL);
spdk_io_device_unregister(&_dev2, NULL);
spdk_io_device_unregister(&_dev3, NULL);
CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
free_threads();
CU_ASSERT(TAILQ_EMPTY(&g_threads));
}
int
main(int argc, char **argv)
{
@ -1065,6 +1260,7 @@ main(int argc, char **argv)
CU_ADD_TEST(suite, channel_destroy_races);
CU_ADD_TEST(suite, thread_exit);
CU_ADD_TEST(suite, thread_update_stats);
CU_ADD_TEST(suite, nested_channel);
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();