bdev: defer completions from within submit_request
If a blockdev module calls spdk_bdev_io_complete() within its submit_request function, and the user's completion callback issues a new I/O, it is possible to cause infinite recursion, consuming all available stack space. To avoid this, track whether a bdev_io is being processed by submit_request, and if io_complete() is called in this case, defer the completion via an event. Change-Id: I6ccdb8ed4ee0d5738e6c9840d35431de52bd5fa2 Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
parent
360a3da3d7
commit
2138676573
@ -265,6 +265,14 @@ struct spdk_bdev_io {
|
||||
/** Status for the IO */
|
||||
enum spdk_bdev_io_status status;
|
||||
|
||||
/**
|
||||
* Set to true while the bdev module submit_request function is in progress.
|
||||
*
|
||||
* This is used to decide whether spdk_bdev_io_complete() can complete the I/O directly
|
||||
* or if completion must be deferred via an event.
|
||||
*/
|
||||
bool in_submit_request;
|
||||
|
||||
/** Used in virtual device (e.g., RAID), indicates its parent spdk_bdev_io **/
|
||||
struct spdk_bdev_io *parent;
|
||||
|
||||
|
@ -415,7 +415,9 @@ __submit_request(struct spdk_bdev *bdev, struct spdk_bdev_io *bdev_io)
|
||||
if (bdev_io->type == SPDK_BDEV_IO_TYPE_RESET) {
|
||||
spdk_bdev_cleanup_pending_rbuf_io(bdev);
|
||||
}
|
||||
bdev_io->in_submit_request = true;
|
||||
bdev->fn_table->submit_request(bdev_io);
|
||||
bdev_io->in_submit_request = false;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -438,6 +440,7 @@ spdk_bdev_io_init(struct spdk_bdev_io *bdev_io,
|
||||
bdev_io->cb = cb;
|
||||
bdev_io->gencnt = bdev->gencnt;
|
||||
bdev_io->status = SPDK_BDEV_IO_STATUS_PENDING;
|
||||
bdev_io->in_submit_request = false;
|
||||
TAILQ_INIT(&bdev_io->child_io);
|
||||
}
|
||||
|
||||
@ -798,9 +801,32 @@ spdk_bdev_free_io(struct spdk_bdev_io *bdev_io)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
bdev_io_deferred_completion(void *arg1, void *arg2)
|
||||
{
|
||||
struct spdk_bdev_io *bdev_io = arg1;
|
||||
enum spdk_bdev_io_status status = (enum spdk_bdev_io_status)arg2;
|
||||
|
||||
assert(bdev_io->in_submit_request == false);
|
||||
|
||||
spdk_bdev_io_complete(bdev_io, status);
|
||||
}
|
||||
|
||||
void
|
||||
spdk_bdev_io_complete(struct spdk_bdev_io *bdev_io, enum spdk_bdev_io_status status)
|
||||
{
|
||||
if (bdev_io->in_submit_request) {
|
||||
/*
|
||||
* Defer completion via an event to avoid potential infinite recursion if the
|
||||
* user's completion callback issues a new I/O.
|
||||
*/
|
||||
spdk_event_call(spdk_event_allocate(spdk_app_get_current_core(),
|
||||
bdev_io_deferred_completion,
|
||||
bdev_io,
|
||||
(void *)status));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdev_io->type == SPDK_BDEV_IO_TYPE_RESET) {
|
||||
/* Successful reset */
|
||||
if (status == SPDK_BDEV_IO_STATUS_SUCCESS) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user