nvmf: add namespace reservation acquire command support

A registrant can obtain a reservation on a namespace by executing
acquire command. Acquire command is associated with specific namespace.
For now only Acquire and Preempt reservation acquire action is
supported, Preempt And Abort will be supported in future.

Change-Id: Ifcbb6b414827393ffc266ceada5982b743716321
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/436937
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
This commit is contained in:
Changpeng Liu 2019-02-28 07:56:04 -05:00
parent bc1d0b91b5
commit 71ac18d1ad
3 changed files with 182 additions and 0 deletions

View File

@ -2128,6 +2128,7 @@ spdk_nvmf_ctrlr_process_io_cmd(struct spdk_nvmf_request *req)
case SPDK_NVME_OPC_DATASET_MANAGEMENT:
return spdk_nvmf_bdev_ctrlr_dsm_cmd(bdev, desc, ch, req);
case SPDK_NVME_OPC_RESERVATION_REGISTER:
case SPDK_NVME_OPC_RESERVATION_ACQUIRE:
spdk_thread_send_msg(ctrlr->subsys->thread, spdk_nvmf_ns_reservation_request, req);
return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
default:

View File

@ -190,6 +190,12 @@ struct spdk_nvmf_ns {
uint32_t gen;
/* registrants head */
TAILQ_HEAD(, spdk_nvmf_registrant) registrants;
/* current reservation key */
uint64_t crkey;
/* reservation type */
enum spdk_nvme_reservation_type rtype;
/* current reservation holder, only valid if reservation type can only have one holder */
struct spdk_nvmf_registrant *holder;
};
struct spdk_nvmf_qpair {

View File

@ -1290,6 +1290,30 @@ nvmf_ns_reservation_get_registrant(struct spdk_nvmf_ns *ns,
return NULL;
}
/* current reservation type is all registrants or not */
static bool
nvmf_ns_reservation_all_registrants_type(struct spdk_nvmf_ns *ns)
{
return (ns->rtype == SPDK_NVME_RESERVE_WRITE_EXCLUSIVE_ALL_REGS ||
ns->rtype == SPDK_NVME_RESERVE_EXCLUSIVE_ACCESS_ALL_REGS);
}
/* current registrant is reservation holder or not */
static bool
nvmf_ns_reservation_registrant_is_holder(struct spdk_nvmf_ns *ns,
struct spdk_nvmf_registrant *reg)
{
if (!reg) {
return false;
}
if (nvmf_ns_reservation_all_registrants_type(ns)) {
return true;
}
return (ns->holder == reg);
}
static int
nvmf_ns_reservation_add_registrant(struct spdk_nvmf_ns *ns,
struct spdk_nvmf_ctrlr *ctrlr,
@ -1321,6 +1345,49 @@ nvmf_ns_reservation_remove_registrant(struct spdk_nvmf_ns *ns,
return;
}
static uint32_t
nvmf_ns_reservation_remove_registrants_by_key(struct spdk_nvmf_ns *ns,
uint64_t rkey)
{
struct spdk_nvmf_registrant *reg, *tmp;
uint32_t count = 0;
TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, tmp) {
if (reg->rkey == rkey) {
nvmf_ns_reservation_remove_registrant(ns, reg);
count++;
}
}
return count;
}
static uint32_t
nvmf_ns_reservation_remove_all_other_registrants(struct spdk_nvmf_ns *ns,
struct spdk_nvmf_registrant *reg)
{
struct spdk_nvmf_registrant *reg_tmp, *reg_tmp2;
uint32_t count = 0;
TAILQ_FOREACH_SAFE(reg_tmp, &ns->registrants, link, reg_tmp2) {
if (reg_tmp != reg) {
nvmf_ns_reservation_remove_registrant(ns, reg_tmp);
count++;
}
}
return count;
}
static void
nvmf_ns_reservation_acquire_reservation(struct spdk_nvmf_ns *ns, uint64_t rkey,
enum spdk_nvme_reservation_type rtype,
struct spdk_nvmf_registrant *holder)
{
ns->rtype = rtype;
ns->crkey = rkey;
assert(ns->holder == NULL);
ns->holder = holder;
}
static void
nvmf_ns_reservation_register(struct spdk_nvmf_ns *ns,
struct spdk_nvmf_ctrlr *ctrlr,
@ -1411,6 +1478,111 @@ exit:
return;
}
static void
nvmf_ns_reservation_acquire(struct spdk_nvmf_ns *ns,
struct spdk_nvmf_ctrlr *ctrlr,
struct spdk_nvmf_request *req)
{
struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
uint8_t racqa, iekey, rtype;
struct spdk_nvme_reservation_acquire_data key;
struct spdk_nvmf_registrant *reg;
bool all_regs = false;
uint32_t count = 0;
uint8_t status = SPDK_NVME_SC_SUCCESS;
racqa = cmd->cdw10 & 0x7u;
iekey = (cmd->cdw10 >> 3) & 0x1u;
rtype = (cmd->cdw10 >> 8) & 0xffu;
memcpy(&key, req->data, sizeof(key));
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "ACQUIIRE: RACQA %u, IEKEY %u, RTYPE %u, "
"NRKEY 0x%"PRIx64", PRKEY 0x%"PRIx64"\n",
racqa, iekey, rtype, key.crkey, key.prkey);
if (iekey) {
SPDK_ERRLOG("Ignore existing key field set to 1\n");
status = SPDK_NVME_SC_INVALID_FIELD;
goto exit;
}
reg = nvmf_ns_reservation_get_registrant(ns, &ctrlr->hostid);
/* must be registrant and CRKEY must match */
if (!reg || reg->rkey != key.crkey) {
SPDK_ERRLOG("No registrant or current key doesn't match "
"with existing registrant key\n");
status = SPDK_NVME_SC_RESERVATION_CONFLICT;
goto exit;
}
all_regs = nvmf_ns_reservation_all_registrants_type(ns);
switch (racqa) {
case SPDK_NVME_RESERVE_ACQUIRE:
/* it's not an error for the holder to acquire same reservation type again */
if (nvmf_ns_reservation_registrant_is_holder(ns, reg) && ns->rtype == rtype) {
/* do nothing */
} else if (ns->holder == NULL) {
/* fisrt time to acquire the reservation */
nvmf_ns_reservation_acquire_reservation(ns, key.crkey, rtype, reg);
} else {
SPDK_ERRLOG("Invalid rtype or current registrant is not holder\n");
status = SPDK_NVME_SC_RESERVATION_CONFLICT;
goto exit;
}
break;
case SPDK_NVME_RESERVE_PREEMPT:
/* no reservation holder */
if (!ns->holder) {
/* unregister with PRKEY */
nvmf_ns_reservation_remove_registrants_by_key(ns, key.prkey);
break;
}
/* only 1 reservation holder and reservation key is valid */
if (!all_regs) {
/* preempt itself */
if (nvmf_ns_reservation_registrant_is_holder(ns, reg) &&
ns->crkey == key.prkey) {
ns->rtype = rtype;
break;
}
if (ns->crkey == key.prkey) {
nvmf_ns_reservation_remove_registrant(ns, ns->holder);
nvmf_ns_reservation_acquire_reservation(ns, key.crkey, rtype, reg);
} else if (key.prkey != 0) {
nvmf_ns_reservation_remove_registrants_by_key(ns, key.prkey);
} else {
/* PRKEY is zero */
SPDK_ERRLOG("Current PRKEY is zero\n");
status = SPDK_NVME_SC_RESERVATION_CONFLICT;
goto exit;
}
} else {
/* release all other registrants except for the current one */
if (key.prkey == 0) {
nvmf_ns_reservation_remove_all_other_registrants(ns, reg);
assert(ns->holder == reg);
} else {
count = nvmf_ns_reservation_remove_registrants_by_key(ns, key.prkey);
if (count == 0) {
SPDK_ERRLOG("PRKEY doesn't match any registrant\n");
status = SPDK_NVME_SC_RESERVATION_CONFLICT;
goto exit;
}
}
}
break;
default:
break;
}
exit:
req->rsp->nvme_cpl.status.sct = SPDK_NVME_SCT_GENERIC;
req->rsp->nvme_cpl.status.sc = status;
return;
}
static void
spdk_nvmf_ns_reservation_complete(void *ctx)
{
@ -1437,6 +1609,9 @@ spdk_nvmf_ns_reservation_request(void *ctx)
case SPDK_NVME_OPC_RESERVATION_REGISTER:
nvmf_ns_reservation_register(ns, ctrlr, req);
break;
case SPDK_NVME_OPC_RESERVATION_ACQUIRE:
nvmf_ns_reservation_acquire(ns, ctrlr, req);
break;
default:
break;
}