diff --git a/lib/nvmf/ctrlr.c b/lib/nvmf/ctrlr.c index dcf48975f..41fac1335 100644 --- a/lib/nvmf/ctrlr.c +++ b/lib/nvmf/ctrlr.c @@ -2129,6 +2129,7 @@ spdk_nvmf_ctrlr_process_io_cmd(struct spdk_nvmf_request *req) return spdk_nvmf_bdev_ctrlr_dsm_cmd(bdev, desc, ch, req); case SPDK_NVME_OPC_RESERVATION_REGISTER: case SPDK_NVME_OPC_RESERVATION_ACQUIRE: + case SPDK_NVME_OPC_RESERVATION_RELEASE: spdk_thread_send_msg(ctrlr->subsys->thread, spdk_nvmf_ns_reservation_request, req); return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS; default: diff --git a/lib/nvmf/subsystem.c b/lib/nvmf/subsystem.c index 70feb9893..a2acd2ca1 100644 --- a/lib/nvmf/subsystem.c +++ b/lib/nvmf/subsystem.c @@ -1335,6 +1335,14 @@ nvmf_ns_reservation_add_registrant(struct spdk_nvmf_ns *ns, return 0; } +static void +nvmf_ns_reservation_release_reservation(struct spdk_nvmf_ns *ns) +{ + ns->rtype = 0; + ns->crkey = 0; + ns->holder = NULL; +} + /* release the reservation if the last registrant was removed */ static void nvmf_ns_reservation_check_release_on_remove_registrant(struct spdk_nvmf_ns *ns, @@ -1354,9 +1362,7 @@ nvmf_ns_reservation_check_release_on_remove_registrant(struct spdk_nvmf_ns *ns, ns->holder = next_reg; } else if (nvmf_ns_reservation_registrant_is_holder(ns, reg)) { /* release the reservation */ - ns->rtype = 0; - ns->crkey = 0; - ns->holder = NULL; + nvmf_ns_reservation_release_reservation(ns); } } @@ -1403,6 +1409,19 @@ nvmf_ns_reservation_remove_all_other_registrants(struct spdk_nvmf_ns *ns, return count; } +static uint32_t +nvmf_ns_reservation_clear_all_registrants(struct spdk_nvmf_ns *ns) +{ + struct spdk_nvmf_registrant *reg, *reg_tmp; + uint32_t count = 0; + + TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, reg_tmp) { + nvmf_ns_reservation_remove_registrant(ns, reg); + count++; + } + return count; +} + static void nvmf_ns_reservation_acquire_reservation(struct spdk_nvmf_ns *ns, uint64_t rkey, enum spdk_nvme_reservation_type rtype, @@ -1609,6 +1628,70 @@ exit: return; } +static void +nvmf_ns_reservation_release(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 rrela, iekey, rtype; + struct spdk_nvmf_registrant *reg; + uint64_t crkey; + uint8_t status = SPDK_NVME_SC_SUCCESS; + + rrela = cmd->cdw10 & 0x7u; + iekey = (cmd->cdw10 >> 3) & 0x1u; + rtype = (cmd->cdw10 >> 8) & 0xffu; + memcpy(&crkey, req->data, sizeof(crkey)); + + SPDK_DEBUGLOG(SPDK_LOG_NVMF, "RELEASE: RRELA %u, IEKEY %u, RTYPE %u, " + "CRKEY 0x%"PRIx64"\n", rrela, iekey, rtype, crkey); + + 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); + if (!reg || reg->rkey != crkey) { + SPDK_ERRLOG("No registrant or current key doesn't match " + "with existing registrant key\n"); + status = SPDK_NVME_SC_RESERVATION_CONFLICT; + goto exit; + } + + switch (rrela) { + case SPDK_NVME_RESERVE_RELEASE: + if (!ns->holder) { + SPDK_DEBUGLOG(SPDK_LOG_NVMF, "RELEASE: no holder\n"); + goto exit; + } + if (ns->rtype != rtype) { + SPDK_ERRLOG("Type doesn't match\n"); + status = SPDK_NVME_SC_INVALID_FIELD; + goto exit; + } + if (!nvmf_ns_reservation_registrant_is_holder(ns, reg)) { + /* not the reservation holder, this isn't an error */ + goto exit; + } + nvmf_ns_reservation_release_reservation(ns); + break; + case SPDK_NVME_RESERVE_CLEAR: + nvmf_ns_reservation_clear_all_registrants(ns); + break; + default: + status = SPDK_NVME_SC_INVALID_FIELD; + goto exit; + } + +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) { @@ -1638,6 +1721,9 @@ spdk_nvmf_ns_reservation_request(void *ctx) case SPDK_NVME_OPC_RESERVATION_ACQUIRE: nvmf_ns_reservation_acquire(ns, ctrlr, req); break; + case SPDK_NVME_OPC_RESERVATION_RELEASE: + nvmf_ns_reservation_release(ns, ctrlr, req); + break; default: break; }