From 2138676573d8e36fe30043368efcccf0fb02d997 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Thu, 12 Jan 2017 10:58:20 -0700 Subject: [PATCH] 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 --- include/spdk/bdev.h | 8 ++++++++ lib/bdev/bdev.c | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/spdk/bdev.h b/include/spdk/bdev.h index dd4bdfc527..6abd10f600 100644 --- a/include/spdk/bdev.h +++ b/include/spdk/bdev.h @@ -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; diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c index b621fc8bd7..4307da7f76 100644 --- a/lib/bdev/bdev.c +++ b/lib/bdev/bdev.c @@ -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) {