From d6e0175cf3f9737a760482d185bb73566bcc9331 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 20 May 2012 11:59:14 -0400 Subject: target: add a parse_cdb method to the backend drivers Instead of trying to handle all SCSI command sets in one function (transport_generic_cmd_sequencer) call out to the backend driver to perform this functionality. For pSCSI a copy of the existing code is used, but for all virtual backends we can use a new parse_sbc_cdb helper is used to provide a simple SBC emulation. For now this setups means a fair amount of duplication between pSCSI and the SBC library, but patches later in this series will sort out that problem. (nab: Fix up build failure in target_core_pscsi.c) Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_pscsi.c | 470 ++++++++++++++++++++++++++++++++++++- 1 file changed, 469 insertions(+), 1 deletion(-) (limited to 'drivers/target/target_core_pscsi.c') diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index bfc72327370..378da242d84 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -35,8 +35,10 @@ #include #include #include -#include +#include #include +#include + #include #include #include @@ -46,6 +48,7 @@ #include #include +#include "target_core_alua.h" #include "target_core_pscsi.h" #define ISPRINT(a) ((a >= ' ') && (a <= '~')) @@ -1019,6 +1022,470 @@ fail: return -ENOMEM; } +static inline u32 pscsi_get_sectors_6( + unsigned char *cdb, + struct se_cmd *cmd, + int *ret) +{ + struct se_device *dev = cmd->se_dev; + + /* + * Assume TYPE_DISK for non struct se_device objects. + * Use 8-bit sector value. + */ + if (!dev) + goto type_disk; + + /* + * Use 24-bit allocation length for TYPE_TAPE. + */ + if (dev->transport->get_device_type(dev) == TYPE_TAPE) + return (u32)(cdb[2] << 16) + (cdb[3] << 8) + cdb[4]; + + /* + * Everything else assume TYPE_DISK Sector CDB location. + * Use 8-bit sector value. SBC-3 says: + * + * A TRANSFER LENGTH field set to zero specifies that 256 + * logical blocks shall be written. Any other value + * specifies the number of logical blocks that shall be + * written. + */ +type_disk: + return cdb[4] ? : 256; +} + +static inline u32 pscsi_get_sectors_10( + unsigned char *cdb, + struct se_cmd *cmd, + int *ret) +{ + struct se_device *dev = cmd->se_dev; + + /* + * Assume TYPE_DISK for non struct se_device objects. + * Use 16-bit sector value. + */ + if (!dev) + goto type_disk; + + /* + * XXX_10 is not defined in SSC, throw an exception + */ + if (dev->transport->get_device_type(dev) == TYPE_TAPE) { + *ret = -EINVAL; + return 0; + } + + /* + * Everything else assume TYPE_DISK Sector CDB location. + * Use 16-bit sector value. + */ +type_disk: + return (u32)(cdb[7] << 8) + cdb[8]; +} + +static inline u32 pscsi_get_sectors_12( + unsigned char *cdb, + struct se_cmd *cmd, + int *ret) +{ + struct se_device *dev = cmd->se_dev; + + /* + * Assume TYPE_DISK for non struct se_device objects. + * Use 32-bit sector value. + */ + if (!dev) + goto type_disk; + + /* + * XXX_12 is not defined in SSC, throw an exception + */ + if (dev->transport->get_device_type(dev) == TYPE_TAPE) { + *ret = -EINVAL; + return 0; + } + + /* + * Everything else assume TYPE_DISK Sector CDB location. + * Use 32-bit sector value. + */ +type_disk: + return (u32)(cdb[6] << 24) + (cdb[7] << 16) + (cdb[8] << 8) + cdb[9]; +} + +static inline u32 pscsi_get_sectors_16( + unsigned char *cdb, + struct se_cmd *cmd, + int *ret) +{ + struct se_device *dev = cmd->se_dev; + + /* + * Assume TYPE_DISK for non struct se_device objects. + * Use 32-bit sector value. + */ + if (!dev) + goto type_disk; + + /* + * Use 24-bit allocation length for TYPE_TAPE. + */ + if (dev->transport->get_device_type(dev) == TYPE_TAPE) + return (u32)(cdb[12] << 16) + (cdb[13] << 8) + cdb[14]; + +type_disk: + return (u32)(cdb[10] << 24) + (cdb[11] << 16) + + (cdb[12] << 8) + cdb[13]; +} + +/* + * Used for VARIABLE_LENGTH_CDB WRITE_32 and READ_32 variants + */ +static inline u32 pscsi_get_sectors_32( + unsigned char *cdb, + struct se_cmd *cmd, + int *ret) +{ + /* + * Assume TYPE_DISK for non struct se_device objects. + * Use 32-bit sector value. + */ + return (u32)(cdb[28] << 24) + (cdb[29] << 16) + + (cdb[30] << 8) + cdb[31]; + +} + +static inline u32 pscsi_get_lba_21(unsigned char *cdb) +{ + return ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3]; +} + +static inline u32 pscsi_get_lba_32(unsigned char *cdb) +{ + return (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; +} + +static inline unsigned long long pscsi_get_lba_64(unsigned char *cdb) +{ + unsigned int __v1, __v2; + + __v1 = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; + __v2 = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9]; + + return ((unsigned long long)__v2) | (unsigned long long)__v1 << 32; +} + +/* + * For VARIABLE_LENGTH_CDB w/ 32 byte extended CDBs + */ +static inline unsigned long long pscsi_get_lba_64_ext(unsigned char *cdb) +{ + unsigned int __v1, __v2; + + __v1 = (cdb[12] << 24) | (cdb[13] << 16) | (cdb[14] << 8) | cdb[15]; + __v2 = (cdb[16] << 24) | (cdb[17] << 16) | (cdb[18] << 8) | cdb[19]; + + return ((unsigned long long)__v2) | (unsigned long long)__v1 << 32; +} + + +static inline u32 pscsi_get_size( + u32 sectors, + unsigned char *cdb, + struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + + if (dev->transport->get_device_type(dev) == TYPE_TAPE) { + if (cdb[1] & 1) { /* sectors */ + return dev->se_sub_dev->se_dev_attrib.block_size * sectors; + } else /* bytes */ + return sectors; + } + + pr_debug("Returning block_size: %u, sectors: %u == %u for" + " %s object\n", dev->se_sub_dev->se_dev_attrib.block_size, + sectors, dev->se_sub_dev->se_dev_attrib.block_size * sectors, + dev->transport->name); + + return dev->se_sub_dev->se_dev_attrib.block_size * sectors; +} + +static int pscsi_parse_cdb(struct se_cmd *cmd, unsigned int *size) +{ + struct se_device *dev = cmd->se_dev; + struct se_subsystem_dev *su_dev = dev->se_sub_dev; + unsigned char *cdb = cmd->t_task_cdb; + int sector_ret = 0; + u32 sectors = 0; + u16 service_action; + int ret; + + if (cmd->se_cmd_flags & SCF_BIDI) + goto out_unsupported_cdb; + + switch (cdb[0]) { + case READ_6: + sectors = pscsi_get_sectors_6(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_21(cdb); + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case READ_10: + sectors = pscsi_get_sectors_10(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_32(cdb); + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case READ_12: + sectors = pscsi_get_sectors_12(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_32(cdb); + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case READ_16: + sectors = pscsi_get_sectors_16(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_64(cdb); + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case WRITE_6: + sectors = pscsi_get_sectors_6(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_21(cdb); + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case WRITE_10: + case WRITE_VERIFY: + sectors = pscsi_get_sectors_10(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_32(cdb); + if (cdb[1] & 0x8) + cmd->se_cmd_flags |= SCF_FUA; + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case WRITE_12: + sectors = pscsi_get_sectors_12(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_32(cdb); + if (cdb[1] & 0x8) + cmd->se_cmd_flags |= SCF_FUA; + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case WRITE_16: + sectors = pscsi_get_sectors_16(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + *size = pscsi_get_size(sectors, cdb, cmd); + cmd->t_task_lba = pscsi_get_lba_64(cdb); + if (cdb[1] & 0x8) + cmd->se_cmd_flags |= SCF_FUA; + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + break; + case VARIABLE_LENGTH_CMD: + service_action = get_unaligned_be16(&cdb[8]); + switch (service_action) { + case WRITE_SAME_32: + sectors = pscsi_get_sectors_32(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + + if (!sectors) { + pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not" + " supported\n"); + goto out_invalid_cdb_field; + } + + *size = pscsi_get_size(1, cdb, cmd); + cmd->t_task_lba = get_unaligned_be64(&cdb[12]); + break; + default: + pr_err("VARIABLE_LENGTH_CMD service action" + " 0x%04x not supported\n", service_action); + goto out_unsupported_cdb; + } + break; + case GPCMD_READ_BUFFER_CAPACITY: + case GPCMD_SEND_OPC: + *size = (cdb[7] << 8) + cdb[8]; + break; + case READ_BLOCK_LIMITS: + *size = READ_BLOCK_LEN; + break; + case GPCMD_GET_CONFIGURATION: + case GPCMD_READ_FORMAT_CAPACITIES: + case GPCMD_READ_DISC_INFO: + case GPCMD_READ_TRACK_RZONE_INFO: + *size = (cdb[7] << 8) + cdb[8]; + break; + case GPCMD_MECHANISM_STATUS: + case GPCMD_READ_DVD_STRUCTURE: + *size = (cdb[8] << 8) + cdb[9]; + break; + case READ_POSITION: + *size = READ_POSITION_LEN; + break; + case READ_BUFFER: + *size = (cdb[6] << 16) + (cdb[7] << 8) + cdb[8]; + break; + case READ_CAPACITY: + *size = READ_CAP_LEN; + break; + case READ_MEDIA_SERIAL_NUMBER: + case SERVICE_ACTION_IN: + case ACCESS_CONTROL_IN: + case ACCESS_CONTROL_OUT: + *size = (cdb[10] << 24) | (cdb[11] << 16) | + (cdb[12] << 8) | cdb[13]; + break; + case READ_TOC: + *size = cdb[8]; + break; + case READ_ELEMENT_STATUS: + *size = 65536 * cdb[7] + 256 * cdb[8] + cdb[9]; + break; + case SYNCHRONIZE_CACHE: + case SYNCHRONIZE_CACHE_16: + /* + * Extract LBA and range to be flushed for emulated SYNCHRONIZE_CACHE + */ + if (cdb[0] == SYNCHRONIZE_CACHE) { + sectors = pscsi_get_sectors_10(cdb, cmd, §or_ret); + cmd->t_task_lba = pscsi_get_lba_32(cdb); + } else { + sectors = pscsi_get_sectors_16(cdb, cmd, §or_ret); + cmd->t_task_lba = pscsi_get_lba_64(cdb); + } + if (sector_ret) + goto out_unsupported_cdb; + + *size = pscsi_get_size(sectors, cdb, cmd); + break; + case UNMAP: + *size = get_unaligned_be16(&cdb[7]); + break; + case WRITE_SAME_16: + sectors = pscsi_get_sectors_16(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + + if (!sectors) { + pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n"); + goto out_invalid_cdb_field; + } + + *size = pscsi_get_size(1, cdb, cmd); + cmd->t_task_lba = get_unaligned_be64(&cdb[2]); + break; + case WRITE_SAME: + sectors = pscsi_get_sectors_10(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + + if (!sectors) { + pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n"); + goto out_invalid_cdb_field; + } + + *size = pscsi_get_size(1, cdb, cmd); + cmd->t_task_lba = get_unaligned_be32(&cdb[2]); + break; + case ALLOW_MEDIUM_REMOVAL: + case ERASE: + case REZERO_UNIT: + case SEEK_10: + case SPACE: + case START_STOP: + case VERIFY: + case WRITE_FILEMARKS: + case GPCMD_CLOSE_TRACK: + case INITIALIZE_ELEMENT_STATUS: + case GPCMD_LOAD_UNLOAD: + case GPCMD_SET_SPEED: + case MOVE_MEDIUM: + *size = 0; + break; + case GET_EVENT_STATUS_NOTIFICATION: + *size = (cdb[7] << 8) | cdb[8]; + break; + case ATA_16: + switch (cdb[2] & 0x3) { /* T_LENGTH */ + case 0x0: + sectors = 0; + break; + case 0x1: + sectors = (((cdb[1] & 0x1) ? cdb[3] : 0) << 8) | cdb[4]; + break; + case 0x2: + sectors = (((cdb[1] & 0x1) ? cdb[5] : 0) << 8) | cdb[6]; + break; + case 0x3: + pr_err("T_LENGTH=0x3 not supported for ATA_16\n"); + goto out_invalid_cdb_field; + } + + /* BYTE_BLOCK */ + if (cdb[2] & 0x4) { + /* BLOCK T_TYPE: 512 or sector */ + *size = sectors * ((cdb[2] & 0x10) ? + dev->se_sub_dev->se_dev_attrib.block_size : 512); + } else { + /* BYTE */ + *size = sectors; + } + break; + default: + ret = spc_parse_cdb(cmd, size, true); + if (ret) + return ret; + } + + if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { + if (sectors > su_dev->se_dev_attrib.fabric_max_sectors) { + printk_ratelimited(KERN_ERR "SCSI OP %02xh with too" + " big sectors %u exceeds fabric_max_sectors:" + " %u\n", cdb[0], sectors, + su_dev->se_dev_attrib.fabric_max_sectors); + goto out_invalid_cdb_field; + } + if (sectors > su_dev->se_dev_attrib.hw_max_sectors) { + printk_ratelimited(KERN_ERR "SCSI OP %02xh with too" + " big sectors %u exceeds backend hw_max_sectors:" + " %u\n", cdb[0], sectors, + su_dev->se_dev_attrib.hw_max_sectors); + goto out_invalid_cdb_field; + } + } + + return 0; + +out_unsupported_cdb: + cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; + cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE; + return -EINVAL; +out_invalid_cdb_field: + cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; + cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; + return -EINVAL; +} + + static int pscsi_execute_cmd(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, enum dma_data_direction data_direction) { @@ -1188,6 +1655,7 @@ static struct se_subsystem_api pscsi_template = { .create_virtdevice = pscsi_create_virtdevice, .free_device = pscsi_free_device, .transport_complete = pscsi_transport_complete, + .parse_cdb = pscsi_parse_cdb, .execute_cmd = pscsi_execute_cmd, .check_configfs_dev_params = pscsi_check_configfs_dev_params, .set_configfs_dev_params = pscsi_set_configfs_dev_params, -- cgit v1.2.3