diff --git a/lib/scsi/lun.c b/lib/scsi/lun.c index 97fa36bd6..cb3c2d2c7 100644 --- a/lib/scsi/lun.c +++ b/lib/scsi/lun.c @@ -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; diff --git a/lib/scsi/scsi_internal.h b/lib/scsi/scsi_internal.h index 789e74381..7ad1caa31 100644 --- a/lib/scsi/scsi_internal.h +++ b/lib/scsi/scsi_internal.h @@ -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; diff --git a/lib/scsi/scsi_pr.c b/lib/scsi/scsi_pr.c index ec6825d39..2f5f41b2f 100644 --- a/lib/scsi/scsi_pr.c +++ b/lib/scsi/scsi_pr.c @@ -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; +} diff --git a/test/unit/lib/scsi/lun.c/lun_ut.c b/test/unit/lib/scsi/lun.c/lun_ut.c index c8bf7fdd0..488ffdce8 100644 --- a/test/unit/lib/scsi/lun.c/lun_ut.c +++ b/test/unit/lib/scsi/lun.c/lun_ut.c @@ -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) {