Commit 8c0c8e53 authored by Changpeng Liu's avatar Changpeng Liu Committed by Jim Harris
Browse files

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: default avatarChangpeng Liu <changpeng.liu@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/436097


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
parent 94e9f0ab
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -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) {
		/* 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;
+1 −0
Original line number Diff line number Diff line
@@ -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;
+139 −0
Original line number Diff line number Diff line
@@ -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;
}
+2 −0
Original line number Diff line number Diff line
@@ -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)
{