Commit b9b8782f authored by Dmitry Bogdanov's avatar Dmitry Bogdanov Committed by Martin K. Petersen
Browse files

scsi: target: core: Add support for RSOC command



Add support for REPORT SUPPORTED OPERATION CODES command according to SPC4.

Reviewed-by: default avatarRoman Bolshakov <r.bolshakov@yadro.com>
Signed-off-by: default avatarDmitry Bogdanov <d.bogdanov@yadro.com>
Link: https://lore.kernel.org/r/20220906103421.22348-2-d.bogdanov@yadro.com


Reviewed-by: default avatarMike Christie <michael.christie@oracle.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 7029e215
Loading
Loading
Loading
Loading
+200 −0
Original line number Original line Diff line number Diff line
@@ -1314,6 +1314,202 @@ spc_emulate_testunitready(struct se_cmd *cmd)
	return 0;
	return 0;
}
}



static struct target_opcode_descriptor *tcm_supported_opcodes[] = {
};

static int
spc_rsoc_encode_command_timeouts_descriptor(unsigned char *buf, u8 ctdp,
				struct target_opcode_descriptor *descr)
{
	if (!ctdp)
		return 0;

	put_unaligned_be16(0xa, buf);
	buf[3] = descr->specific_timeout;
	put_unaligned_be32(descr->nominal_timeout, &buf[4]);
	put_unaligned_be32(descr->recommended_timeout, &buf[8]);

	return 12;
}

static int
spc_rsoc_encode_command_descriptor(unsigned char *buf, u8 ctdp,
				   struct target_opcode_descriptor *descr)
{
	int td_size = 0;

	buf[0] = descr->opcode;

	put_unaligned_be16(descr->service_action, &buf[2]);

	buf[5] = (ctdp << 1) | descr->serv_action_valid;
	put_unaligned_be16(descr->cdb_size, &buf[6]);

	td_size = spc_rsoc_encode_command_timeouts_descriptor(&buf[8], ctdp,
							      descr);

	return 8 + td_size;
}

static int
spc_rsoc_encode_one_command_descriptor(unsigned char *buf, u8 ctdp,
				       struct target_opcode_descriptor *descr)
{
	int td_size = 0;

	if (!descr) {
		buf[1] = (ctdp << 7) | SCSI_SUPPORT_NOT_SUPPORTED;
		return 2;
	}

	buf[1] = (ctdp << 7) | SCSI_SUPPORT_FULL;
	put_unaligned_be16(descr->cdb_size, &buf[2]);
	memcpy(&buf[4], descr->usage_bits, descr->cdb_size);

	td_size = spc_rsoc_encode_command_timeouts_descriptor(
			&buf[4 + descr->cdb_size], ctdp, descr);

	return 4 + descr->cdb_size + td_size;
}

static sense_reason_t
spc_rsoc_get_descr(struct se_cmd *cmd, struct target_opcode_descriptor **opcode)
{
	struct target_opcode_descriptor *descr;
	struct se_session *sess = cmd->se_sess;
	unsigned char *cdb = cmd->t_task_cdb;
	u8 opts = cdb[2] & 0x3;
	u8 requested_opcode;
	u16 requested_sa;
	int i;

	requested_opcode = cdb[3];
	requested_sa = ((u16)cdb[4]) << 8 | cdb[5];
	*opcode = NULL;

	if (opts > 3) {
		pr_debug("TARGET_CORE[%s]: Invalid REPORT SUPPORTED OPERATION CODES"
			" with unsupported REPORTING OPTIONS %#x for 0x%08llx from %s\n",
			cmd->se_tfo->fabric_name, opts,
			cmd->se_lun->unpacked_lun,
			sess->se_node_acl->initiatorname);
		return TCM_INVALID_CDB_FIELD;
	}

	for (i = 0; i < ARRAY_SIZE(tcm_supported_opcodes); i++) {
		descr = tcm_supported_opcodes[i];
		if (descr->opcode != requested_opcode)
			continue;

		switch (opts) {
		case 0x1:
			/*
			 * If the REQUESTED OPERATION CODE field specifies an
			 * operation code for which the device server implements
			 * service actions, then the device server shall
			 * terminate the command with CHECK CONDITION status,
			 * with the sense key set to ILLEGAL REQUEST, and the
			 * additional sense code set to INVALID FIELD IN CDB
			 */
			if (descr->serv_action_valid)
				return TCM_INVALID_CDB_FIELD;
			*opcode = descr;
			break;
		case 0x2:
			/*
			 * If the REQUESTED OPERATION CODE field specifies an
			 * operation code for which the device server does not
			 * implement service actions, then the device server
			 * shall terminate the command with CHECK CONDITION
			 * status, with the sense key set to ILLEGAL REQUEST,
			 * and the additional sense code set to INVALID FIELD IN CDB.
			 */
			if (descr->serv_action_valid &&
			    descr->service_action == requested_sa)
				*opcode = descr;
			else if (!descr->serv_action_valid)
				return TCM_INVALID_CDB_FIELD;
			break;
		case 0x3:
			/*
			 * The command support data for the operation code and
			 * service action a specified in the REQUESTED OPERATION
			 * CODE field and REQUESTED SERVICE ACTION field shall
			 * be returned in the one_command parameter data format.
			 */
			if (descr->service_action == requested_sa)
				*opcode = descr;
			break;
		}
	}
	return 0;
}

static sense_reason_t
spc_emulate_report_supp_op_codes(struct se_cmd *cmd)
{
	int descr_num = ARRAY_SIZE(tcm_supported_opcodes);
	struct target_opcode_descriptor *descr = NULL;
	unsigned char *cdb = cmd->t_task_cdb;
	u8 rctd = (cdb[2] >> 7) & 0x1;
	unsigned char *buf = NULL;
	int response_length = 0;
	u8 opts = cdb[2] & 0x3;
	unsigned char *rbuf;
	sense_reason_t ret = 0;
	int i;

	rbuf = transport_kmap_data_sg(cmd);
	if (cmd->data_length && !rbuf) {
		ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
		goto out;
	}

	if (opts == 0)
		response_length = 4 + (8 + rctd * 12) * descr_num;
	else {
		ret = spc_rsoc_get_descr(cmd, &descr);
		if (ret)
			goto out;

		if (descr)
			response_length = 4 + descr->cdb_size + rctd * 12;
		else
			response_length = 2;
	}

	buf = kzalloc(response_length, GFP_KERNEL);
	if (!buf) {
		ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
		goto out;
	}
	response_length = 0;

	if (opts == 0) {
		response_length += 4;

		for (i = 0; i < ARRAY_SIZE(tcm_supported_opcodes); i++) {
			descr = tcm_supported_opcodes[i];
			response_length += spc_rsoc_encode_command_descriptor(
					&buf[response_length], rctd, descr);
		}
		put_unaligned_be32(response_length - 3, buf);
	} else {
		response_length = spc_rsoc_encode_one_command_descriptor(
				&buf[response_length], rctd, descr);
	}

	memcpy(rbuf, buf, min_t(u32, response_length, cmd->data_length));
out:
	kfree(buf);
	transport_kunmap_data_sg(cmd);

	if (!ret)
		target_complete_cmd_with_length(cmd, SAM_STAT_GOOD, response_length);
	return ret;
}

sense_reason_t
sense_reason_t
spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
{
{
@@ -1439,6 +1635,10 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
				cmd->execute_cmd =
				cmd->execute_cmd =
					target_emulate_report_target_port_groups;
					target_emulate_report_target_port_groups;
			}
			}
			if ((cdb[1] & 0x1f) ==
			    MI_REPORT_SUPPORTED_OPERATION_CODES)
				cmd->execute_cmd =
					spc_emulate_report_supp_op_codes;
			*size = get_unaligned_be32(&cdb[6]);
			*size = get_unaligned_be32(&cdb[6]);
		} else {
		} else {
			/*
			/*
+7 −0
Original line number Original line Diff line number Diff line
@@ -342,4 +342,11 @@ enum scsi_version_descriptor {
	SCSI_VERSION_DESCRIPTOR_SRP	= 0x0940
	SCSI_VERSION_DESCRIPTOR_SRP	= 0x0940
};
};


enum scsi_support_opcode {
	SCSI_SUPPORT_NO_INFO		= 0,
	SCSI_SUPPORT_NOT_SUPPORTED	= 1,
	SCSI_SUPPORT_FULL		= 3,
	SCSI_SUPPORT_VENDOR		= 5,
};

#endif /* _SCSI_PROTO_H_ */
#endif /* _SCSI_PROTO_H_ */
+12 −0
Original line number Original line Diff line number Diff line
@@ -867,6 +867,18 @@ struct se_device {
	struct se_device_queue	*queues;
	struct se_device_queue	*queues;
};
};


struct target_opcode_descriptor {
	u8			support:3;
	u8			serv_action_valid:1;
	u8			opcode;
	u16			service_action;
	u32			cdb_size;
	u8			specific_timeout;
	u16			nominal_timeout;
	u16			recommended_timeout;
	u8			usage_bits[];
};

struct se_hba {
struct se_hba {
	u16			hba_tpgt;
	u16			hba_tpgt;
	u32			hba_id;
	u32			hba_id;