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:
parent
a12aae4c85
commit
b32411ddaa
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user