lib/util: handle channel being removed during spdk_for_each_channel

A channel may have been deleted between when a message was sent to
a thread and when it gets executed on that thread.  So we must look
for the channel when the messages gets executed - if it's not found,
just continue to the next thread(s).

Signed-off-by: Jim Harris <james.r.harris@intel.com>
Change-Id: Ib7e596f11f287c6be521ba729d33378f0a825c7e

Reviewed-on: https://review.gerrithub.io/378002
Reviewed-by: Dariusz Stojaczyk <dariuszx.stojaczyk@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
This commit is contained in:
Jim Harris 2017-09-11 13:58:40 -07:00
parent 32a87ed9e3
commit 93cfe874b2
2 changed files with 89 additions and 5 deletions

View File

@ -404,7 +404,6 @@ struct call_channel {
void *ctx;
struct spdk_thread *cur_thread;
struct spdk_io_channel *cur_ch;
struct spdk_thread *orig_thread;
spdk_channel_for_each_cpl cpl;
@ -429,9 +428,22 @@ _call_channel(void *ctx)
struct spdk_io_channel *ch;
thread = ch_ctx->cur_thread;
ch = ch_ctx->cur_ch;
pthread_mutex_lock(&g_devlist_mutex);
TAILQ_FOREACH(ch, &thread->io_channels, tailq) {
if (ch->dev->io_device == ch_ctx->io_device) {
break;
}
}
pthread_mutex_unlock(&g_devlist_mutex);
ch_ctx->fn(ch_ctx->io_device, ch, ch_ctx->ctx);
/*
* It is possible that the channel was deleted before this
* message had a chance to execute. If so, skip calling
* the fn() on this thread.
*/
if (ch != NULL) {
ch_ctx->fn(ch_ctx->io_device, ch, ch_ctx->ctx);
}
pthread_mutex_lock(&g_devlist_mutex);
thread = TAILQ_NEXT(thread, tailq);
@ -439,7 +451,6 @@ _call_channel(void *ctx)
TAILQ_FOREACH(ch, &thread->io_channels, tailq) {
if (ch->dev->io_device == ch_ctx->io_device) {
ch_ctx->cur_thread = thread;
ch_ctx->cur_ch = ch;
pthread_mutex_unlock(&g_devlist_mutex);
spdk_thread_send_msg(thread, _call_channel, ch_ctx);
return;
@ -479,7 +490,6 @@ spdk_for_each_channel(void *io_device, spdk_channel_msg fn, void *ctx,
TAILQ_FOREACH(ch, &thread->io_channels, tailq) {
if (ch->dev->io_device == io_device) {
ch_ctx->cur_thread = thread;
ch_ctx->cur_ch = ch;
pthread_mutex_unlock(&g_devlist_mutex);
spdk_thread_send_msg(thread, _call_channel, ch_ctx);
return;

View File

@ -97,6 +97,79 @@ thread_send_msg(void)
free_threads();
}
static int
channel_create(void *io_device, void *ctx_buf)
{
return 0;
}
static void
channel_destroy(void *io_device, void *ctx_buf)
{
}
static void
channel_msg(void *io_device, struct spdk_io_channel *ch, void *ctx)
{
int *count = spdk_io_channel_get_ctx(ch);
(*count)++;
}
static void
channel_cpl(void *io_device, void *ctx)
{
}
static void
for_each_channel_remove(void)
{
struct spdk_io_channel *ch0, *ch1, *ch2;
int io_target;
int count = 0;
allocate_threads(3);
spdk_io_device_register(&io_target, channel_create, channel_destroy, sizeof(int));
set_thread(0);
ch0 = spdk_get_io_channel(&io_target);
set_thread(1);
ch1 = spdk_get_io_channel(&io_target);
set_thread(2);
ch2 = spdk_get_io_channel(&io_target);
/*
* Test that io_channel handles the case where we start to iterate through
* the channels, and during the iteration, one of the channels is deleted.
* This is done in some different and sometimes non-intuitive orders, because
* some operations are deferred and won't execute until their threads are
* polled.
*
* Case #1: Put the I/O channel before spdk_for_each_channel.
*/
set_thread(0);
spdk_put_io_channel(ch0);
spdk_for_each_channel(&io_target, channel_msg, &count, channel_cpl);
poll_threads();
/*
* Case #2: Put the I/O channel after spdk_for_each_channel, but before
* thread 0 is polled.
*/
ch0 = spdk_get_io_channel(&io_target);
spdk_for_each_channel(&io_target, channel_msg, &count, channel_cpl);
spdk_put_io_channel(ch0);
poll_threads();
set_thread(1);
spdk_put_io_channel(ch1);
set_thread(2);
spdk_put_io_channel(ch2);
spdk_io_device_unregister(&io_target, NULL);
poll_threads();
free_threads();
}
static void
thread_name(void)
{
@ -244,6 +317,7 @@ main(int argc, char **argv)
if (
CU_add_test(suite, "thread_alloc", thread_alloc) == NULL ||
CU_add_test(suite, "thread_send_msg", thread_send_msg) == NULL ||
CU_add_test(suite, "for_each_channel_remove", for_each_channel_remove) == NULL ||
CU_add_test(suite, "thread_name", thread_name) == NULL ||
CU_add_test(suite, "channel", channel) == NULL
) {