static int core_scsi3_pr_seq_non_holder()

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 */
}