scsi: add persistent reservation SCSI commands check

All the SCSI commands have two states: allowed and conflict,
based on different reservation types and I_T nexus, the SCSI
module will check each commands before sending to the backend
device.

Change-Id: Ib05cece1c237873360f85c68d139362200d2d47e
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/436097
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Changpeng Liu 2019-05-14 01:56:42 -04:00 committed by Jim Harris
parent 94e9f0ab8b
commit 8c0c8e5333
4 changed files with 150 additions and 1 deletions

View File

@ -169,7 +169,14 @@ _scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
spdk_trace_record(TRACE_SCSI_TASK_START, lun->dev->id, task->length, (uintptr_t)task, 0);
TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link);
if (!lun->removed) {
rc = spdk_bdev_scsi_execute(task);
/* Check the command is allowed or not when reservation is exist */
rc = spdk_scsi_pr_check(task);
if (spdk_unlikely(rc < 0)) {
/* Reservation Conflict */
rc = SPDK_SCSI_TASK_COMPLETE;
} else {
rc = spdk_bdev_scsi_execute(task);
}
} else {
spdk_scsi_task_process_abort(task);
rc = SPDK_SCSI_TASK_COMPLETE;

View File

@ -203,6 +203,7 @@ bool spdk_scsi_bdev_get_dif_ctx(struct spdk_bdev *bdev, uint8_t *cdb, uint32_t o
int spdk_scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len);
int spdk_scsi_pr_in(struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len);
int spdk_scsi_pr_check(struct spdk_scsi_task *task);
struct spdk_scsi_globals {
pthread_mutex_t mutex;

View File

@ -730,3 +730,142 @@ invalid:
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return -EINVAL;
}
int
spdk_scsi_pr_check(struct spdk_scsi_task *task)
{
struct spdk_scsi_lun *lun = task->lun;
uint8_t *cdb = task->cdb;
enum spdk_scsi_pr_type_code rtype;
enum spdk_scsi_pr_out_service_action_code action;
struct spdk_scsi_pr_registrant *reg;
bool dma_to_device = false;
/* no reservation holders */
if (lun->reservation.holder == NULL) {
return 0;
}
rtype = lun->reservation.rtype;
assert(rtype != 0);
reg = spdk_scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
/* current I_T nexus hold the reservation */
if (spdk_scsi_pr_registrant_is_holder(lun, reg)) {
return 0;
}
/* reservation is held by other I_T nexus */
switch (cdb[0]) {
case SPDK_SPC_INQUIRY:
case SPDK_SPC_REPORT_LUNS:
case SPDK_SPC_REQUEST_SENSE:
case SPDK_SPC_LOG_SENSE:
case SPDK_SPC_TEST_UNIT_READY:
case SPDK_SBC_START_STOP_UNIT:
case SPDK_SBC_READ_CAPACITY_10:
case SPDK_SPC_PERSISTENT_RESERVE_IN:
case SPDK_SPC_SERVICE_ACTION_IN_16:
return 0;
case SPDK_SPC_MODE_SELECT_6:
case SPDK_SPC_MODE_SELECT_10:
case SPDK_SPC_MODE_SENSE_6:
case SPDK_SPC_MODE_SENSE_10:
case SPDK_SPC_LOG_SELECT:
/* I_T nexus is registrant but not holder */
if (!reg) {
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "CHECK: current I_T nexus "
"is not registered, cdb 0x%x\n", cdb[0]);
goto conflict;
}
break;
case SPDK_SPC_PERSISTENT_RESERVE_OUT:
action = cdb[1] & 0x1f;
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "CHECK: PR OUT action %u\n", action);
switch (action) {
case SPDK_SCSI_PR_OUT_RELEASE:
case SPDK_SCSI_PR_OUT_CLEAR:
case SPDK_SCSI_PR_OUT_PREEMPT:
case SPDK_SCSI_PR_OUT_PREEMPT_AND_ABORT:
if (!reg) {
SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
goto conflict;
}
break;
case SPDK_SCSI_PR_OUT_REGISTER:
case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
return 0;
case SPDK_SCSI_PR_OUT_REG_AND_MOVE:
SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
goto conflict;
default:
SPDK_ERRLOG("CHECK: PR OUT invalid action %u\n", action);
goto conflict;
}
/* For most SBC R/W commands */
default:
break;
}
switch (cdb[0]) {
case SPDK_SBC_READ_6:
case SPDK_SBC_READ_10:
case SPDK_SBC_READ_12:
case SPDK_SBC_READ_16:
break;
case SPDK_SBC_WRITE_6:
case SPDK_SBC_WRITE_10:
case SPDK_SBC_WRITE_12:
case SPDK_SBC_WRITE_16:
case SPDK_SBC_UNMAP:
case SPDK_SBC_SYNCHRONIZE_CACHE_10:
case SPDK_SBC_SYNCHRONIZE_CACHE_16:
dma_to_device = true;
break;
default:
SPDK_ERRLOG("CHECK: unsupported SCSI command cdb 0x%x\n", cdb[0]);
goto conflict;
}
switch (rtype) {
case SPDK_SCSI_PR_WRITE_EXCLUSIVE:
if (dma_to_device) {
SPDK_ERRLOG("CHECK: Write Exclusive reservation type "
"rejects command 0x%x\n", cdb[0]);
goto conflict;
}
break;
case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
"rejects command 0x%x\n", cdb[0]);
goto conflict;
case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
if (!reg && dma_to_device) {
SPDK_ERRLOG("CHECK: Registrants only reservation "
"type reject command 0x%x\n", cdb[0]);
goto conflict;
}
break;
case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY:
case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
if (!reg) {
SPDK_ERRLOG("CHECK: All Registrants reservation "
"type reject command 0x%x\n", cdb[0]);
goto conflict;
}
break;
default:
break;
}
return 0;
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;
}

View File

@ -111,6 +111,8 @@ DEFINE_STUB_V(spdk_scsi_dev_queue_mgmt_task,
DEFINE_STUB_V(spdk_scsi_dev_delete_lun,
(struct spdk_scsi_dev *dev, struct spdk_scsi_lun *lun));
DEFINE_STUB(spdk_scsi_pr_check, int, (struct spdk_scsi_task *task), 0);
void
spdk_bdev_scsi_reset(struct spdk_scsi_task *task)
{