Commit 43cfaf81 authored by Shuhei Matsumoto's avatar Shuhei Matsumoto Committed by Tomasz Zawadzki
Browse files

bdev/nvme: nvme_ctrlr_op_rpc() can enable/disables ctrlr dynamically



Extend nvme_ctrlr_op_rpc() and nvme_bdev_ctrlr_op_rpc() to support enable
or disable a controller or all controllers in a bdev controller.

To disable a controller, bdev_nvme_disable_ctrlr() cancels reconnect
and disable a controller if reconnect is already scheduled, or disconnect
and disable a controller. Disable is to keep a controller disconnected
without scheduling a reconnect.

To enable a controller, bdev_nvme_enable_ctrlr() reconnects a controller
if it is disabled.

To indicate a controller is disabled, add a disabled variable to the
nvme_ctrlr structure.

Signed-off-by: default avatarShuhei Matsumoto <smatsumoto@nvidia.com>
Change-Id: I02f97cdc549f317f4d37c802a125bf0f0db855fe
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/18235


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: default avatarRichael <richael.zhuang@arm.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
parent 512b7553
Loading
Loading
Loading
Loading
+208 −2
Original line number Diff line number Diff line
@@ -881,6 +881,10 @@ nvme_ctrlr_is_failed(struct nvme_ctrlr *nvme_ctrlr)
		return false;
	}

	if (nvme_ctrlr->disabled) {
		return true;
	}

	if (spdk_nvme_ctrlr_is_failed(nvme_ctrlr->ctrlr)) {
		return true;
	} else {
@@ -903,6 +907,10 @@ nvme_ctrlr_is_available(struct nvme_ctrlr *nvme_ctrlr)
		return false;
	}

	if (nvme_ctrlr->disabled) {
		return false;
	}

	return true;
}

@@ -2281,6 +2289,12 @@ bdev_nvme_reset_ctrlr(struct nvme_ctrlr *nvme_ctrlr)
		return -EBUSY;
	}

	if (nvme_ctrlr->disabled) {
		pthread_mutex_unlock(&nvme_ctrlr->mutex);
		SPDK_NOTICELOG("Unable to perform reset. Controller is disabled.\n");
		return -EALREADY;
	}

	nvme_ctrlr->resetting = true;
	nvme_ctrlr->dont_retry = true;

@@ -2301,6 +2315,175 @@ bdev_nvme_reset_ctrlr(struct nvme_ctrlr *nvme_ctrlr)
	return 0;
}

static int
bdev_nvme_enable_ctrlr(struct nvme_ctrlr *nvme_ctrlr)
{
	pthread_mutex_lock(&nvme_ctrlr->mutex);
	if (nvme_ctrlr->destruct) {
		pthread_mutex_unlock(&nvme_ctrlr->mutex);
		return -ENXIO;
	}

	if (nvme_ctrlr->resetting) {
		pthread_mutex_unlock(&nvme_ctrlr->mutex);
		return -EBUSY;
	}

	if (!nvme_ctrlr->disabled) {
		pthread_mutex_unlock(&nvme_ctrlr->mutex);
		return -EALREADY;
	}

	nvme_ctrlr->disabled = false;
	nvme_ctrlr->resetting = true;

	nvme_ctrlr->reset_start_tsc = spdk_get_ticks();

	pthread_mutex_unlock(&nvme_ctrlr->mutex);

	spdk_thread_send_msg(nvme_ctrlr->thread, bdev_nvme_reconnect_ctrlr_now, nvme_ctrlr);
	return 0;
}

static void
_bdev_nvme_disable_ctrlr_complete(struct spdk_io_channel_iter *i, int status)
{
	struct nvme_ctrlr *nvme_ctrlr = spdk_io_channel_iter_get_io_device(i);
	bdev_nvme_ctrlr_op_cb ctrlr_op_cb_fn = nvme_ctrlr->ctrlr_op_cb_fn;
	void *ctrlr_op_cb_arg = nvme_ctrlr->ctrlr_op_cb_arg;
	enum bdev_nvme_op_after_reset op_after_disable;

	assert(nvme_ctrlr->thread == spdk_get_thread());

	nvme_ctrlr->ctrlr_op_cb_fn = NULL;
	nvme_ctrlr->ctrlr_op_cb_arg = NULL;

	pthread_mutex_lock(&nvme_ctrlr->mutex);

	nvme_ctrlr->resetting = false;
	nvme_ctrlr->dont_retry = false;

	op_after_disable = bdev_nvme_check_op_after_reset(nvme_ctrlr, true);

	nvme_ctrlr->disabled = true;
	spdk_poller_pause(nvme_ctrlr->adminq_timer_poller);

	pthread_mutex_unlock(&nvme_ctrlr->mutex);

	if (ctrlr_op_cb_fn) {
		ctrlr_op_cb_fn(ctrlr_op_cb_arg, 0);
	}

	switch (op_after_disable) {
	case OP_COMPLETE_PENDING_DESTRUCT:
		nvme_ctrlr_unregister(nvme_ctrlr);
		break;
	default:
		break;
	}

}

static void
bdev_nvme_disable_ctrlr_complete(struct nvme_ctrlr *nvme_ctrlr)
{
	/* Make sure we clear any pending resets before returning. */
	spdk_for_each_channel(nvme_ctrlr,
			      bdev_nvme_complete_pending_resets,
			      NULL,
			      _bdev_nvme_disable_ctrlr_complete);
}

static void
bdev_nvme_disable_destroy_qpairs_done(struct spdk_io_channel_iter *i, int status)
{
	struct nvme_ctrlr *nvme_ctrlr = spdk_io_channel_iter_get_io_device(i);

	assert(status == 0);

	if (!spdk_nvme_ctrlr_is_fabrics(nvme_ctrlr->ctrlr)) {
		bdev_nvme_disable_ctrlr_complete(nvme_ctrlr);
	} else {
		nvme_ctrlr_disconnect(nvme_ctrlr, bdev_nvme_disable_ctrlr_complete);
	}
}

static void
bdev_nvme_disable_destroy_qpairs(struct nvme_ctrlr *nvme_ctrlr)
{
	spdk_for_each_channel(nvme_ctrlr,
			      bdev_nvme_reset_destroy_qpair,
			      NULL,
			      bdev_nvme_disable_destroy_qpairs_done);
}

static void
_bdev_nvme_cancel_reconnect_and_disable_ctrlr(void *ctx)
{
	struct nvme_ctrlr *nvme_ctrlr = ctx;

	assert(nvme_ctrlr->resetting == true);
	assert(nvme_ctrlr->thread == spdk_get_thread());

	spdk_poller_unregister(&nvme_ctrlr->reconnect_delay_timer);

	bdev_nvme_disable_ctrlr_complete(nvme_ctrlr);
}

static void
_bdev_nvme_disconnect_and_disable_ctrlr(void *ctx)
{
	struct nvme_ctrlr *nvme_ctrlr = ctx;

	assert(nvme_ctrlr->resetting == true);
	assert(nvme_ctrlr->thread == spdk_get_thread());

	if (!spdk_nvme_ctrlr_is_fabrics(nvme_ctrlr->ctrlr)) {
		nvme_ctrlr_disconnect(nvme_ctrlr, bdev_nvme_disable_destroy_qpairs);
	} else {
		bdev_nvme_disable_destroy_qpairs(nvme_ctrlr);
	}
}

static int
bdev_nvme_disable_ctrlr(struct nvme_ctrlr *nvme_ctrlr)
{
	spdk_msg_fn msg_fn;

	pthread_mutex_lock(&nvme_ctrlr->mutex);
	if (nvme_ctrlr->destruct) {
		pthread_mutex_unlock(&nvme_ctrlr->mutex);
		return -ENXIO;
	}

	if (nvme_ctrlr->resetting) {
		pthread_mutex_unlock(&nvme_ctrlr->mutex);
		return -EBUSY;
	}

	if (nvme_ctrlr->disabled) {
		pthread_mutex_unlock(&nvme_ctrlr->mutex);
		return -EALREADY;
	}

	nvme_ctrlr->resetting = true;
	nvme_ctrlr->dont_retry = true;

	if (nvme_ctrlr->reconnect_is_delayed) {
		msg_fn = _bdev_nvme_cancel_reconnect_and_disable_ctrlr;
		nvme_ctrlr->reconnect_is_delayed = false;
	} else {
		msg_fn = _bdev_nvme_disconnect_and_disable_ctrlr;
	}

	nvme_ctrlr->reset_start_tsc = spdk_get_ticks();

	pthread_mutex_unlock(&nvme_ctrlr->mutex);

	spdk_thread_send_msg(nvme_ctrlr->thread, msg_fn, nvme_ctrlr);
	return 0;
}

static int
nvme_ctrlr_op(struct nvme_ctrlr *nvme_ctrlr, enum nvme_ctrlr_op op,
	      bdev_nvme_ctrlr_op_cb cb_fn, void *cb_arg)
@@ -2311,6 +2494,12 @@ nvme_ctrlr_op(struct nvme_ctrlr *nvme_ctrlr, enum nvme_ctrlr_op op,
	case NVME_CTRLR_OP_RESET:
		rc = bdev_nvme_reset_ctrlr(nvme_ctrlr);
		break;
	case NVME_CTRLR_OP_ENABLE:
		rc = bdev_nvme_enable_ctrlr(nvme_ctrlr);
		break;
	case NVME_CTRLR_OP_DISABLE:
		rc = bdev_nvme_disable_ctrlr(nvme_ctrlr);
		break;
	default:
		rc = -EINVAL;
		break;
@@ -2380,6 +2569,8 @@ nvme_ctrlr_op_rpc(struct nvme_ctrlr *nvme_ctrlr, enum nvme_ctrlr_op op,
	rc = nvme_ctrlr_op(nvme_ctrlr, op, nvme_ctrlr_op_rpc_complete, ctx);
	if (rc == 0) {
		return;
	} else if (rc == -EALREADY) {
		rc = 0;
	}

	nvme_ctrlr_op_rpc_complete(ctx, rc);
@@ -2410,6 +2601,9 @@ _nvme_bdev_ctrlr_op_rpc_continue(void *_ctx)
	if (rc == 0) {
		ctx->nvme_ctrlr = next_nvme_ctrlr;
		return;
	} else if (rc == -EALREADY) {
		ctx->nvme_ctrlr = next_nvme_ctrlr;
		rc = 0;
	}

	ctx->rc = rc;
@@ -2458,6 +2652,9 @@ nvme_bdev_ctrlr_op_rpc(struct nvme_bdev_ctrlr *nbdev_ctrlr, enum nvme_ctrlr_op o
	if (rc == 0) {
		ctx->nvme_ctrlr = nvme_ctrlr;
		return;
	} else if (rc == -EALREADY) {
		ctx->nvme_ctrlr = nvme_ctrlr;
		rc = 0;
	}

	nvme_bdev_ctrlr_op_rpc_continue(ctx, rc);
@@ -2587,8 +2784,8 @@ bdev_nvme_reset_io(struct nvme_bdev_channel *nbdev_ch, struct nvme_bdev_io *bio)

	rc = _bdev_nvme_reset_io(io_path, bio);
	if (rc != 0) {
		bio->cpl.cdw0 = 1;
		bdev_nvme_reset_io_complete(bio);
		/* If the current nvme_ctrlr is disabled, skip it and move to the next nvme_ctrlr. */
		bdev_nvme_reset_io_continue(bio, rc == -EALREADY);
	}
}

@@ -2622,6 +2819,13 @@ bdev_nvme_failover_ctrlr_unsafe(struct nvme_ctrlr *nvme_ctrlr, bool remove)
		return -EALREADY;
	}

	if (nvme_ctrlr->disabled) {
		SPDK_NOTICELOG("Controller is disabled.\n");

		/* We rely on the enablement for the failover. */
		return -EALREADY;
	}

	nvme_ctrlr->resetting = true;
	nvme_ctrlr->in_failover = true;

@@ -3218,6 +3422,8 @@ nvme_ctrlr_get_state_str(struct nvme_ctrlr *nvme_ctrlr)
		return "resetting";
	} else if (nvme_ctrlr->reconnect_is_delayed > 0) {
		return "reconnect_is_delayed";
	} else if (nvme_ctrlr->disabled) {
		return "disabled";
	} else {
		return "enabled";
	}
+3 −0
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ struct nvme_ctrlr {
	uint32_t				ana_log_page_updating : 1;
	uint32_t				io_path_cache_clearing : 1;
	uint32_t				dont_retry : 1;
	uint32_t				disabled : 1;

	struct nvme_ctrlr_opts			opts;

@@ -341,6 +342,8 @@ int bdev_nvme_delete(const char *name, const struct nvme_path_id *path_id);

enum nvme_ctrlr_op {
	NVME_CTRLR_OP_RESET = 1,
	NVME_CTRLR_OP_ENABLE,
	NVME_CTRLR_OP_DISABLE,
};

/**
+184 −0
Original line number Diff line number Diff line
@@ -6991,6 +6991,189 @@ test_bdev_ctrlr_op_rpc(void)
	CU_ASSERT(nvme_bdev_ctrlr_get_by_name("nvme0") == NULL);
}

static void
test_disable_enable_ctrlr(void)
{
	struct spdk_nvme_transport_id trid = {};
	struct spdk_nvme_ctrlr ctrlr = {};
	struct nvme_ctrlr *nvme_ctrlr = NULL;
	struct nvme_path_id *curr_trid;
	struct spdk_io_channel *ch1, *ch2;
	struct nvme_ctrlr_channel *ctrlr_ch1, *ctrlr_ch2;
	int rc;

	ut_init_trid(&trid);
	TAILQ_INIT(&ctrlr.active_io_qpairs);
	ctrlr.adminq.is_connected = true;

	set_thread(0);

	rc = nvme_ctrlr_create(&ctrlr, "nvme0", &trid, NULL);
	CU_ASSERT(rc == 0);

	nvme_ctrlr = nvme_ctrlr_get_by_name("nvme0");
	SPDK_CU_ASSERT_FATAL(nvme_ctrlr != NULL);

	curr_trid = TAILQ_FIRST(&nvme_ctrlr->trids);
	SPDK_CU_ASSERT_FATAL(curr_trid != NULL);

	ch1 = spdk_get_io_channel(nvme_ctrlr);
	SPDK_CU_ASSERT_FATAL(ch1 != NULL);

	ctrlr_ch1 = spdk_io_channel_get_ctx(ch1);
	CU_ASSERT(ctrlr_ch1->qpair != NULL);

	set_thread(1);

	ch2 = spdk_get_io_channel(nvme_ctrlr);
	SPDK_CU_ASSERT_FATAL(ch2 != NULL);

	ctrlr_ch2 = spdk_io_channel_get_ctx(ch2);
	CU_ASSERT(ctrlr_ch2->qpair != NULL);

	/* Disable starts from thread 1. */
	set_thread(1);

	/* Case 1: ctrlr is already disabled. */
	nvme_ctrlr->disabled = true;

	rc = bdev_nvme_disable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == -EALREADY);

	/* Case 2: ctrlr is already being destructed. */
	nvme_ctrlr->disabled = false;
	nvme_ctrlr->destruct = true;

	rc = bdev_nvme_disable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == -ENXIO);

	/* Case 3: reset is in progress. */
	nvme_ctrlr->destruct = false;
	nvme_ctrlr->resetting = true;

	rc = bdev_nvme_disable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == -EBUSY);

	/* Case 4: disable completes successfully. */
	nvme_ctrlr->resetting = false;

	rc = bdev_nvme_disable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == 0);
	CU_ASSERT(nvme_ctrlr->resetting == true);
	CU_ASSERT(ctrlr_ch1->qpair != NULL);
	CU_ASSERT(ctrlr_ch2->qpair != NULL);

	poll_thread_times(0, 3);
	CU_ASSERT(ctrlr_ch1->qpair->qpair == NULL);
	CU_ASSERT(ctrlr_ch2->qpair->qpair != NULL);

	poll_thread_times(0, 1);
	poll_thread_times(1, 1);
	CU_ASSERT(ctrlr_ch1->qpair->qpair == NULL);
	CU_ASSERT(ctrlr_ch2->qpair->qpair == NULL);

	poll_thread_times(1, 1);
	poll_thread_times(0, 1);
	CU_ASSERT(ctrlr.adminq.is_connected == false);
	poll_thread_times(1, 1);
	poll_thread_times(0, 1);
	poll_thread_times(1, 1);
	poll_thread_times(0, 1);
	CU_ASSERT(nvme_ctrlr->resetting == false);
	CU_ASSERT(nvme_ctrlr->disabled == true);

	/* Case 5: enable completes successfully. */
	rc = bdev_nvme_enable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == 0);

	CU_ASSERT(nvme_ctrlr->resetting == true);
	CU_ASSERT(nvme_ctrlr->disabled == false);

	spdk_delay_us(g_opts.nvme_adminq_poll_period_us);
	poll_thread_times(0, 2);
	CU_ASSERT(ctrlr.adminq.is_connected == true);

	poll_thread_times(0, 1);
	CU_ASSERT(ctrlr_ch1->qpair->qpair != NULL);
	CU_ASSERT(ctrlr_ch2->qpair->qpair == NULL);

	poll_thread_times(1, 1);
	CU_ASSERT(ctrlr_ch1->qpair->qpair != NULL);
	CU_ASSERT(ctrlr_ch2->qpair->qpair != NULL);
	CU_ASSERT(nvme_ctrlr->resetting == true);

	poll_thread_times(0, 2);
	CU_ASSERT(nvme_ctrlr->resetting == true);
	poll_thread_times(1, 1);
	CU_ASSERT(nvme_ctrlr->resetting == true);
	poll_thread_times(0, 1);
	CU_ASSERT(nvme_ctrlr->resetting == false);

	/* Case 6: ctrlr is already enabled. */
	rc = bdev_nvme_enable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == -EALREADY);

	set_thread(0);

	/* Case 7: disable cancels delayed reconnect. */
	nvme_ctrlr->opts.reconnect_delay_sec = 10;
	ctrlr.fail_reset = true;

	rc = bdev_nvme_reset_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == 0);

	poll_threads();

	CU_ASSERT(nvme_ctrlr->resetting == false);
	CU_ASSERT(ctrlr.is_failed == false);
	CU_ASSERT(ctrlr_ch1->qpair->qpair == NULL);
	CU_ASSERT(ctrlr_ch2->qpair->qpair == NULL);
	CU_ASSERT(nvme_ctrlr->reconnect_delay_timer != NULL);
	CU_ASSERT(nvme_ctrlr->reconnect_is_delayed == true);

	rc = bdev_nvme_disable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == 0);

	CU_ASSERT(nvme_ctrlr->resetting == true);
	CU_ASSERT(nvme_ctrlr->reconnect_is_delayed == false);

	poll_threads();
	spdk_delay_us(g_opts.nvme_adminq_poll_period_us);
	poll_threads();

	CU_ASSERT(nvme_ctrlr->resetting == false);
	CU_ASSERT(nvme_ctrlr->disabled == true);

	rc = bdev_nvme_enable_ctrlr(nvme_ctrlr);
	CU_ASSERT(rc == 0);

	CU_ASSERT(nvme_ctrlr->resetting == true);
	CU_ASSERT(nvme_ctrlr->disabled == false);

	poll_threads();

	CU_ASSERT(nvme_ctrlr->resetting == false);

	set_thread(1);

	spdk_put_io_channel(ch2);

	set_thread(0);

	spdk_put_io_channel(ch1);

	poll_threads();

	rc = bdev_nvme_delete("nvme0", &g_any_path);
	CU_ASSERT(rc == 0);

	poll_threads();
	spdk_delay_us(1000);
	poll_threads();

	CU_ASSERT(nvme_ctrlr_get_by_name("nvme0") == NULL);
}

int
main(int argc, const char **argv)
{
@@ -7047,6 +7230,7 @@ main(int argc, const char **argv)
	CU_ADD_TEST(suite, test_race_between_reset_and_disconnected);
	CU_ADD_TEST(suite, test_ctrlr_op_rpc);
	CU_ADD_TEST(suite, test_bdev_ctrlr_op_rpc);
	CU_ADD_TEST(suite, test_disable_enable_ctrlr);

	CU_basic_set_mode(CU_BRM_VERBOSE);