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 */
|
/** Status for the IO */
|
||||||
enum spdk_bdev_io_status status;
|
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 **/
|
/** Used in virtual device (e.g., RAID), indicates its parent spdk_bdev_io **/
|
||||||
struct spdk_bdev_io *parent;
|
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) {
|
if (bdev_io->type == SPDK_BDEV_IO_TYPE_RESET) {
|
||||||
spdk_bdev_cleanup_pending_rbuf_io(bdev);
|
spdk_bdev_cleanup_pending_rbuf_io(bdev);
|
||||||
}
|
}
|
||||||
|
bdev_io->in_submit_request = true;
|
||||||
bdev->fn_table->submit_request(bdev_io);
|
bdev->fn_table->submit_request(bdev_io);
|
||||||
|
bdev_io->in_submit_request = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -438,6 +440,7 @@ spdk_bdev_io_init(struct spdk_bdev_io *bdev_io,
|
|||||||
bdev_io->cb = cb;
|
bdev_io->cb = cb;
|
||||||
bdev_io->gencnt = bdev->gencnt;
|
bdev_io->gencnt = bdev->gencnt;
|
||||||
bdev_io->status = SPDK_BDEV_IO_STATUS_PENDING;
|
bdev_io->status = SPDK_BDEV_IO_STATUS_PENDING;
|
||||||
|
bdev_io->in_submit_request = false;
|
||||||
TAILQ_INIT(&bdev_io->child_io);
|
TAILQ_INIT(&bdev_io->child_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,9 +801,32 @@ spdk_bdev_free_io(struct spdk_bdev_io *bdev_io)
|
|||||||
return 0;
|
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
|
void
|
||||||
spdk_bdev_io_complete(struct spdk_bdev_io *bdev_io, enum spdk_bdev_io_status status)
|
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) {
|
if (bdev_io->type == SPDK_BDEV_IO_TYPE_RESET) {
|
||||||
/* Successful reset */
|
/* Successful reset */
|
||||||
if (status == SPDK_BDEV_IO_STATUS_SUCCESS) {
|
if (status == SPDK_BDEV_IO_STATUS_SUCCESS) {
|
||||||
|
Loading…
Reference in New Issue
Block a user