From 089d178abb4d958a693c3564f6c5e1e398e8c398 Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Thu, 7 May 2020 08:39:35 +0900 Subject: [PATCH] lib/bdev: Terminate parent I/O with failure immediately if one of child I/O failed Previously, bdev_io_split_done() had continued splitting process even if the status became failed. To abort split I/O, this patch changes bdev_io_split_done() to terminate with failure before continuing splitting process if the status became failed. Add necessary unit test together. Signed-off-by: Shuhei Matsumoto Change-Id: Ifd1ea49c22523e8c06fb45ebdcb2c84a57afd2ef Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2234 Tested-by: SPDK CI Jenkins Community-CI: Mellanox Build Bot Community-CI: Broadcom CI Reviewed-by: Aleksey Marchuk Reviewed-by: Michael Haeuptle Reviewed-by: Jim Harris --- lib/bdev/bdev.c | 3 +++ test/unit/lib/bdev/bdev.c/bdev_ut.c | 36 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c index bc1b5d3be..754d803d5 100644 --- a/lib/bdev/bdev.c +++ b/lib/bdev/bdev.c @@ -1952,6 +1952,9 @@ bdev_io_split_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) if (!success) { parent_io->internal.status = SPDK_BDEV_IO_STATUS_FAILED; + /* If any child I/O failed, stop further splitting process. */ + parent_io->u.bdev.split_current_offset_blocks += parent_io->u.bdev.split_remaining_num_blocks; + parent_io->u.bdev.split_remaining_num_blocks = 0; } parent_io->u.bdev.split_outstanding--; if (parent_io->u.bdev.split_outstanding != 0) { diff --git a/test/unit/lib/bdev/bdev.c/bdev_ut.c b/test/unit/lib/bdev/bdev.c/bdev_ut.c index b314e0186..14632e3f7 100644 --- a/test/unit/lib/bdev/bdev.c/bdev_ut.c +++ b/test/unit/lib/bdev/bdev.c/bdev_ut.c @@ -1546,6 +1546,42 @@ bdev_io_split_test(void) CU_ASSERT(g_io_done == true); CU_ASSERT(g_io_status == SPDK_BDEV_IO_STATUS_FAILED); + /* Test if a multi vector command terminated with failure before continueing + * splitting process when one of child I/O failed. + * The multi vector command is as same as the above that needs to be split by strip + * and then needs to be split further due to the capacity of child iovs. + */ + for (i = 0; i < BDEV_IO_NUM_CHILD_IOV - 1; i++) { + iov[i].iov_base = (void *)((i + 1) * 0x10000); + iov[i].iov_len = 512; + } + iov[BDEV_IO_NUM_CHILD_IOV - 1].iov_base = (void *)(BDEV_IO_NUM_CHILD_IOV * 0x10000); + iov[BDEV_IO_NUM_CHILD_IOV - 1].iov_len = 256; + + iov[BDEV_IO_NUM_CHILD_IOV].iov_base = (void *)((BDEV_IO_NUM_CHILD_IOV + 1) * 0x10000); + iov[BDEV_IO_NUM_CHILD_IOV].iov_len = 256; + + iov[BDEV_IO_NUM_CHILD_IOV + 1].iov_base = (void *)((BDEV_IO_NUM_CHILD_IOV + 2) * 0x10000); + iov[BDEV_IO_NUM_CHILD_IOV + 1].iov_len = 512; + + bdev->optimal_io_boundary = BDEV_IO_NUM_CHILD_IOV; + + g_io_exp_status = SPDK_BDEV_IO_STATUS_FAILED; + g_io_done = false; + g_io_status = SPDK_BDEV_IO_STATUS_SUCCESS; + + rc = spdk_bdev_readv_blocks(desc, io_ch, iov, BDEV_IO_NUM_CHILD_IOV * 2, 0, + BDEV_IO_NUM_CHILD_IOV + 1, io_done, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_io_done == false); + + CU_ASSERT(g_bdev_ut_channel->outstanding_io_count == 1); + stub_complete_io(1); + CU_ASSERT(g_io_done == true); + CU_ASSERT(g_io_status == SPDK_BDEV_IO_STATUS_FAILED); + + g_io_exp_status = SPDK_BDEV_IO_STATUS_SUCCESS; + /* for this test we will create the following conditions to hit the code path where * we are trying to send and IO following a split that has no iovs because we had to * trim them for alignment reasons.