lib/nvme: Make internal of spdk_nvme_detach() asynchronous

Add two new helper functions, nvme_ctrlr_detach_async() and
nvme_ctrlr_detach_poll_async() to make the internal of
spdk_nvme_detach() asynchronous.

Use callback function to remove controller from the attached list after
completing shutdown and before freeing to avoid conflict between
attach and detach.

Update MOCKs in the corresponding unit test cases.

The next patch will add two public APIs spdk_nvme_detach_async()
and spdk_nvme_detach_poll_async() based on this patch.

Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Change-Id: Ifbdfec2a1facde9354007c6248f280e245a36eed
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/4416
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
Reviewed-by: Jacek Kalwas <jacek.kalwas@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Shuhei Matsumoto 2020-10-16 03:07:21 +09:00 committed by Jim Harris
parent 3806b2e1f9
commit ea1bfd84cd
4 changed files with 115 additions and 16 deletions

View File

@ -63,24 +63,98 @@ nvme_ctrlr_connected(struct spdk_nvme_probe_ctx *probe_ctx,
TAILQ_INSERT_TAIL(&probe_ctx->init_ctrlrs, ctrlr, tailq);
}
int
spdk_nvme_detach(struct spdk_nvme_ctrlr *ctrlr)
static void
nvme_ctrlr_detach_async_finish(struct spdk_nvme_ctrlr *ctrlr)
{
nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock);
if (nvme_ctrlr_shared(ctrlr)) {
TAILQ_REMOVE(&g_spdk_nvme_driver->shared_attached_ctrlrs, ctrlr, tailq);
} else {
TAILQ_REMOVE(&g_nvme_attached_ctrlrs, ctrlr, tailq);
}
nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock);
}
nvme_ctrlr_proc_put_ref(ctrlr);
static int
nvme_ctrlr_detach_async(struct spdk_nvme_ctrlr *ctrlr,
struct nvme_ctrlr_detach_ctx **_ctx)
{
struct nvme_ctrlr_detach_ctx *ctx;
int ref_count;
if (nvme_ctrlr_get_ref_count(ctrlr) == 0) {
nvme_io_msg_ctrlr_detach(ctrlr);
if (nvme_ctrlr_shared(ctrlr)) {
TAILQ_REMOVE(&g_spdk_nvme_driver->shared_attached_ctrlrs, ctrlr, tailq);
} else {
TAILQ_REMOVE(&g_nvme_attached_ctrlrs, ctrlr, tailq);
nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock);
ref_count = nvme_ctrlr_get_ref_count(ctrlr);
assert(ref_count > 0);
if (ref_count == 1) {
/* This is the last reference to the controller, so we need to
* allocate a context to destruct it.
*/
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) {
nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock);
return -ENOMEM;
}
nvme_ctrlr_destruct(ctrlr);
ctx->cb_fn = nvme_ctrlr_detach_async_finish;
nvme_ctrlr_proc_put_ref(ctrlr);
nvme_io_msg_ctrlr_detach(ctrlr);
nvme_ctrlr_destruct_async(ctrlr, ctx);
*_ctx = ctx;
} else {
nvme_ctrlr_proc_put_ref(ctrlr);
}
nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock);
return 0;
}
static int
nvme_ctrlr_detach_poll_async(struct spdk_nvme_ctrlr *ctrlr,
struct nvme_ctrlr_detach_ctx *ctx)
{
int rc;
rc = nvme_ctrlr_destruct_poll_async(ctrlr, ctx);
if (rc == -EAGAIN) {
return -EAGAIN;
}
free(ctx);
return rc;
}
int
spdk_nvme_detach(struct spdk_nvme_ctrlr *ctrlr)
{
struct nvme_ctrlr_detach_ctx *ctx = NULL;
int rc;
rc = nvme_ctrlr_detach_async(ctrlr, &ctx);
if (rc != 0) {
return rc;
} else if (ctx == NULL) {
/* ctrlr was detached from the caller process but any other process
* still attaches it.
*/
return 0;
}
while (1) {
rc = nvme_ctrlr_detach_poll_async(ctrlr, ctx);
if (rc != -EAGAIN) {
break;
}
nvme_delay(1000);
}
return 0;
}

View File

@ -3317,6 +3317,10 @@ nvme_ctrlr_destruct_poll_async(struct spdk_nvme_ctrlr *ctrlr,
/* Destruct ctrlr forcefully for any other error. */
}
if (ctx->cb_fn) {
ctx->cb_fn(ctrlr);
}
nvme_ctrlr_destruct_namespaces(ctrlr);
spdk_bit_array_free(&ctrlr->free_io_qids);

View File

@ -843,10 +843,13 @@ struct spdk_nvme_probe_ctx {
TAILQ_HEAD(, spdk_nvme_ctrlr) init_ctrlrs;
};
typedef void (*nvme_ctrlr_detach_cb)(struct spdk_nvme_ctrlr *ctrlr);
struct nvme_ctrlr_detach_ctx {
uint64_t shutdown_start_tsc;
uint32_t shutdown_timeout_ms;
bool shutdown_complete;
nvme_ctrlr_detach_cb cb_fn;
uint64_t shutdown_start_tsc;
uint32_t shutdown_timeout_ms;
bool shutdown_complete;
};
struct nvme_driver {

View File

@ -74,6 +74,23 @@ nvme_ctrlr_destruct(struct spdk_nvme_ctrlr *ctrlr)
ut_destruct_called = true;
}
void
nvme_ctrlr_destruct_async(struct spdk_nvme_ctrlr *ctrlr, struct nvme_ctrlr_detach_ctx *ctx)
{
ut_destruct_called = true;
}
int
nvme_ctrlr_destruct_poll_async(struct spdk_nvme_ctrlr *ctrlr,
struct nvme_ctrlr_detach_ctx *ctx)
{
if (ctx->cb_fn) {
ctx->cb_fn(ctrlr);
}
return 0;
}
void
spdk_nvme_ctrlr_get_default_ctrlr_opts(struct spdk_nvme_ctrlr_opts *opts, size_t opts_size)
{
@ -284,6 +301,7 @@ test_spdk_nvme_connect(void)
CU_ASSERT_EQUAL(ret_ctrlr->opts.num_io_queues, 1);
CU_ASSERT_EQUAL(ret_ctrlr->opts.opts_size, 4);
/* remove the attached ctrlr on the attached_list */
MOCK_SET(nvme_ctrlr_get_ref_count, 1);
CU_ASSERT(spdk_nvme_detach(&ctrlr) == 0);
CU_ASSERT(TAILQ_EMPTY(&g_spdk_nvme_driver->shared_attached_ctrlrs));
@ -506,7 +524,7 @@ test_spdk_nvme_detach(void)
* called (we aren't testing what the real destruct function does
* here.)
*/
MOCK_SET(nvme_ctrlr_get_ref_count, 0);
MOCK_SET(nvme_ctrlr_get_ref_count, 1);
rc = spdk_nvme_detach(&ctrlr);
ret_ctrlr = TAILQ_FIRST(&test_driver.shared_attached_ctrlrs);
CU_ASSERT(ret_ctrlr == NULL);
@ -518,7 +536,7 @@ test_spdk_nvme_detach(void)
* function is not called and that attached ctrl list is
* not empty.
*/
MOCK_SET(nvme_ctrlr_get_ref_count, 1);
MOCK_SET(nvme_ctrlr_get_ref_count, 2);
TAILQ_INSERT_TAIL(&test_driver.shared_attached_ctrlrs, &ctrlr, tailq);
ut_destruct_called = false;
rc = spdk_nvme_detach(&ctrlr);
@ -536,7 +554,7 @@ test_spdk_nvme_detach(void)
ctrlr.trid.trtype = SPDK_NVME_TRANSPORT_RDMA;
TAILQ_INIT(&g_nvme_attached_ctrlrs);
TAILQ_INSERT_TAIL(&g_nvme_attached_ctrlrs, &ctrlr, tailq);
MOCK_SET(nvme_ctrlr_get_ref_count, 0);
MOCK_SET(nvme_ctrlr_get_ref_count, 1);
rc = spdk_nvme_detach(&ctrlr);
CU_ASSERT(TAILQ_EMPTY(&g_nvme_attached_ctrlrs));
CU_ASSERT(ut_destruct_called == true);