bdev: fix completion for unsubmitted IOs

If an IO is completed, before submitting it to a module, it isn't put on
the io_submitted list, so we can't use bdev_io_complete() to complete
it, as it'll break that list.  To avoid that, a new function was added,
bdev_io_complete_unsubmitted(), that will safely complete the IOs in
such case.  For now, it's equivalent to executing user's completion
callback, but it'll serve as a good place to release any resources that
should be freed before an IO is completed.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I1442ead9d272d9210553803bed1d1c989a2bf761
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16970
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
This commit is contained in:
Konrad Sztyber 2023-02-23 16:24:33 +01:00 committed by Ben Walker
parent 7b71fdc28c
commit 9beb6b163c

View File

@ -361,6 +361,7 @@ struct spdk_bdev_io_error_stat {
#define __io_ch_to_bdev_mgmt_ch(io_ch) ((struct spdk_bdev_mgmt_channel *)spdk_io_channel_get_ctx(io_ch))
static inline void bdev_io_complete(void *ctx);
static inline void bdev_io_complete_unsubmitted(struct spdk_bdev_io *bdev_io);
static void bdev_write_zero_buffer_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg);
static void bdev_write_zero_buffer_next(void *_bdev_io);
@ -388,8 +389,6 @@ static int bdev_unlock_lba_range(struct spdk_bdev_desc *desc, struct spdk_io_cha
uint64_t offset, uint64_t length,
lock_range_cb cb_fn, void *cb_arg);
static inline void bdev_io_complete(void *ctx);
static bool bdev_abort_queued_io(bdev_io_tailq_t *queue, struct spdk_bdev_io *bio_to_abort);
static bool bdev_abort_buf_io(struct spdk_bdev_mgmt_channel *ch, struct spdk_bdev_io *bio_to_abort);
@ -1416,7 +1415,8 @@ _bdev_memory_domain_get_io_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *b
{
if (!success) {
SPDK_ERRLOG("Failed to get data buffer, completing IO\n");
bdev_io_complete(bdev_io);
bdev_io->internal.status = SPDK_BDEV_IO_STATUS_FAILED;
bdev_io_complete_unsubmitted(bdev_io);
} else {
bdev_io_submit(bdev_io);
}
@ -6484,6 +6484,18 @@ bdev_io_update_io_stat(struct spdk_bdev_io *bdev_io, uint64_t tsc_diff)
#endif
}
static inline void
_bdev_io_complete(void *ctx)
{
struct spdk_bdev_io *bdev_io = ctx;
assert(bdev_io->internal.cb != NULL);
assert(spdk_get_thread() == spdk_bdev_io_get_thread(bdev_io));
bdev_io->internal.cb(bdev_io, bdev_io->internal.status == SPDK_BDEV_IO_STATUS_SUCCESS,
bdev_io->internal.caller_ctx);
}
static inline void
bdev_io_complete(void *ctx)
{
@ -6513,12 +6525,23 @@ bdev_io_complete(void *ctx)
}
bdev_io_update_io_stat(bdev_io, tsc_diff);
_bdev_io_complete(bdev_io);
}
assert(bdev_io->internal.cb != NULL);
assert(spdk_get_thread() == spdk_bdev_io_get_thread(bdev_io));
/* The difference between this function and bdev_io_complete() is that this should be called to
* complete IOs that haven't been submitted via bdev_io_submit(), as they weren't added onto the
* io_submitted list and don't have submit_tsc updated.
*/
static inline void
bdev_io_complete_unsubmitted(struct spdk_bdev_io *bdev_io)
{
/* Since the IO hasn't been submitted it's bound to be failed */
assert(bdev_io->internal.status != SPDK_BDEV_IO_STATUS_SUCCESS);
bdev_io->internal.cb(bdev_io, bdev_io->internal.status == SPDK_BDEV_IO_STATUS_SUCCESS,
bdev_io->internal.caller_ctx);
/* At this point we don't know if the IO is completed from submission context or not, but,
* since this is an error path, we can always do an spdk_thread_send_msg(). */
spdk_thread_send_msg(spdk_bdev_io_get_thread(bdev_io),
_bdev_io_complete, bdev_io);
}
static void bdev_destroy_cb(void *io_device);