diff --git a/scst/include/scst.h b/scst/include/scst.h index 378dc589c..4d3361e85 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -661,6 +661,7 @@ struct scst_acg; struct scst_acg_dev; struct scst_acn; struct scst_aen; +struct scst_opcode_descriptor; /* * SCST uses 64-bit numbers to represent LUN's internally. The value @@ -1374,6 +1375,29 @@ struct scst_dev_type { */ bool (*on_sg_tablesize_low) (struct scst_cmd *cmd); + /* + * Called to return array of supported opcodes in out_supp_opcodes + * argument with out_supp_opcodes_cnt elements count or execute + * REPORT SUPPORTED OPERATION CODES command in place. Must return + * 0 on success or any other code otherwise. In the latter case, + * cmd supposed to have correct sense set. + * + * OPTIONAL + */ + int (*get_supported_opcodes) (struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); + + /* + * Called to put (release) array of supported opcodes returned + * by get_supported_opcodes() callback. + * + * OPTIONAL + */ + void (*put_supported_opcodes) (struct scst_cmd *cmd, + const struct scst_opcode_descriptor **supp_opcodes, + int supp_opcodes_cnt); + /* * Called when new device is attaching to the dev handler * Returns 0 on success, error code otherwise. @@ -2896,6 +2920,58 @@ struct scst_aen { int delivery_status; }; +#define SCST_OD_DEFAULT_CONTROL_BYTE 0 + +struct scst_opcode_descriptor { + uint16_t od_serv_action; + uint8_t od_opcode; + uint8_t od_serv_action_valid:1; + uint8_t od_support:3; /* SUPPORT bits */ + uint16_t od_cdb_size; + uint8_t od_comm_specific_timeout; + uint32_t od_nominal_timeout; + uint32_t od_recommended_timeout; + uint8_t od_cdb_usage_bits[]; +} __packed; + +extern const struct scst_opcode_descriptor scst_op_descr_log_select; +extern const struct scst_opcode_descriptor scst_op_descr_log_sense; +extern const struct scst_opcode_descriptor scst_op_descr_mode_select6; +extern const struct scst_opcode_descriptor scst_op_descr_mode_sense6; +extern const struct scst_opcode_descriptor scst_op_descr_mode_select10; +extern const struct scst_opcode_descriptor scst_op_descr_mode_sense10; +extern const struct scst_opcode_descriptor scst_op_descr_rtpg; +extern const struct scst_opcode_descriptor scst_op_descr_stpg; +extern const struct scst_opcode_descriptor scst_op_descr_send_diagnostic; + +extern const struct scst_opcode_descriptor scst_op_descr_inquiry; +extern const struct scst_opcode_descriptor scst_op_descr_tur; +extern const struct scst_opcode_descriptor scst_op_descr_reserve6; +extern const struct scst_opcode_descriptor scst_op_descr_release6; +extern const struct scst_opcode_descriptor scst_op_descr_reserve10; +extern const struct scst_opcode_descriptor scst_op_descr_release10; +extern const struct scst_opcode_descriptor scst_op_descr_pr_in; +extern const struct scst_opcode_descriptor scst_op_descr_pr_out; +extern const struct scst_opcode_descriptor scst_op_descr_report_luns; +extern const struct scst_opcode_descriptor scst_op_descr_request_sense; +extern const struct scst_opcode_descriptor scst_op_descr_report_supp_tm_fns; +extern const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes; + +#define SCST_OPCODE_DESCRIPTORS \ + &scst_op_descr_inquiry, \ + &scst_op_descr_tur, \ + &scst_op_descr_reserve6, \ + &scst_op_descr_release6, \ + &scst_op_descr_reserve10, \ + &scst_op_descr_release10, \ + &scst_op_descr_pr_in, \ + &scst_op_descr_pr_out, \ + &scst_op_descr_report_luns, \ + &scst_op_descr_request_sense, \ + &scst_op_descr_report_supp_opcodes, \ + &scst_op_descr_report_supp_tm_fns, + + #ifndef smp_mb__after_set_bit /* There is no smp_mb__after_set_bit() in the kernel */ #define smp_mb__after_set_bit() smp_mb() diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index 036078e41..e4ebb6944 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -571,6 +571,7 @@ enum scst_tg_sup { ** Various timeouts *************************************************************/ #define SCST_DEFAULT_TIMEOUT (30 * HZ) +#define SCST_DEFAULT_NOMINAL_TIMEOUT_SEC 1 #define SCST_GENERIC_CHANGER_TIMEOUT (3 * HZ) #define SCST_GENERIC_CHANGER_LONG_TIMEOUT (14000 * HZ) @@ -586,8 +587,8 @@ enum scst_tg_sup { #define SCST_GENERIC_MODISK_REG_TIMEOUT (900 * HZ) #define SCST_GENERIC_MODISK_LONG_TIMEOUT (14000 * HZ) -#define SCST_GENERIC_DISK_SMALL_TIMEOUT (3 * HZ) -#define SCST_GENERIC_DISK_REG_TIMEOUT (30 * HZ) +#define SCST_GENERIC_DISK_SMALL_TIMEOUT (10 * HZ) +#define SCST_GENERIC_DISK_REG_TIMEOUT (60 * HZ) #define SCST_GENERIC_DISK_LONG_TIMEOUT (3600 * HZ) #define SCST_GENERIC_RAID_TIMEOUT (3 * HZ) diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index b42a51ad3..f073630dd 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -244,6 +244,12 @@ static int vdisk_attach(struct scst_device *dev); static void vdisk_detach(struct scst_device *dev); static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev); static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev); +static int vdisk_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); +static int vcdrom_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); static int fileio_alloc_data_buf(struct scst_cmd *cmd); static int vdisk_parse(struct scst_cmd *); static int vcdrom_parse(struct scst_cmd *); @@ -565,6 +571,7 @@ static struct scst_dev_type vdisk_file_devtype = { .exec = vdisk_exec, .on_free_cmd = fileio_on_free_cmd, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vdisk_get_supported_opcodes, .devt_priv = (void *)fileio_ops, #ifdef CONFIG_SCST_PROC .read_proc = vdisk_read_proc, @@ -613,6 +620,7 @@ static struct scst_dev_type vdisk_blk_devtype = { .parse = non_fileio_parse, .exec = non_fileio_exec, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vdisk_get_supported_opcodes, .devt_priv = (void *)blockio_ops, #ifndef CONFIG_SCST_PROC .add_device = vdisk_add_blockio_device, @@ -655,6 +663,7 @@ static struct scst_dev_type vdisk_null_devtype = { .exec = non_fileio_exec, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, .devt_priv = (void *)nullio_ops, + .get_supported_opcodes = vdisk_get_supported_opcodes, #ifndef CONFIG_SCST_PROC .add_device = vdisk_add_nullio_device, .del_device = vdisk_del_device, @@ -694,6 +703,7 @@ static struct scst_dev_type vcdrom_devtype = { .exec = vcdrom_exec, .on_free_cmd = fileio_on_free_cmd, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vcdrom_get_supported_opcodes, #ifdef CONFIG_SCST_PROC .read_proc = vcdrom_read_proc, .write_proc = vcdrom_write_proc, @@ -1398,6 +1408,311 @@ static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) return INVALID_OPCODE; } +#define VDEV_DEF_RDPROTECT 0 +#define VDEV_DEF_WRPROTECT 0 +#define VDEV_DEF_VRPROTECT 0 + +#define VDEF_DEF_GROUP_NUM 0 + +static const struct scst_opcode_descriptor scst_op_descr_cwr = { + .od_opcode = COMPARE_AND_WRITE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { COMPARE_AND_WRITE, VDEV_DEF_WRPROTECT | 0x18, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0, 0, 0, 0xFF, VDEF_DEF_GROUP_NUM, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_format_unit = { + .od_opcode = FORMAT_UNIT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_LONG_TIMEOUT/HZ, + .od_cdb_usage_bits = { FORMAT_UNIT, 0xF0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_get_lba_status = { + .od_opcode = SERVICE_ACTION_IN, + .od_serv_action = SAI_GET_LBA_STATUS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SERVICE_ACTION_IN, SAI_GET_LBA_STATUS, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_allow_medium_removal = { + .od_opcode = ALLOW_MEDIUM_REMOVAL, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 3, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read6 = { + .od_opcode = READ_6, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_6, 0x1F, \ + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read10 = { + .od_opcode = READ_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_10, VDEV_DEF_RDPROTECT | 0x18, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read12 = { + .od_opcode = READ_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_12, VDEV_DEF_RDPROTECT | 0x18, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read16 = { + .od_opcode = READ_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_16, VDEV_DEF_RDPROTECT | 0x18, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_capacity = { + .od_opcode = READ_CAPACITY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, \ + 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_capacity16 = { + .od_opcode = SERVICE_ACTION_IN, + .od_serv_action = SAI_READ_CAPACITY_16, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SERVICE_ACTION_IN, SAI_READ_CAPACITY_16, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_start_stop_unit = { + .od_opcode = START_STOP, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { START_STOP, 1, 0, 0xF, 0xF7, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_sync_cache10 = { + .od_opcode = SYNCHRONIZE_CACHE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { SYNCHRONIZE_CACHE, 2, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_sync_cache16 = { + .od_opcode = SYNCHRONIZE_CACHE_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { SYNCHRONIZE_CACHE_16, 2, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_unmap = { + .od_opcode = UNMAP, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { UNMAP, 0, 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify10 = { + .od_opcode = VERIFY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY, VDEV_DEF_VRPROTECT | 0x16, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify12 = { + .od_opcode = VERIFY_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY_12, VDEV_DEF_VRPROTECT | 0x16, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify16 = { + .od_opcode = VERIFY_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY_16, VDEV_DEF_VRPROTECT | 0x16, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write6 = { + .od_opcode = WRITE_6, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_6, 0x1F, \ + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write10 = { + .od_opcode = WRITE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_10, VDEV_DEF_WRPROTECT | 0x1A, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write12 = { + .od_opcode = WRITE_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_12, VDEV_DEF_WRPROTECT | 0x1A, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write16 = { + .od_opcode = WRITE_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_16, VDEV_DEF_WRPROTECT | 0x1A, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify10 = { + .od_opcode = WRITE_VERIFY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY, VDEV_DEF_WRPROTECT | 0x16, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify12 = { + .od_opcode = WRITE_VERIFY_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY_12, VDEV_DEF_WRPROTECT | 0x16, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify16 = { + .od_opcode = WRITE_VERIFY_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY_16, VDEV_DEF_WRPROTECT | 0x16, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_same10 = { + .od_opcode = WRITE_SAME, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_SAME, VDEV_DEF_WRPROTECT | 0x8, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_same16 = { + .od_opcode = WRITE_SAME_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_SAME_16, VDEV_DEF_WRPROTECT | 0x8, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_toc = { + .od_opcode = READ_TOC, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_TOC, 0, 0xF, 0, 0, 0, 0xFF, \ + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + #define SHARED_OPS \ [SYNCHRONIZE_CACHE] = vdisk_synchronize_cache, \ [SYNCHRONIZE_CACHE_16] = vdisk_synchronize_cache, \ @@ -1427,6 +1742,34 @@ static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) [SEND_DIAGNOSTIC] = vdisk_exec_send_diagnostic, \ [FORMAT_UNIT] = vdisk_exec_format_unit, +#define SHARED_OPCODE_DESCRIPTORS \ + &scst_op_descr_sync_cache10, \ + &scst_op_descr_sync_cache16, \ + &scst_op_descr_mode_sense6, \ + &scst_op_descr_mode_sense10, \ + &scst_op_descr_mode_select6, \ + &scst_op_descr_mode_select10, \ + &scst_op_descr_log_select, \ + &scst_op_descr_log_sense, \ + &scst_op_descr_start_stop_unit, \ + &scst_op_descr_read_capacity, \ + &scst_op_descr_send_diagnostic, \ + &scst_op_descr_rtpg, \ + &scst_op_descr_read6, \ + &scst_op_descr_read10, \ + &scst_op_descr_read12, \ + &scst_op_descr_read16, \ + &scst_op_descr_write6, \ + &scst_op_descr_write10, \ + &scst_op_descr_write12, \ + &scst_op_descr_write16, \ + &scst_op_descr_write_verify10, \ + &scst_op_descr_write_verify12, \ + &scst_op_descr_write_verify16, \ + &scst_op_descr_verify10, \ + &scst_op_descr_verify12, \ + &scst_op_descr_verify16, + static vdisk_op_fn blockio_ops[256] = { [READ_6] = blockio_exec_read, [READ_10] = blockio_exec_read, @@ -1481,6 +1824,45 @@ static vdisk_op_fn nullio_ops[256] = { SHARED_OPS }; +static const struct scst_opcode_descriptor *vdisk_opcode_descriptors[] = { + SHARED_OPCODE_DESCRIPTORS +#if 0 /* it's INVALID OPCODE currently */ + &scst_op_descr_get_lba_status, +#endif + &scst_op_descr_read_capacity16, + &scst_op_descr_write_same10, + &scst_op_descr_write_same16, + &scst_op_descr_unmap, + &scst_op_descr_format_unit, + &scst_op_descr_cwr, + SCST_OPCODE_DESCRIPTORS +}; + +static const struct scst_opcode_descriptor *vcdrom_opcode_descriptors[] = { + SHARED_OPCODE_DESCRIPTORS + &scst_op_descr_allow_medium_removal, + &scst_op_descr_read_toc, + SCST_OPCODE_DESCRIPTORS +}; + +static int vdisk_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt) +{ + *out_supp_opcodes = vdisk_opcode_descriptors; + *out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors); + return 0; +} + +static int vcdrom_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt) +{ + *out_supp_opcodes = vcdrom_opcode_descriptors; + *out_supp_opcodes_cnt = ARRAY_SIZE(vcdrom_opcode_descriptors); + return 0; +} + /* * Compute p->loff and p->fua. * Returns true for success or false otherwise and set error in the commeand. @@ -3535,6 +3917,7 @@ out: static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p) { + /* Changing it don't forget to add it to vdisk_opcode_descriptors! */ return INVALID_OPCODE; } diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 2efc14160..f30af16e8 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -88,6 +88,240 @@ static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, static void scst_free_descriptors(struct scst_cmd *cmd); +const struct scst_opcode_descriptor scst_op_descr_inquiry = { + .od_opcode = INQUIRY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { INQUIRY, 1, 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_inquiry); + +const struct scst_opcode_descriptor scst_op_descr_tur = { + .od_opcode = TEST_UNIT_READY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { TEST_UNIT_READY, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_tur); + +const struct scst_opcode_descriptor scst_op_descr_log_select = { + .od_opcode = LOG_SELECT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { LOG_SELECT, 3, 0xFF, 0xFF, 0, 0, 0, 0xFF, 0xFF, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_log_select); + +const struct scst_opcode_descriptor scst_op_descr_log_sense = { + .od_opcode = LOG_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { LOG_SENSE, 1, 0xFF, 0xFF, 0, 0xFF, 0xFF, 0xFF, 0xFF, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_log_sense); + +const struct scst_opcode_descriptor scst_op_descr_mode_select6 = { + .od_opcode = MODE_SELECT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SELECT, 0x11, 0, 0, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_select6); + +const struct scst_opcode_descriptor scst_op_descr_mode_sense6 = { + .od_opcode = MODE_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SENSE, 8, 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_sense6); + +const struct scst_opcode_descriptor scst_op_descr_mode_select10 = { + .od_opcode = MODE_SELECT_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SELECT_10, 0x11, 0, 0, 0, 0, 0, 0xFF, 0xFF, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_select10); + +const struct scst_opcode_descriptor scst_op_descr_mode_sense10 = { + .od_opcode = MODE_SENSE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SENSE_10, 0x18, 0xFF, 0xFF, 0, 0, 0, 0xFF, 0xFF, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_sense10); + +const struct scst_opcode_descriptor scst_op_descr_rtpg = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_TARGET_PGS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, 0xE0|MI_REPORT_TARGET_PGS, 0, 0, \ + 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_rtpg); + +const struct scst_opcode_descriptor scst_op_descr_stpg = { + .od_opcode = MAINTENANCE_OUT, + .od_serv_action = MO_SET_TARGET_PGS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MO_SET_TARGET_PGS, 0, 0, 0, 0, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_stpg); + +const struct scst_opcode_descriptor scst_op_descr_send_diagnostic = { + .od_opcode = SEND_DIAGNOSTIC, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SEND_DIAGNOSTIC, 0xF7, 0, 0xFF, 0xFF, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_send_diagnostic); + +const struct scst_opcode_descriptor scst_op_descr_reserve6 = { + .od_opcode = RESERVE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RESERVE, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_reserve6); + +const struct scst_opcode_descriptor scst_op_descr_release6 = { + .od_opcode = RELEASE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RELEASE, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_release6); + +const struct scst_opcode_descriptor scst_op_descr_reserve10 = { + .od_opcode = RESERVE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RESERVE_10, 0, 0, 0, 0, 0, 0, 0, 0, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_reserve10); + +const struct scst_opcode_descriptor scst_op_descr_release10 = { + .od_opcode = RELEASE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RELEASE_10, 0, 0, 0, 0, 0, 0, 0, 0, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_release10); + +const struct scst_opcode_descriptor scst_op_descr_pr_in = { + .od_opcode = PERSISTENT_RESERVE_IN, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { PERSISTENT_RESERVE_IN, 0x1F, 0, 0, 0, 0, 0, 0xFF, 0xFF, \ + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_pr_in); + +const struct scst_opcode_descriptor scst_op_descr_pr_out = { + .od_opcode = PERSISTENT_RESERVE_OUT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { PERSISTENT_RESERVE_OUT, 0x1F, 0xFF, 0, 0, 0xFF, \ + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_pr_out); + +const struct scst_opcode_descriptor scst_op_descr_report_luns = { + .od_opcode = REPORT_LUNS, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { REPORT_LUNS, 0, 0xFF, 0, 0, 0, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_luns); + +const struct scst_opcode_descriptor scst_op_descr_request_sense = { + .od_opcode = REQUEST_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { REQUEST_SENSE, 1, 0, 0, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_request_sense); + +const struct scst_opcode_descriptor scst_op_descr_report_supp_tm_fns = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS, \ + 0x80, 0, 0, 0, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_supp_tm_fns); + +const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_SUPPORTED_OPERATION_CODES, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MI_REPORT_SUPPORTED_OPERATION_CODES, \ + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_supp_opcodes); + struct scst_sdbops; static int get_cdb_info_len_10(struct scst_cmd *cmd, @@ -6901,7 +7135,8 @@ static int get_cdb_info_min(struct scst_cmd *cmd, break; case MI_REPORT_SUPPORTED_OPERATION_CODES: cmd->op_name = "REPORT SUPPORTED OPERATION CODES"; - cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED; + cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED | + SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; break; case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: cmd->op_name = "REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS"; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 705b24263..2f824abeb 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -2111,6 +2111,208 @@ out_compl: return res; } +static int scst_report_supported_opcodes(struct scst_cmd *cmd) +{ + int res = SCST_EXEC_COMPLETED; + int length, buf_len, i, offs; + uint8_t *address; + uint8_t *buf; + bool inline_buf; + bool rctd = cmd->cdb[2] >> 7; + int options = cmd->cdb[2] & 7; + int req_opcode = cmd->cdb[3]; + int req_sa = get_unaligned_be16(&cmd->cdb[4]); + const struct scst_opcode_descriptor *op = NULL; + const struct scst_opcode_descriptor **supp_opcodes = NULL; + int supp_opcodes_cnt; + + TRACE_ENTRY(); + + if (cmd->devt->get_supported_opcodes == NULL) { + TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + goto out_compl; + } else { + int rc = cmd->devt->get_supported_opcodes(cmd, &supp_opcodes, + &supp_opcodes_cnt); + if (rc != 0) + goto out_compl; + } + + TRACE_DBG("cmd %p, options %d, req_opcode %x, req_sa %x, rctd %d", + cmd, options, req_opcode, req_sa, rctd); + + switch (options) { + case 0: /* all */ + buf_len = 4; + for (i = 0; i < supp_opcodes_cnt; i++) { + buf_len += 8; + if (rctd) + buf_len += 12; + } + break; + case 1: + buf_len = 0; + for (i = 0; i < supp_opcodes_cnt; i++) { + if (req_opcode == supp_opcodes[i]->od_opcode) { + op = supp_opcodes[i]; + if (op->od_serv_action_valid) { + TRACE(TRACE_MINOR, "Requested opcode %x " + "with unexpected service action " + "(dev %s, initiator %s)", + req_opcode, cmd->dev->virt_name, + cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + buf_len = 4 + op->od_cdb_size; + if (rctd) + buf_len += 12; + break; + } + } + if (op == NULL) { + TRACE(TRACE_MINOR, "Requested opcode %x not found " + "(dev %s, initiator %s)", req_opcode, + cmd->dev->virt_name, cmd->sess->initiator_name); + buf_len = 4; + } + break; + case 2: + buf_len = 0; + for (i = 0; i < supp_opcodes_cnt; i++) { + if (req_opcode == supp_opcodes[i]->od_opcode) { + op = supp_opcodes[i]; + if (!op->od_serv_action_valid) { + TRACE(TRACE_MINOR, "Requested opcode %x " + "without expected service action " + "(dev %s, initiator %s)", + req_opcode, cmd->dev->virt_name, + cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + if (req_sa != op->od_serv_action) { + op = NULL; /* reset it */ + continue; + } + buf_len = 4 + op->od_cdb_size; + if (rctd) + buf_len += 12; + break; + } + } + if (op == NULL) { + TRACE(TRACE_MINOR, "Requested opcode %x/%x not found " + "(dev %s, initiator %s)", req_opcode, req_sa, + cmd->dev->virt_name, cmd->sess->initiator_name); + buf_len = 4; + } + break; + default: + PRINT_ERROR("REPORT SUPPORTED OPERATION CODES: REPORTING OPTIONS " + "%x not supported (dev %s, initiator %s)", options, + cmd->dev->virt_name, cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + + length = scst_get_buf_full_sense(cmd, &address); + TRACE_DBG("length %d, buf_len %d, op %p", length, buf_len, op); + if (unlikely(length <= 0)) + goto out_compl; + + if (length >= buf_len) { + buf = address; + inline_buf = true; + } else { + buf = vzalloc(buf_len); /* it can be big */ + if (buf == NULL) { + PRINT_ERROR("Unable to allocate REPORT SUPPORTED " + "OPERATION CODES buffer with size %d", buf_len); + scst_set_busy(cmd); + goto out_err_put; + } + inline_buf = false; + } + + memset(buf, 0, sizeof(buf)); + + switch(options) { + case 0: /* all */ + put_unaligned_be32(buf_len - 3, &buf[0]); + offs = 4; + for (i = 0; i < supp_opcodes_cnt; i++) { + op = supp_opcodes[i]; + buf[offs] = op->od_opcode; + if (op->od_serv_action_valid) { + put_unaligned_be16(op->od_serv_action, &buf[offs + 2]); + buf[offs + 5] |= 1; + } + put_unaligned_be16(op->od_cdb_size, &buf[offs + 6]); + offs += 8; + if (rctd) { + buf[(offs - 8) + 5] |= 2; + buf[offs + 1] = 0xA; + buf[offs + 3] = op->od_comm_specific_timeout; + put_unaligned_be32(op->od_nominal_timeout, &buf[offs + 4]); + put_unaligned_be32(op->od_recommended_timeout, &buf[offs + 8]); + offs += 12; + } + } + break; + case 1: + case 2: + if (op != NULL) { + buf[1] |= op->od_support; + put_unaligned_be16(op->od_cdb_size, &buf[2]); + memcpy(&buf[4], op->od_cdb_usage_bits, op->od_cdb_size); + if (rctd) { + buf[1] |= 0x80; + offs = 4 + op->od_cdb_size; + buf[offs + 1] = 0xA; + buf[offs + 3] = op->od_comm_specific_timeout; + put_unaligned_be32(op->od_nominal_timeout, &buf[offs + 4]); + put_unaligned_be32(op->od_recommended_timeout, &buf[offs + 8]); + } + } + break; + default: + sBUG_ON(1); + goto out_compl; + } + + if (length > buf_len) + length = buf_len; + if (!inline_buf) { + memcpy(address, buf, length); + vfree(buf); + } + + scst_put_buf_full(cmd, address); + if (length < cmd->resp_data_len) + scst_set_resp_data_len(cmd, length); + +out_compl: + if ((supp_opcodes != NULL) && (cmd->devt->put_supported_opcodes != NULL)) + cmd->devt->put_supported_opcodes(cmd, supp_opcodes, supp_opcodes_cnt); + + cmd->completed = 1; + + /* Report the result */ + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); + + TRACE_EXIT_RES(res); + return res; + +out_err_put: + scst_put_buf_full(cmd, address); + goto out_compl; +} + static int scst_maintenance_in(struct scst_cmd *cmd) { int res; @@ -2121,6 +2323,9 @@ static int scst_maintenance_in(struct scst_cmd *cmd) case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: res = scst_report_supported_tm_fns(cmd); break; + case MI_REPORT_SUPPORTED_OPERATION_CODES: + res = scst_report_supported_opcodes(cmd); + break; default: res = SCST_EXEC_NOT_COMPLETED; break;