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)/../..)
|
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||||
|
|
||||||
SO_VER := 3
|
SO_VER := 4
|
||||||
SO_MINOR := 0
|
SO_MINOR := 0
|
||||||
|
|
||||||
C_SRCS = dev.c lun.c port.c scsi.c scsi_bdev.c scsi_pr.c scsi_rpc.c task.c
|
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);
|
TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link);
|
||||||
if (!lun->removed) {
|
if (!lun->removed) {
|
||||||
/* Check the command is allowed or not when reservation is exist */
|
/* Check the command is allowed or not when reservation is exist */
|
||||||
|
if (spdk_unlikely(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
|
||||||
|
rc = scsi2_reserve_check(task);
|
||||||
|
} else {
|
||||||
rc = scsi_pr_check(task);
|
rc = scsi_pr_check(task);
|
||||||
|
}
|
||||||
if (spdk_unlikely(rc < 0)) {
|
if (spdk_unlikely(rc < 0)) {
|
||||||
/* Reservation Conflict */
|
/* Reservation Conflict */
|
||||||
rc = SPDK_SCSI_TASK_COMPLETE;
|
rc = SPDK_SCSI_TASK_COMPLETE;
|
||||||
|
@ -1712,7 +1712,6 @@ bdev_scsi_process_primary(struct spdk_scsi_task *task)
|
|||||||
int dbd, pc, page, subpage;
|
int dbd, pc, page, subpage;
|
||||||
int cmd_parsed = 0;
|
int cmd_parsed = 0;
|
||||||
|
|
||||||
|
|
||||||
switch (cdb[0]) {
|
switch (cdb[0]) {
|
||||||
case SPDK_SPC_INQUIRY:
|
case SPDK_SPC_INQUIRY:
|
||||||
alloc_len = from_be16(&cdb[3]);
|
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);
|
rc = scsi_pr_in(task, cdb, data, data_len);
|
||||||
break;
|
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:
|
default:
|
||||||
return SPDK_SCSI_TASK_UNKNOWN;
|
return SPDK_SCSI_TASK_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -73,8 +73,11 @@ struct spdk_scsi_pr_registrant {
|
|||||||
TAILQ_ENTRY(spdk_scsi_pr_registrant) link;
|
TAILQ_ENTRY(spdk_scsi_pr_registrant) link;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SCSI_SPC2_RESERVE 0x00000001U
|
||||||
|
|
||||||
/* Reservation with LU_SCOPE */
|
/* Reservation with LU_SCOPE */
|
||||||
struct spdk_scsi_pr_reservation {
|
struct spdk_scsi_pr_reservation {
|
||||||
|
uint32_t flags;
|
||||||
struct spdk_scsi_pr_registrant *holder;
|
struct spdk_scsi_pr_registrant *holder;
|
||||||
enum spdk_scsi_pr_type_code rtype;
|
enum spdk_scsi_pr_type_code rtype;
|
||||||
uint64_t crkey;
|
uint64_t crkey;
|
||||||
@ -144,6 +147,8 @@ struct spdk_scsi_lun {
|
|||||||
uint32_t pr_generation;
|
uint32_t pr_generation;
|
||||||
/** Reservation for the LUN */
|
/** Reservation for the LUN */
|
||||||
struct spdk_scsi_pr_reservation reservation;
|
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. */
|
/** List of open descriptors for this LUN. */
|
||||||
TAILQ_HEAD(, spdk_scsi_lun_desc) open_descs;
|
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_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 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 {
|
struct spdk_scsi_globals {
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
};
|
};
|
||||||
|
@ -53,6 +53,23 @@ scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
|
|||||||
return NULL;
|
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 */
|
/* Reservation type is all registrants or not */
|
||||||
static inline bool
|
static inline bool
|
||||||
scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
|
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;
|
param = (struct spdk_scsi_pr_in_report_capabilities_data *)data;
|
||||||
|
|
||||||
memset(param, 0, sizeof(*param));
|
memset(param, 0, sizeof(*param));
|
||||||
/* TODO: can support more capabilities bits */
|
|
||||||
to_be16(¶m->length, sizeof(*param));
|
to_be16(¶m->length, sizeof(*param));
|
||||||
|
/* Compatible reservation handling to support RESERVE/RELEASE defined in SPC-2 */
|
||||||
|
param->crh = 1;
|
||||||
param->tmv = 1;
|
param->tmv = 1;
|
||||||
param->wr_ex = 1;
|
param->wr_ex = 1;
|
||||||
param->ex_ac = 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_SBC_READ_CAPACITY_10:
|
||||||
case SPDK_SPC_PERSISTENT_RESERVE_IN:
|
case SPDK_SPC_PERSISTENT_RESERVE_IN:
|
||||||
case SPDK_SPC_SERVICE_ACTION_IN_16:
|
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;
|
return 0;
|
||||||
case SPDK_SPC_MODE_SELECT_6:
|
case SPDK_SPC_MODE_SELECT_6:
|
||||||
case SPDK_SPC_MODE_SELECT_10:
|
case SPDK_SPC_MODE_SELECT_10:
|
||||||
@ -878,3 +902,166 @@ conflict:
|
|||||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||||
return -1;
|
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));
|
(struct spdk_scsi_dev *dev, struct spdk_scsi_lun *lun));
|
||||||
|
|
||||||
DEFINE_STUB(scsi_pr_check, int, (struct spdk_scsi_task *task), 0);
|
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
|
void
|
||||||
bdev_scsi_reset(struct spdk_scsi_task *task)
|
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,
|
DEFINE_STUB(scsi_pr_in, int, (struct spdk_scsi_task *task, uint8_t *cdb,
|
||||||
uint8_t *data, uint16_t data_len), 0);
|
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
|
void
|
||||||
scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
|
scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user