scsi: target: core: Add support for RSOC command
authorDmitry Bogdanov <d.bogdanov@yadro.com>
Tue, 6 Sep 2022 10:34:16 +0000 (13:34 +0300)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 27 Oct 2022 01:44:32 +0000 (01:44 +0000)
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>
drivers/target/target_core_spc.c
include/scsi/scsi_proto.h
include/target/target_core_base.h

index 7cca3b15472b3a676046cf7cef541a1933c9ba10..afd5ea0344f361abdc60da7766418d1d45530047 100644 (file)
@@ -1314,6 +1314,202 @@ spc_emulate_testunitready(struct se_cmd *cmd)
        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
 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 =
                                        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]);
                } else {
                        /*
index c03e35fc382cde37fd9f268bd7b04f3903632bc2..651b5183451c156603e56649ee6dc0b284d7b5c3 100644 (file)
@@ -342,4 +342,11 @@ enum scsi_version_descriptor {
        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_ */
index 8c920456edd931d532ca9461a6cd6db1c7be6b6f..02a2d48d20b694ac2736f52d2899447322966c30 100644 (file)
@@ -867,6 +867,18 @@ struct se_device {
        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 {
        u16                     hba_tpgt;
        u32                     hba_id;