scsi: target: core: Add support for RSOC command
Add support for REPORT SUPPORTED OPERATION CODES command according to SPC4. Reviewed-by: Roman Bolshakov <r.bolshakov@yadro.com> Signed-off-by: Dmitry Bogdanov <d.bogdanov@yadro.com> Link: https://lore.kernel.org/r/20220906103421.22348-2-d.bogdanov@yadro.com Reviewed-by: Mike Christie <michael.christie@oracle.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
7029e2151a
commit
b9b8782f89
@ -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 {
|
||||||
/*
|
/*
|
||||||
|
@ -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_ */
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user