-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tested: add test for nested aio_poll() in poll handlers
Cc: qemu-stable@nongnu.org Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Message-Id: <20230502184134.534703-3-stefanha@redhat.com> [kwolf: Restrict to CONFIG_POSIX, Windows doesn't support polling] Tested-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
- Loading branch information
1 parent
6d740fb
commit 844a12a
Showing
2 changed files
with
134 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
/* | ||
* Test that poll handlers are not re-entrant in nested aio_poll() | ||
* | ||
* Copyright Red Hat | ||
* | ||
* Poll handlers are usually level-triggered. That means they continue firing | ||
* until the condition is reset (e.g. a virtqueue becomes empty). If a poll | ||
* handler calls nested aio_poll() before the condition is reset, then infinite | ||
* recursion occurs. | ||
* | ||
* aio_poll() is supposed to prevent this by disabling poll handlers in nested | ||
* aio_poll() calls. This test case checks that this is indeed what happens. | ||
*/ | ||
#include "qemu/osdep.h" | ||
#include "block/aio.h" | ||
#include "qapi/error.h" | ||
|
||
typedef struct { | ||
AioContext *ctx; | ||
|
||
/* This is the EventNotifier that drives the test */ | ||
EventNotifier poll_notifier; | ||
|
||
/* This EventNotifier is only used to wake aio_poll() */ | ||
EventNotifier dummy_notifier; | ||
|
||
bool nested; | ||
} TestData; | ||
|
||
static void io_read(EventNotifier *notifier) | ||
{ | ||
fprintf(stderr, "%s %p\n", __func__, notifier); | ||
event_notifier_test_and_clear(notifier); | ||
} | ||
|
||
static bool io_poll_true(void *opaque) | ||
{ | ||
fprintf(stderr, "%s %p\n", __func__, opaque); | ||
return true; | ||
} | ||
|
||
static bool io_poll_false(void *opaque) | ||
{ | ||
fprintf(stderr, "%s %p\n", __func__, opaque); | ||
return false; | ||
} | ||
|
||
static void io_poll_ready(EventNotifier *notifier) | ||
{ | ||
TestData *td = container_of(notifier, TestData, poll_notifier); | ||
|
||
fprintf(stderr, "> %s\n", __func__); | ||
|
||
g_assert(!td->nested); | ||
td->nested = true; | ||
|
||
/* Wake the following nested aio_poll() call */ | ||
event_notifier_set(&td->dummy_notifier); | ||
|
||
/* This nested event loop must not call io_poll()/io_poll_ready() */ | ||
g_assert(aio_poll(td->ctx, true)); | ||
|
||
td->nested = false; | ||
|
||
fprintf(stderr, "< %s\n", __func__); | ||
} | ||
|
||
/* dummy_notifier never triggers */ | ||
static void io_poll_never_ready(EventNotifier *notifier) | ||
{ | ||
g_assert_not_reached(); | ||
} | ||
|
||
static void test(void) | ||
{ | ||
TestData td = { | ||
.ctx = aio_context_new(&error_abort), | ||
}; | ||
|
||
qemu_set_current_aio_context(td.ctx); | ||
|
||
/* Enable polling */ | ||
aio_context_set_poll_params(td.ctx, 1000000, 2, 2, &error_abort); | ||
|
||
/* | ||
* The GSource is unused but this has the side-effect of changing the fdmon | ||
* that AioContext uses. | ||
*/ | ||
aio_get_g_source(td.ctx); | ||
|
||
/* Make the event notifier active (set) right away */ | ||
event_notifier_init(&td.poll_notifier, 1); | ||
aio_set_event_notifier(td.ctx, &td.poll_notifier, false, | ||
io_read, io_poll_true, io_poll_ready); | ||
|
||
/* This event notifier will be used later */ | ||
event_notifier_init(&td.dummy_notifier, 0); | ||
aio_set_event_notifier(td.ctx, &td.dummy_notifier, false, | ||
io_read, io_poll_false, io_poll_never_ready); | ||
|
||
/* Consume aio_notify() */ | ||
g_assert(!aio_poll(td.ctx, false)); | ||
|
||
/* | ||
* Run the io_read() handler. This has the side-effect of activating | ||
* polling in future aio_poll() calls. | ||
*/ | ||
g_assert(aio_poll(td.ctx, true)); | ||
|
||
/* The second time around the io_poll()/io_poll_ready() handler runs */ | ||
g_assert(aio_poll(td.ctx, true)); | ||
|
||
/* Run io_poll()/io_poll_ready() one more time to show it keeps working */ | ||
g_assert(aio_poll(td.ctx, true)); | ||
|
||
aio_set_event_notifier(td.ctx, &td.dummy_notifier, false, | ||
NULL, NULL, NULL); | ||
aio_set_event_notifier(td.ctx, &td.poll_notifier, false, NULL, NULL, NULL); | ||
event_notifier_cleanup(&td.dummy_notifier); | ||
event_notifier_cleanup(&td.poll_notifier); | ||
aio_context_unref(td.ctx); | ||
} | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
g_test_init(&argc, &argv, NULL); | ||
g_test_add_func("/nested-aio-poll", test); | ||
return g_test_run(); | ||
} |