scsi: add SPC2 RESERVE/RELEASE support
Some OS(Windows and VMWARE esxi) will send SPC2 RESERVE/RELEASE commands when formatting the filesystems, but in our previous implementation of reservation feature, we didn't add such support, in specification SPC3/4, they all include one section "Exceptions to SPC-2 RESERVE and RELEASE behavior" feature for compatible support of SCP2 RESERVE/RELEASE, so we add this support now. Fix issue #1414. Change-Id: I65d85ffb3b8f824b0ae4e130f53f01d95735d700 Signed-off-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2802 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Community-CI: Broadcom CI Community-CI: Mellanox Build Bot Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
This commit is contained in:
parent
9d7b1f7525
commit
6b6a3ff91f
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 3
|
||||
SO_VER := 4
|
||||
SO_MINOR := 0
|
||||
|
||||
C_SRCS = dev.c lun.c port.c scsi.c scsi_bdev.c scsi_pr.c scsi_rpc.c task.c
|
||||
|
@ -197,7 +197,11 @@ _scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
|
||||
TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link);
|
||||
if (!lun->removed) {
|
||||
/* Check the command is allowed or not when reservation is exist */
|
||||
rc = scsi_pr_check(task);
|
||||
if (spdk_unlikely(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
|
||||
rc = scsi2_reserve_check(task);
|
||||
} else {
|
||||
rc = scsi_pr_check(task);
|
||||
}
|
||||
if (spdk_unlikely(rc < 0)) {
|
||||
/* Reservation Conflict */
|
||||
rc = SPDK_SCSI_TASK_COMPLETE;
|
||||
|
@ -1712,7 +1712,6 @@ bdev_scsi_process_primary(struct spdk_scsi_task *task)
|
||||
int dbd, pc, page, subpage;
|
||||
int cmd_parsed = 0;
|
||||
|
||||
|
||||
switch (cdb[0]) {
|
||||
case SPDK_SPC_INQUIRY:
|
||||
alloc_len = from_be16(&cdb[3]);
|
||||
@ -1931,6 +1930,22 @@ bdev_scsi_process_primary(struct spdk_scsi_task *task)
|
||||
rc = scsi_pr_in(task, cdb, data, data_len);
|
||||
break;
|
||||
|
||||
case SPDK_SPC2_RESERVE_6:
|
||||
case SPDK_SPC2_RESERVE_10:
|
||||
rc = scsi2_reserve(task, cdb);
|
||||
if (rc == 0) {
|
||||
if (cdb[0] == SPDK_SPC2_RESERVE_10) {
|
||||
rc = from_be16(&cdb[7]);
|
||||
}
|
||||
data_len = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SPDK_SPC2_RELEASE_6:
|
||||
case SPDK_SPC2_RELEASE_10:
|
||||
rc = scsi2_release(task);
|
||||
break;
|
||||
|
||||
default:
|
||||
return SPDK_SCSI_TASK_UNKNOWN;
|
||||
}
|
||||
|
@ -73,8 +73,11 @@ struct spdk_scsi_pr_registrant {
|
||||
TAILQ_ENTRY(spdk_scsi_pr_registrant) link;
|
||||
};
|
||||
|
||||
#define SCSI_SPC2_RESERVE 0x00000001U
|
||||
|
||||
/* Reservation with LU_SCOPE */
|
||||
struct spdk_scsi_pr_reservation {
|
||||
uint32_t flags;
|
||||
struct spdk_scsi_pr_registrant *holder;
|
||||
enum spdk_scsi_pr_type_code rtype;
|
||||
uint64_t crkey;
|
||||
@ -144,6 +147,8 @@ struct spdk_scsi_lun {
|
||||
uint32_t pr_generation;
|
||||
/** Reservation for the LUN */
|
||||
struct spdk_scsi_pr_reservation reservation;
|
||||
/** Reservation holder for SPC2 RESERVE(6) and RESERVE(10) */
|
||||
struct spdk_scsi_pr_registrant scsi2_holder;
|
||||
|
||||
/** List of open descriptors for this LUN. */
|
||||
TAILQ_HEAD(, spdk_scsi_lun_desc) open_descs;
|
||||
@ -196,6 +201,10 @@ int scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16
|
||||
int scsi_pr_in(struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len);
|
||||
int scsi_pr_check(struct spdk_scsi_task *task);
|
||||
|
||||
int scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb);
|
||||
int scsi2_release(struct spdk_scsi_task *task);
|
||||
int scsi2_reserve_check(struct spdk_scsi_task *task);
|
||||
|
||||
struct spdk_scsi_globals {
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
@ -53,6 +53,23 @@ scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
scsi2_it_nexus_is_holder(struct spdk_scsi_lun *lun,
|
||||
struct spdk_scsi_port *initiator_port,
|
||||
struct spdk_scsi_port *target_port)
|
||||
{
|
||||
struct spdk_scsi_pr_registrant *reg = lun->reservation.holder;
|
||||
|
||||
assert(reg != NULL);
|
||||
|
||||
if ((reg->initiator_port == initiator_port) &&
|
||||
(reg->target_port == target_port)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reservation type is all registrants or not */
|
||||
static inline bool
|
||||
scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
|
||||
@ -638,8 +655,9 @@ scsi_pr_in_report_capabilities(struct spdk_scsi_task *task,
|
||||
param = (struct spdk_scsi_pr_in_report_capabilities_data *)data;
|
||||
|
||||
memset(param, 0, sizeof(*param));
|
||||
/* TODO: can support more capabilities bits */
|
||||
to_be16(¶m->length, sizeof(*param));
|
||||
/* Compatible reservation handling to support RESERVE/RELEASE defined in SPC-2 */
|
||||
param->crh = 1;
|
||||
param->tmv = 1;
|
||||
param->wr_ex = 1;
|
||||
param->ex_ac = 1;
|
||||
@ -775,6 +793,12 @@ scsi_pr_check(struct spdk_scsi_task *task)
|
||||
case SPDK_SBC_READ_CAPACITY_10:
|
||||
case SPDK_SPC_PERSISTENT_RESERVE_IN:
|
||||
case SPDK_SPC_SERVICE_ACTION_IN_16:
|
||||
/* CRH enabled, processed by scsi2_reserve() */
|
||||
case SPDK_SPC2_RESERVE_6:
|
||||
case SPDK_SPC2_RESERVE_10:
|
||||
/* CRH enabled, processed by scsi2_release() */
|
||||
case SPDK_SPC2_RELEASE_6:
|
||||
case SPDK_SPC2_RELEASE_10:
|
||||
return 0;
|
||||
case SPDK_SPC_MODE_SELECT_6:
|
||||
case SPDK_SPC_MODE_SELECT_10:
|
||||
@ -878,3 +902,166 @@ conflict:
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
scsi2_check_reservation_conflict(struct spdk_scsi_task *task)
|
||||
{
|
||||
struct spdk_scsi_lun *lun = task->lun;
|
||||
struct spdk_scsi_pr_registrant *reg;
|
||||
bool conflict = false;
|
||||
|
||||
reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
|
||||
if (reg) {
|
||||
/*
|
||||
* From spc4r31 5.9.3 Exceptions to SPC-2 RESERVE and RELEASE
|
||||
* behavior
|
||||
*
|
||||
* A RESERVE(6) or RESERVE(10) command shall complete with GOOD
|
||||
* status, but no reservation shall be established and the
|
||||
* persistent reservation shall not be changed, if the command
|
||||
* is received from a) and b) below.
|
||||
*
|
||||
* A RELEASE(6) or RELEASE(10) command shall complete with GOOD
|
||||
* status, but the persistent reservation shall not be released,
|
||||
* if the command is received from a) and b)
|
||||
*
|
||||
* a) An I_T nexus that is a persistent reservation holder; or
|
||||
* b) An I_T nexus that is registered if a registrants only or
|
||||
* all registrants type persistent reservation is present.
|
||||
*
|
||||
* In all other cases, a RESERVE(6) command, RESERVE(10) command,
|
||||
* RELEASE(6) command, or RELEASE(10) command shall be processed
|
||||
* as defined in SPC-2.
|
||||
*/
|
||||
if (scsi_pr_registrant_is_holder(lun, reg)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY ||
|
||||
lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
conflict = true;
|
||||
} else {
|
||||
/*
|
||||
* From spc2r20 5.5.1 Reservations overview:
|
||||
*
|
||||
* If a logical unit has executed a PERSISTENT RESERVE OUT
|
||||
* command with the REGISTER or the REGISTER AND IGNORE
|
||||
* EXISTING KEY service action and is still registered by any
|
||||
* initiator, all RESERVE commands and all RELEASE commands
|
||||
* regardless of initiator shall conflict and shall terminate
|
||||
* with a RESERVATION CONFLICT status.
|
||||
*/
|
||||
conflict = TAILQ_EMPTY(&lun->reg_head) ? false : true;
|
||||
}
|
||||
|
||||
if (conflict) {
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
|
||||
SPDK_SCSI_SENSE_NO_SENSE,
|
||||
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb)
|
||||
{
|
||||
struct spdk_scsi_lun *lun = task->lun;
|
||||
struct spdk_scsi_pr_registrant *reg = &lun->scsi2_holder;
|
||||
int ret;
|
||||
|
||||
/* Obsolete Bits and LongID set, returning ILLEGAL_REQUEST */
|
||||
if (cdb[1] & 0x3) {
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = scsi2_check_reservation_conflict(task);
|
||||
/* PERSISTENT RESERVE is enabled */
|
||||
if (ret == 1) {
|
||||
return 0;
|
||||
} else if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* SPC2 RESERVE */
|
||||
reg->initiator_port = task->initiator_port;
|
||||
if (task->initiator_port) {
|
||||
snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
|
||||
task->initiator_port->name);
|
||||
reg->transport_id_len = task->initiator_port->transport_id_len;
|
||||
memcpy(reg->transport_id, task->initiator_port->transport_id,
|
||||
reg->transport_id_len);
|
||||
}
|
||||
reg->target_port = task->target_port;
|
||||
if (task->target_port) {
|
||||
snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
|
||||
task->target_port->name);
|
||||
}
|
||||
|
||||
lun->reservation.flags = SCSI_SPC2_RESERVE;
|
||||
lun->reservation.holder = &lun->scsi2_holder;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
scsi2_release(struct spdk_scsi_task *task)
|
||||
{
|
||||
struct spdk_scsi_lun *lun = task->lun;
|
||||
int ret;
|
||||
|
||||
ret = scsi2_check_reservation_conflict(task);
|
||||
/* PERSISTENT RESERVE is enabled */
|
||||
if (ret == 1) {
|
||||
return 0;
|
||||
} else if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(lun->reservation.flags & SCSI_SPC2_RESERVE);
|
||||
|
||||
memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
|
||||
memset(&lun->scsi2_holder, 0, sizeof(struct spdk_scsi_pr_registrant));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scsi2_reserve_check(struct spdk_scsi_task *task)
|
||||
{
|
||||
struct spdk_scsi_lun *lun = task->lun;
|
||||
uint8_t *cdb = task->cdb;
|
||||
|
||||
switch (cdb[0]) {
|
||||
case SPDK_SPC_INQUIRY:
|
||||
case SPDK_SPC2_RELEASE_6:
|
||||
case SPDK_SPC2_RELEASE_10:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* no reservation holders */
|
||||
if (!scsi_pr_has_reservation(lun)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (scsi2_it_nexus_is_holder(lun, task->initiator_port, task->target_port)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
|
||||
SPDK_SCSI_SENSE_NO_SENSE,
|
||||
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return -1;
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ DEFINE_STUB_V(spdk_scsi_dev_delete_lun,
|
||||
(struct spdk_scsi_dev *dev, struct spdk_scsi_lun *lun));
|
||||
|
||||
DEFINE_STUB(scsi_pr_check, int, (struct spdk_scsi_task *task), 0);
|
||||
DEFINE_STUB(scsi2_reserve_check, int, (struct spdk_scsi_task *task), 0);
|
||||
|
||||
void
|
||||
bdev_scsi_reset(struct spdk_scsi_task *task)
|
||||
|
@ -104,6 +104,9 @@ DEFINE_STUB(scsi_pr_out, int, (struct spdk_scsi_task *task,
|
||||
DEFINE_STUB(scsi_pr_in, int, (struct spdk_scsi_task *task, uint8_t *cdb,
|
||||
uint8_t *data, uint16_t data_len), 0);
|
||||
|
||||
DEFINE_STUB(scsi2_reserve, int, (struct spdk_scsi_task *task, uint8_t *cdb), 0);
|
||||
DEFINE_STUB(scsi2_release, int, (struct spdk_scsi_task *task), 0);
|
||||
|
||||
void
|
||||
scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user