Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d6e0175c authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Nicholas Bellinger
Browse files

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: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent 88455ec4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ target_core_mod-y := target_core_configfs.o \
				   target_core_tpg.o \
				   target_core_transport.o \
				   target_core_cdb.o \
				   target_core_sbc.o \
				   target_core_spc.o \
				   target_core_ua.o \
				   target_core_rd.o \
+1 −0
Original line number Diff line number Diff line
@@ -561,6 +561,7 @@ static struct se_subsystem_api fileio_template = {
	.allocate_virtdevice	= fd_allocate_virtdevice,
	.create_virtdevice	= fd_create_virtdevice,
	.free_device		= fd_free_device,
	.parse_cdb		= sbc_parse_cdb,
	.execute_cmd		= fd_execute_cmd,
	.do_sync_cache		= fd_emulate_sync_cache,
	.check_configfs_dev_params = fd_check_configfs_dev_params,
+1 −0
Original line number Diff line number Diff line
@@ -653,6 +653,7 @@ static struct se_subsystem_api iblock_template = {
	.allocate_virtdevice	= iblock_allocate_virtdevice,
	.create_virtdevice	= iblock_create_virtdevice,
	.free_device		= iblock_free_device,
	.parse_cdb		= sbc_parse_cdb,
	.execute_cmd		= iblock_execute_cmd,
	.do_discard		= iblock_do_discard,
	.do_sync_cache		= iblock_emulate_sync_cache,
+0 −3
Original line number Diff line number Diff line
@@ -96,9 +96,6 @@ int core_tpg_post_addlun(struct se_portal_group *, struct se_lun *,
struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32 unpacked_lun);
int	core_tpg_post_dellun(struct se_portal_group *, struct se_lun *);

/* target_core_spc.c */
int	spc_parse_cdb(struct se_cmd *cmd, unsigned int *size, bool passthrough);

/* target_core_transport.c */
extern struct kmem_cache *se_tmr_req_cache;

+469 −1
Original line number Diff line number Diff line
@@ -35,8 +35,10 @@
#include <linux/spinlock.h>
#include <linux/genhd.h>
#include <linux/cdrom.h>
#include <linux/file.h>
#include <linux/ratelimit.h>
#include <linux/module.h>
#include <asm/unaligned.h>

#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
@@ -46,6 +48,7 @@
#include <target/target_core_base.h>
#include <target/target_core_backend.h>

#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, &sector_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, &sector_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, &sector_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, &sector_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, &sector_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, &sector_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, &sector_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, &sector_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, &sector_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, &sector_ret);
			cmd->t_task_lba = pscsi_get_lba_32(cdb);
		} else {
			sectors = pscsi_get_sectors_16(cdb, cmd, &sector_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, &sector_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, &sector_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,
Loading