nvmf: Add the Keep Alive feature
The controller shall treat a Keep Alive Timeout in the same manner as connection loss. If the Keep Alive feature is in use and the timer expires, then the controller shall: 1, stop processing commands and set the Controller Fatal Status (CSTS,CFS) bit to '1'; 2, terminate the NVMe Transport connection; 3, break the host to controller association; A timer poller is added to each subsystem to monitor timeout event. Change-Id: I001afab8a6764f30c39df37fa96384180d117486 Signed-off-by: JinYu <jin.yu@intel.com> Reviewed-on: https://review.gerrithub.io/c/439330 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
This commit is contained in:
parent
058c4c890b
commit
b8769cdb08
188
lib/nvmf/ctrlr.c
188
lib/nvmf/ctrlr.c
@ -47,7 +47,10 @@
|
|||||||
|
|
||||||
#include "spdk_internal/log.h"
|
#include "spdk_internal/log.h"
|
||||||
|
|
||||||
#define MIN_KEEP_ALIVE_TIMEOUT 10000
|
#define MIN_KEEP_ALIVE_TIMEOUT_IN_MS 10000
|
||||||
|
#define NVMF_DISC_KATO_IN_MS 120000
|
||||||
|
#define KAS_DEFAULT_VALUE 10
|
||||||
|
#define KAS_TIME_UNIT_IN_MS 100
|
||||||
|
|
||||||
#define MODEL_NUMBER "SPDK bdev Controller"
|
#define MODEL_NUMBER "SPDK bdev Controller"
|
||||||
|
|
||||||
@ -72,6 +75,110 @@ spdk_nvmf_invalid_connect_response(struct spdk_nvmf_fabric_connect_rsp *rsp,
|
|||||||
#define SPDK_NVMF_INVALID_CONNECT_DATA(rsp, field) \
|
#define SPDK_NVMF_INVALID_CONNECT_DATA(rsp, field) \
|
||||||
spdk_nvmf_invalid_connect_response(rsp, 1, offsetof(struct spdk_nvmf_fabric_connect_data, field))
|
spdk_nvmf_invalid_connect_response(rsp, 1, offsetof(struct spdk_nvmf_fabric_connect_data, field))
|
||||||
|
|
||||||
|
static void
|
||||||
|
spdk_nvmf_ctrlr_stop_keep_alive_timer(struct spdk_nvmf_ctrlr *ctrlr)
|
||||||
|
{
|
||||||
|
if (!ctrlr) {
|
||||||
|
SPDK_ERRLOG("Controller is NULL\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctrlr->keep_alive_poller == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Stop keep alive poller\n");
|
||||||
|
spdk_poller_unregister(&ctrlr->keep_alive_poller);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spdk_nvmf_ctrlr_disconnect_qpairs_done(struct spdk_io_channel_iter *i, int status)
|
||||||
|
{
|
||||||
|
if (status == 0) {
|
||||||
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "ctrlr disconnect qpairs complete successfully\n");
|
||||||
|
} else {
|
||||||
|
SPDK_ERRLOG("Fail to disconnect ctrlr qpairs\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spdk_nvmf_ctrlr_disconnect_qpairs_on_pg(struct spdk_io_channel_iter *i)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
struct spdk_nvmf_ctrlr *ctrlr;
|
||||||
|
struct spdk_nvmf_qpair *qpair, *temp_qpair;
|
||||||
|
struct spdk_io_channel *ch;
|
||||||
|
struct spdk_nvmf_poll_group *group;
|
||||||
|
|
||||||
|
ctrlr = spdk_io_channel_iter_get_ctx(i);
|
||||||
|
ch = spdk_io_channel_iter_get_channel(i);
|
||||||
|
group = spdk_io_channel_get_ctx(ch);
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(qpair, &group->qpairs, link, temp_qpair) {
|
||||||
|
if (qpair->ctrlr == ctrlr) {
|
||||||
|
rc = spdk_nvmf_qpair_disconnect(qpair, NULL, NULL);
|
||||||
|
if (rc) {
|
||||||
|
SPDK_ERRLOG("Qpair disconnect failed\n");
|
||||||
|
goto next_channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_channel:
|
||||||
|
spdk_for_each_channel_continue(i, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spdk_nvmf_ctrlr_keep_alive_poll(void *ctx)
|
||||||
|
{
|
||||||
|
uint64_t keep_alive_timeout_tick;
|
||||||
|
uint64_t now = spdk_get_ticks();
|
||||||
|
struct spdk_nvmf_ctrlr *ctrlr = ctx;
|
||||||
|
|
||||||
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Polling ctrlr keep alive timeout\n");
|
||||||
|
|
||||||
|
/* If the Keep alive feature is in use and the timer expires */
|
||||||
|
keep_alive_timeout_tick = ctrlr->last_keep_alive_tick +
|
||||||
|
ctrlr->feat.keep_alive_timer.bits.kato * spdk_get_ticks_hz() / UINT64_C(1000);
|
||||||
|
if (now > keep_alive_timeout_tick) {
|
||||||
|
/* set the Controller Fatal Status bit to '1' */
|
||||||
|
if (ctrlr->vcprop.csts.bits.cfs == 0) {
|
||||||
|
ctrlr->vcprop.csts.bits.cfs = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* disconnect qpairs, terminate Transport connection
|
||||||
|
* destroy ctrlr, break the host to controller association
|
||||||
|
* disconnect qpairs with qpair->ctrlr == ctrlr
|
||||||
|
*/
|
||||||
|
spdk_for_each_channel(ctrlr->subsys->tgt,
|
||||||
|
spdk_nvmf_ctrlr_disconnect_qpairs_on_pg,
|
||||||
|
ctrlr,
|
||||||
|
spdk_nvmf_ctrlr_disconnect_qpairs_done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spdk_nvmf_ctrlr_start_keep_alive_timer(struct spdk_nvmf_ctrlr *ctrlr)
|
||||||
|
{
|
||||||
|
if (!ctrlr) {
|
||||||
|
SPDK_ERRLOG("Controller is NULL\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if cleared to 0 then the Keep Alive Timer is disabled */
|
||||||
|
if (ctrlr->feat.keep_alive_timer.bits.kato != 0) {
|
||||||
|
|
||||||
|
ctrlr->last_keep_alive_tick = spdk_get_ticks();
|
||||||
|
|
||||||
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Ctrlr add keep alive poller\n");
|
||||||
|
ctrlr->keep_alive_poller = spdk_poller_register(spdk_nvmf_ctrlr_keep_alive_poll, ctrlr,
|
||||||
|
ctrlr->feat.keep_alive_timer.bits.kato * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ctrlr_add_qpair_and_update_rsp(struct spdk_nvmf_qpair *qpair,
|
ctrlr_add_qpair_and_update_rsp(struct spdk_nvmf_qpair *qpair,
|
||||||
struct spdk_nvmf_ctrlr *ctrlr,
|
struct spdk_nvmf_ctrlr *ctrlr,
|
||||||
@ -121,6 +228,7 @@ _spdk_nvmf_ctrlr_add_admin_qpair(void *ctx)
|
|||||||
struct spdk_nvmf_ctrlr *ctrlr = qpair->ctrlr;
|
struct spdk_nvmf_ctrlr *ctrlr = qpair->ctrlr;
|
||||||
|
|
||||||
ctrlr->admin_qpair = qpair;
|
ctrlr->admin_qpair = qpair;
|
||||||
|
spdk_nvmf_ctrlr_start_keep_alive_timer(ctrlr);
|
||||||
ctrlr_add_qpair_and_update_rsp(qpair, ctrlr, rsp);
|
ctrlr_add_qpair_and_update_rsp(qpair, ctrlr, rsp);
|
||||||
spdk_nvmf_request_complete(req);
|
spdk_nvmf_request_complete(req);
|
||||||
}
|
}
|
||||||
@ -172,10 +280,43 @@ spdk_nvmf_ctrlr_create(struct spdk_nvmf_subsystem *subsystem,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrlr->feat.keep_alive_timer.bits.kato = connect_cmd->kato;
|
/*
|
||||||
|
* Because Identify Controller data KAS default is 10
|
||||||
|
* KAS: this field indicates the granularity of the Keep Alive Timer in 100ms units
|
||||||
|
* keep-alive timeout in milliseconds
|
||||||
|
*/
|
||||||
|
ctrlr->feat.keep_alive_timer.bits.kato = spdk_divide_round_up(connect_cmd->kato,
|
||||||
|
KAS_DEFAULT_VALUE * KAS_TIME_UNIT_IN_MS) *
|
||||||
|
KAS_DEFAULT_VALUE * KAS_TIME_UNIT_IN_MS;
|
||||||
ctrlr->feat.async_event_configuration.bits.ns_attr_notice = 1;
|
ctrlr->feat.async_event_configuration.bits.ns_attr_notice = 1;
|
||||||
ctrlr->feat.volatile_write_cache.bits.wce = 1;
|
ctrlr->feat.volatile_write_cache.bits.wce = 1;
|
||||||
|
|
||||||
|
if (ctrlr->subsys->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) {
|
||||||
|
/* Don't accept keep-alive timeout for discovery controllers */
|
||||||
|
if (ctrlr->feat.keep_alive_timer.bits.kato != 0) {
|
||||||
|
SPDK_ERRLOG("Discovery controller don't accept keep-alive timeout\n");
|
||||||
|
spdk_bit_array_free(&ctrlr->qpair_mask);
|
||||||
|
free(ctrlr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Discovery controllers use some arbitrary high value in order
|
||||||
|
* to cleanup stale discovery sessions
|
||||||
|
*
|
||||||
|
* From the 1.0a nvme-of spec:
|
||||||
|
* "The Keep Alive command is reserved for
|
||||||
|
* Discovery controllers. A transport may specify a
|
||||||
|
* fixed Discovery controller activity timeout value
|
||||||
|
* (e.g., 2 minutes). If no commands are received
|
||||||
|
* by a Discovery controller within that time
|
||||||
|
* period, the controller may perform the
|
||||||
|
* actions for Keep Alive Timer expiration".
|
||||||
|
* kato is in millisecond.
|
||||||
|
*/
|
||||||
|
ctrlr->feat.keep_alive_timer.bits.kato = NVMF_DISC_KATO_IN_MS;
|
||||||
|
}
|
||||||
|
|
||||||
/* Subtract 1 for admin queue, 1 for 0's based */
|
/* Subtract 1 for admin queue, 1 for 0's based */
|
||||||
ctrlr->feat.number_of_queues.bits.ncqr = transport->opts.max_qpairs_per_ctrlr - 1 -
|
ctrlr->feat.number_of_queues.bits.ncqr = transport->opts.max_qpairs_per_ctrlr - 1 -
|
||||||
1;
|
1;
|
||||||
@ -217,12 +358,21 @@ spdk_nvmf_ctrlr_create(struct spdk_nvmf_subsystem *subsystem,
|
|||||||
return ctrlr;
|
return ctrlr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_spdk_nvmf_ctrlr_destruct(void *ctx)
|
||||||
|
{
|
||||||
|
struct spdk_nvmf_ctrlr *ctrlr = ctx;
|
||||||
|
|
||||||
|
spdk_nvmf_ctrlr_stop_keep_alive_timer(ctrlr);
|
||||||
|
free(ctrlr);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
spdk_nvmf_ctrlr_destruct(struct spdk_nvmf_ctrlr *ctrlr)
|
spdk_nvmf_ctrlr_destruct(struct spdk_nvmf_ctrlr *ctrlr)
|
||||||
{
|
{
|
||||||
spdk_nvmf_subsystem_remove_ctrlr(ctrlr->subsys, ctrlr);
|
spdk_nvmf_subsystem_remove_ctrlr(ctrlr->subsys, ctrlr);
|
||||||
|
|
||||||
free(ctrlr);
|
spdk_thread_send_msg(ctrlr->thread, _spdk_nvmf_ctrlr_destruct, ctrlr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -265,7 +415,6 @@ spdk_nvmf_ctrlr_add_io_qpair(void *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctrlr_add_qpair_and_update_rsp(qpair, ctrlr, rsp);
|
ctrlr_add_qpair_and_update_rsp(qpair, ctrlr, rsp);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
spdk_thread_send_msg(qpair->group->thread, _spdk_nvmf_request_complete, req);
|
spdk_thread_send_msg(qpair->group->thread, _spdk_nvmf_request_complete, req);
|
||||||
}
|
}
|
||||||
@ -877,12 +1026,31 @@ spdk_nvmf_ctrlr_set_features_keep_alive_timer(struct spdk_nvmf_request *req)
|
|||||||
|
|
||||||
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Set Features - Keep Alive Timer (%u ms)\n", cmd->cdw11);
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Set Features - Keep Alive Timer (%u ms)\n", cmd->cdw11);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if attempts to disable keep alive by setting kato to 0h
|
||||||
|
* a status value of keep alive invalid shall be returned
|
||||||
|
*/
|
||||||
if (cmd->cdw11 == 0) {
|
if (cmd->cdw11 == 0) {
|
||||||
rsp->status.sc = SPDK_NVME_SC_KEEP_ALIVE_INVALID;
|
rsp->status.sc = SPDK_NVME_SC_KEEP_ALIVE_INVALID;
|
||||||
} else if (cmd->cdw11 < MIN_KEEP_ALIVE_TIMEOUT) {
|
} else if (cmd->cdw11 < MIN_KEEP_ALIVE_TIMEOUT_IN_MS) {
|
||||||
ctrlr->feat.keep_alive_timer.bits.kato = MIN_KEEP_ALIVE_TIMEOUT;
|
ctrlr->feat.keep_alive_timer.bits.kato = MIN_KEEP_ALIVE_TIMEOUT_IN_MS;
|
||||||
} else {
|
} else {
|
||||||
ctrlr->feat.keep_alive_timer.bits.kato = cmd->cdw11;
|
/* round up to milliseconds */
|
||||||
|
ctrlr->feat.keep_alive_timer.bits.kato = spdk_divide_round_up(cmd->cdw11,
|
||||||
|
KAS_DEFAULT_VALUE * KAS_TIME_UNIT_IN_MS) *
|
||||||
|
KAS_DEFAULT_VALUE * KAS_TIME_UNIT_IN_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if change the keep alive timeout value successfully
|
||||||
|
* update the keep alive poller.
|
||||||
|
*/
|
||||||
|
if (cmd->cdw11 != 0) {
|
||||||
|
if (ctrlr->keep_alive_poller != NULL) {
|
||||||
|
spdk_poller_unregister(&ctrlr->keep_alive_poller);
|
||||||
|
}
|
||||||
|
ctrlr->keep_alive_poller = spdk_poller_register(spdk_nvmf_ctrlr_keep_alive_poll, ctrlr,
|
||||||
|
ctrlr->feat.keep_alive_timer.bits.kato * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Set Features - Keep Alive Timer set to %u ms\n",
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Set Features - Keep Alive Timer set to %u ms\n",
|
||||||
@ -1225,7 +1393,7 @@ spdk_nvmf_ctrlr_identify_ctrlr(struct spdk_nvmf_ctrlr *ctrlr, struct spdk_nvme_c
|
|||||||
if (subsystem->subtype == SPDK_NVMF_SUBTYPE_NVME) {
|
if (subsystem->subtype == SPDK_NVMF_SUBTYPE_NVME) {
|
||||||
spdk_strcpy_pad(cdata->mn, MODEL_NUMBER, sizeof(cdata->mn), ' ');
|
spdk_strcpy_pad(cdata->mn, MODEL_NUMBER, sizeof(cdata->mn), ' ');
|
||||||
spdk_strcpy_pad(cdata->sn, spdk_nvmf_subsystem_get_sn(subsystem), sizeof(cdata->sn), ' ');
|
spdk_strcpy_pad(cdata->sn, spdk_nvmf_subsystem_get_sn(subsystem), sizeof(cdata->sn), ' ');
|
||||||
cdata->kas = 10;
|
cdata->kas = KAS_DEFAULT_VALUE;
|
||||||
|
|
||||||
cdata->rab = 6;
|
cdata->rab = 6;
|
||||||
cdata->cmic.multi_port = 1;
|
cdata->cmic.multi_port = 1;
|
||||||
@ -1592,6 +1760,8 @@ spdk_nvmf_ctrlr_set_features(struct spdk_nvmf_request *req)
|
|||||||
static int
|
static int
|
||||||
spdk_nvmf_ctrlr_keep_alive(struct spdk_nvmf_request *req)
|
spdk_nvmf_ctrlr_keep_alive(struct spdk_nvmf_request *req)
|
||||||
{
|
{
|
||||||
|
struct spdk_nvmf_ctrlr *ctrlr = req->qpair->ctrlr;
|
||||||
|
|
||||||
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Keep Alive\n");
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Keep Alive\n");
|
||||||
/*
|
/*
|
||||||
* To handle keep alive just clear or reset the
|
* To handle keep alive just clear or reset the
|
||||||
@ -1601,6 +1771,8 @@ spdk_nvmf_ctrlr_keep_alive(struct spdk_nvmf_request *req)
|
|||||||
* keep alive has exceeded the max duration and
|
* keep alive has exceeded the max duration and
|
||||||
* take appropriate action.
|
* take appropriate action.
|
||||||
*/
|
*/
|
||||||
|
ctrlr->last_keep_alive_tick = spdk_get_ticks();
|
||||||
|
|
||||||
return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
|
return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +236,10 @@ struct spdk_nvmf_ctrlr {
|
|||||||
uint16_t changed_ns_list_count;
|
uint16_t changed_ns_list_count;
|
||||||
struct spdk_nvme_ns_list changed_ns_list;
|
struct spdk_nvme_ns_list changed_ns_list;
|
||||||
|
|
||||||
|
/* Time to trigger keep-alive--poller_time = now_tick + period */
|
||||||
|
uint64_t last_keep_alive_tick;
|
||||||
|
struct spdk_poller *keep_alive_poller;
|
||||||
|
|
||||||
TAILQ_ENTRY(spdk_nvmf_ctrlr) link;
|
TAILQ_ENTRY(spdk_nvmf_ctrlr) link;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -120,6 +120,12 @@ DEFINE_STUB(spdk_nvmf_transport_qpair_set_sqsize,
|
|||||||
(struct spdk_nvmf_qpair *qpair),
|
(struct spdk_nvmf_qpair *qpair),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
|
int
|
||||||
|
spdk_nvmf_qpair_disconnect(struct spdk_nvmf_qpair *qpair, nvmf_qpair_disconnect_cb cb_fn, void *ctx)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_nvmf_ns *ns, struct spdk_nvme_ns_data *nsdata)
|
spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_nvmf_ns *ns, struct spdk_nvme_ns_data *nsdata)
|
||||||
{
|
{
|
||||||
@ -315,6 +321,7 @@ test_connect(void)
|
|||||||
CU_ASSERT(rc == SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS);
|
CU_ASSERT(rc == SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS);
|
||||||
CU_ASSERT(nvme_status_success(&rsp.nvme_cpl.status));
|
CU_ASSERT(nvme_status_success(&rsp.nvme_cpl.status));
|
||||||
CU_ASSERT(qpair.ctrlr != NULL);
|
CU_ASSERT(qpair.ctrlr != NULL);
|
||||||
|
spdk_nvmf_ctrlr_stop_keep_alive_timer(qpair.ctrlr);
|
||||||
spdk_bit_array_free(&qpair.ctrlr->qpair_mask);
|
spdk_bit_array_free(&qpair.ctrlr->qpair_mask);
|
||||||
free(qpair.ctrlr);
|
free(qpair.ctrlr);
|
||||||
qpair.ctrlr = NULL;
|
qpair.ctrlr = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user