in target_core_pr.c [311:561]
static int core_scsi3_pr_seq_non_holder(struct se_cmd *cmd, u32 pr_reg_type,
bool isid_mismatch)
{
unsigned char *cdb = cmd->t_task_cdb;
struct se_session *se_sess = cmd->se_sess;
struct se_node_acl *nacl = se_sess->se_node_acl;
int other_cdb = 0;
int registered_nexus = 0, ret = 1; /* Conflict by default */
int all_reg = 0, reg_only = 0; /* ALL_REG, REG_ONLY */
int we = 0; /* Write Exclusive */
int legacy = 0; /* Act like a legacy device and return
* RESERVATION CONFLICT on some CDBs */
if (isid_mismatch) {
registered_nexus = 0;
} else {
struct se_dev_entry *se_deve;
rcu_read_lock();
se_deve = target_nacl_find_deve(nacl, cmd->orig_fe_lun);
if (se_deve)
registered_nexus = test_bit(DEF_PR_REG_ACTIVE,
&se_deve->deve_flags);
rcu_read_unlock();
}
switch (pr_reg_type) {
case PR_TYPE_WRITE_EXCLUSIVE:
we = 1;
fallthrough;
case PR_TYPE_EXCLUSIVE_ACCESS:
/*
* Some commands are only allowed for the persistent reservation
* holder.
*/
break;
case PR_TYPE_WRITE_EXCLUSIVE_REGONLY:
we = 1;
fallthrough;
case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY:
/*
* Some commands are only allowed for registered I_T Nexuses.
*/
reg_only = 1;
break;
case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
we = 1;
fallthrough;
case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
/*
* Each registered I_T Nexus is a reservation holder.
*/
all_reg = 1;
break;
default:
return -EINVAL;
}
/*
* Referenced from spc4r17 table 45 for *NON* PR holder access
*/
switch (cdb[0]) {
case SECURITY_PROTOCOL_IN:
if (registered_nexus)
return 0;
ret = (we) ? 0 : 1;
break;
case MODE_SENSE:
case MODE_SENSE_10:
case READ_ATTRIBUTE:
case READ_BUFFER:
case RECEIVE_DIAGNOSTIC:
if (legacy) {
ret = 1;
break;
}
if (registered_nexus) {
ret = 0;
break;
}
ret = (we) ? 0 : 1; /* Allowed Write Exclusive */
break;
case PERSISTENT_RESERVE_OUT:
/*
* This follows PERSISTENT_RESERVE_OUT service actions that
* are allowed in the presence of various reservations.
* See spc4r17, table 46
*/
switch (cdb[1] & 0x1f) {
case PRO_CLEAR:
case PRO_PREEMPT:
case PRO_PREEMPT_AND_ABORT:
ret = (registered_nexus) ? 0 : 1;
break;
case PRO_REGISTER:
case PRO_REGISTER_AND_IGNORE_EXISTING_KEY:
ret = 0;
break;
case PRO_REGISTER_AND_MOVE:
case PRO_RESERVE:
ret = 1;
break;
case PRO_RELEASE:
ret = (registered_nexus) ? 0 : 1;
break;
default:
pr_err("Unknown PERSISTENT_RESERVE_OUT service"
" action: 0x%02x\n", cdb[1] & 0x1f);
return -EINVAL;
}
break;
case RELEASE:
case RELEASE_10:
/* Handled by CRH=1 in target_scsi2_reservation_release() */
ret = 0;
break;
case RESERVE:
case RESERVE_10:
/* Handled by CRH=1 in target_scsi2_reservation_reserve() */
ret = 0;
break;
case TEST_UNIT_READY:
ret = (legacy) ? 1 : 0; /* Conflict for legacy */
break;
case MAINTENANCE_IN:
switch (cdb[1] & 0x1f) {
case MI_MANAGEMENT_PROTOCOL_IN:
if (registered_nexus) {
ret = 0;
break;
}
ret = (we) ? 0 : 1; /* Allowed Write Exclusive */
break;
case MI_REPORT_SUPPORTED_OPERATION_CODES:
case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS:
if (legacy) {
ret = 1;
break;
}
if (registered_nexus) {
ret = 0;
break;
}
ret = (we) ? 0 : 1; /* Allowed Write Exclusive */
break;
case MI_REPORT_ALIASES:
case MI_REPORT_IDENTIFYING_INFORMATION:
case MI_REPORT_PRIORITY:
case MI_REPORT_TARGET_PGS:
case MI_REPORT_TIMESTAMP:
ret = 0; /* Allowed */
break;
default:
pr_err("Unknown MI Service Action: 0x%02x\n",
(cdb[1] & 0x1f));
return -EINVAL;
}
break;
case ACCESS_CONTROL_IN:
case ACCESS_CONTROL_OUT:
case INQUIRY:
case LOG_SENSE:
case SERVICE_ACTION_IN_12:
case REPORT_LUNS:
case REQUEST_SENSE:
case PERSISTENT_RESERVE_IN:
ret = 0; /*/ Allowed CDBs */
break;
default:
other_cdb = 1;
break;
}
/*
* Case where the CDB is explicitly allowed in the above switch
* statement.
*/
if (!ret && !other_cdb) {
pr_debug("Allowing explicit CDB: 0x%02x for %s"
" reservation holder\n", cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
return ret;
}
/*
* Check if write exclusive initiator ports *NOT* holding the
* WRITE_EXCLUSIVE_* reservation.
*/
if (we && !registered_nexus) {
if (cmd->data_direction == DMA_TO_DEVICE) {
/*
* Conflict for write exclusive
*/
pr_debug("%s Conflict for unregistered nexus"
" %s CDB: 0x%02x to %s reservation\n",
transport_dump_cmd_direction(cmd),
se_sess->se_node_acl->initiatorname, cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
return 1;
} else {
/*
* Allow non WRITE CDBs for all Write Exclusive
* PR TYPEs to pass for registered and
* non-registered_nexuxes NOT holding the reservation.
*
* We only make noise for the unregisterd nexuses,
* as we expect registered non-reservation holding
* nexuses to issue CDBs.
*/
if (!registered_nexus) {
pr_debug("Allowing implicit CDB: 0x%02x"
" for %s reservation on unregistered"
" nexus\n", cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
}
return 0;
}
} else if ((reg_only) || (all_reg)) {
if (registered_nexus) {
/*
* For PR_*_REG_ONLY and PR_*_ALL_REG reservations,
* allow commands from registered nexuses.
*/
pr_debug("Allowing implicit CDB: 0x%02x for %s"
" reservation\n", cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
return 0;
}
} else if (we && registered_nexus) {
/*
* Reads are allowed for Write Exclusive locks
* from all registrants.
*/
if (cmd->data_direction == DMA_FROM_DEVICE) {
pr_debug("Allowing READ CDB: 0x%02x for %s"
" reservation\n", cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
return 0;
}
}
pr_debug("%s Conflict for %sregistered nexus %s CDB: 0x%2x"
" for %s reservation\n", transport_dump_cmd_direction(cmd),
(registered_nexus) ? "" : "un",
se_sess->se_node_acl->initiatorname, cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
return 1; /* Conflict by default */
}