bdev/nvme: Not try freeing qpair when it is NULL, and add test scenario

The API spdk_nvme_ctrlr_free_io_qpair() returns immediately if the
passed qpair is NULL, but calling spdk_nvme_ctrlr_free_io_qpair()
with NULL should be avoided.

This patch cleans up the code to ensure that nvme_ch->qpair is NULL if
disconnected and spdk_nvme_ctrlr_free_io_qpair() is called only if
nvme_ch->qpair is not NULL.

Then add a test scenario that two reset requests were submitted
simultaneously and the first reset request failed and then the second
reset request also failed. This verifies the refactoring done in the
next patch.

Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Change-Id: Iae461f7f826b0e1a4607a17e528c04a642242d6e
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/7041
Community-CI: Broadcom CI
Community-CI: Mellanox Build Bot
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
Shuhei Matsumoto 2021-03-24 19:31:43 +09:00 committed by Tomasz Zawadzki
parent 059dcf1da1
commit cb8aa8abc5
2 changed files with 44 additions and 7 deletions

View File

@ -340,6 +340,7 @@ bdev_nvme_create_qpair(struct nvme_io_channel *nvme_ch)
{
struct spdk_nvme_ctrlr *ctrlr = nvme_ch->ctrlr->ctrlr;
struct spdk_nvme_io_qpair_opts opts;
struct spdk_nvme_qpair *qpair;
int rc;
spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr, &opts, sizeof(opts));
@ -348,29 +349,31 @@ bdev_nvme_create_qpair(struct nvme_io_channel *nvme_ch)
opts.io_queue_requests = spdk_max(g_opts.io_queue_requests, opts.io_queue_requests);
g_opts.io_queue_requests = opts.io_queue_requests;
nvme_ch->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts, sizeof(opts));
if (nvme_ch->qpair == NULL) {
qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts, sizeof(opts));
if (qpair == NULL) {
return -1;
}
assert(nvme_ch->group != NULL);
rc = spdk_nvme_poll_group_add(nvme_ch->group->group, nvme_ch->qpair);
rc = spdk_nvme_poll_group_add(nvme_ch->group->group, qpair);
if (rc != 0) {
SPDK_ERRLOG("Unable to begin polling on NVMe Channel.\n");
goto err;
}
rc = spdk_nvme_ctrlr_connect_io_qpair(ctrlr, nvme_ch->qpair);
rc = spdk_nvme_ctrlr_connect_io_qpair(ctrlr, qpair);
if (rc != 0) {
SPDK_ERRLOG("Unable to connect I/O qpair.\n");
goto err;
}
nvme_ch->qpair = qpair;
return 0;
err:
spdk_nvme_ctrlr_free_io_qpair(nvme_ch->qpair);
spdk_nvme_ctrlr_free_io_qpair(qpair);
return rc;
}
@ -1020,7 +1023,9 @@ bdev_nvme_destroy_cb(void *io_device, void *ctx_buf)
bdev_ocssd_destroy_io_channel(nvme_ch);
}
spdk_nvme_ctrlr_free_io_qpair(nvme_ch->qpair);
if (nvme_ch->qpair != NULL) {
spdk_nvme_ctrlr_free_io_qpair(nvme_ch->qpair);
}
spdk_put_io_channel(spdk_io_channel_from_ctx(nvme_ch->group));
}

View File

@ -252,6 +252,7 @@ struct spdk_nvme_ctrlr {
struct spdk_nvme_qpair adminq;
struct spdk_nvme_ctrlr_data cdata;
bool is_failed;
bool fail_reset;
struct spdk_nvme_transport_id trid;
TAILQ_HEAD(, spdk_nvme_qpair) active_io_qpairs;
TAILQ_ENTRY(spdk_nvme_ctrlr) tailq;
@ -615,6 +616,10 @@ spdk_nvme_ctrlr_free_io_qpair(struct spdk_nvme_qpair *qpair)
int
spdk_nvme_ctrlr_reset(struct spdk_nvme_ctrlr *ctrlr)
{
if (ctrlr->fail_reset) {
return -EIO;
}
ctrlr->is_failed = false;
return 0;
@ -1331,7 +1336,7 @@ test_pending_reset(void)
nvme_ch2 = spdk_io_channel_get_ctx(ch2);
/* The first abort request is submitted on thread 1, and the second abort request
/* The first reset request is submitted on thread 1, and the second reset request
* is submitted on thread 0 while processing the first request.
*/
rc = bdev_nvme_reset(nvme_ch2, first_bio);
@ -1351,6 +1356,33 @@ test_pending_reset(void)
CU_ASSERT(first_bdev_io->internal.status == SPDK_BDEV_IO_STATUS_SUCCESS);
CU_ASSERT(second_bdev_io->internal.status == SPDK_BDEV_IO_STATUS_SUCCESS);
/* The first reset request is submitted on thread 1, and the second reset request
* is submitted on thread 0 while processing the first request.
*
* The difference from the above scenario is that the controller is removed while
* processing the first request. Hence both reset requests should fail.
*/
set_thread(1);
rc = bdev_nvme_reset(nvme_ch2, first_bio);
CU_ASSERT(rc == 0);
CU_ASSERT(nvme_bdev_ctrlr->resetting == true);
CU_ASSERT(TAILQ_EMPTY(&nvme_ch2->pending_resets));
set_thread(0);
rc = bdev_nvme_reset(nvme_ch1, second_bio);
CU_ASSERT(rc == 0);
CU_ASSERT(TAILQ_FIRST(&nvme_ch1->pending_resets) == second_bdev_io);
ctrlr.fail_reset = true;
poll_threads();
CU_ASSERT(nvme_bdev_ctrlr->resetting == false);
CU_ASSERT(first_bdev_io->internal.status == SPDK_BDEV_IO_STATUS_FAILED);
CU_ASSERT(second_bdev_io->internal.status == SPDK_BDEV_IO_STATUS_FAILED);
spdk_put_io_channel(ch1);
set_thread(1);