[PATCH] tcm: Add support for CDBs greater than TCM_MAX_COMMAND_SIZE bytes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Nicholas Bellinger <nab@xxxxxxxxxxxxxxx>

Greetings all,

This patch allows TCM Core to process CDBs containing a received scsi_command_size()
larger than the hardcoded default of TCM_MAX_COMMAND_SIZE (32-bytes).  This patch
first converts the hardcoded per struct se_cmd -> struct se_transport_task->t_task_cdb[]
to ->__t_task_cdb[TCM_MAX_COMMAND_SIZE], and turns ->t_task_cdb into a pointer used
to reference ->__t_task_cdb or an extended CDB buffer setup in transport_generic_allocate_tasks().

It next adds a new struct se_subsystem_api->get_max_cdb_len() API caller that is
used by TCM subsystem plugins to report their individual CDB size limits.  Next
transport_generic_cmd_sequencer() has been changed to use ->get_max_cdb_len() for
VARIABLE_LENGTH_CMD to determine if the backstore can support the received
> TCM_MAX_COMMAND_SIZE sized CDB, or to fail the I/O with exception status.

The extended CDB buffer is setup in transport_generic_allocate_tasks() when necessary,
and a new SCF_ECDB_ALLOCATION flag is then set.  The release of the extended CDB buffer
happens in transport_free_se_cmd() in the normal struct se_cmd release path.

This patch also updates TCM/pSCSI to handle extended CDBs up to PSCSI_MAX_CDB_SIZE=240
(maximum current OSD CDB size) and sets up the extra struct pscsi_plugin_task->pscsi_cdb
allocation in struct se_task context in order to complete the > TCM_MAX_COMMAND_SIZE=32
request.  The IBLOCK, FILEIO, RAMDISK, and STGT subsystem plugins have been updated to
enforce the TCM_MAX_COMMAND_SIZE=32 limit via struct se_subsystem_api->get_max_cdb_len().

So far this code been tested with 'sgv4_xdwriteread -e' into a TCM_Loop -> TCM/pSCSI
-> scsi_debug backstore with a TCM_MAX_COMMAND_SIZE=16 to force the extended CDB
allocations.  This was done due to the lack of an easy method to test > 32-byte CDBs
w/o an TCM/OSD fabric module.

Many thanks to Boaz Harrosh for his help in this area!

Signed-off-by: Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx>
---
 drivers/target/target_core_file.c      |    6 +++++
 drivers/target/target_core_iblock.c    |    6 +++++
 drivers/target/target_core_pscsi.c     |   39 ++++++++++++++++++++++++++++++++
 drivers/target/target_core_pscsi.h     |    6 ++++-
 drivers/target/target_core_rd.c        |    6 +++++
 drivers/target/target_core_stgt.c      |    6 +++++
 drivers/target/target_core_transport.c |   34 +++++++++++++++++++++++----
 include/target/target_core_base.h      |    6 +++-
 include/target/target_core_transport.h |    5 ++++
 9 files changed, 106 insertions(+), 8 deletions(-)

diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index d0afb50..bf9bfc2 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -1125,6 +1125,11 @@ static unsigned char *fd_get_cdb(struct se_task *task)
 	return req->fd_scsi_cdb;
 }
 
+static u32 fd_get_max_cdb_len(struct se_device *dev)
+{
+	return TCM_MAX_COMMAND_SIZE;
+}
+
 /*	fd_get_blocksize(): (Part of se_subsystem_api_t template)
  *
  *
@@ -1224,6 +1229,7 @@ static struct se_subsystem_api fileio_template = {
 	.check_lba		= fd_check_lba,
 	.check_for_SG		= fd_check_for_SG,
 	.get_cdb		= fd_get_cdb,
+	.get_max_cdb_len	= fd_get_max_cdb_len,
 	.get_blocksize		= fd_get_blocksize,
 	.get_device_rev		= fd_get_device_rev,
 	.get_device_type	= fd_get_device_type,
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 2dae7fd..3337357 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -1059,6 +1059,11 @@ static unsigned char *iblock_get_cdb(struct se_task *task)
 	return req->ib_scsi_cdb;
 }
 
+static u32 iblock_get_max_cdb_len(struct se_device *dev)
+{
+	return TCM_MAX_COMMAND_SIZE;
+}
+
 static u32 iblock_get_blocksize(struct se_device *dev)
 {
 	struct iblock_dev *ibd = dev->dev_ptr;
@@ -1167,6 +1172,7 @@ static struct se_subsystem_api iblock_template = {
 	.check_lba		= iblock_check_lba,
 	.check_for_SG		= iblock_check_for_SG,
 	.get_cdb		= iblock_get_cdb,
+	.get_max_cdb_len	= iblock_get_max_cdb_len,
 	.get_blocksize		= iblock_get_blocksize,
 	.get_device_rev		= iblock_get_device_rev,
 	.get_device_type	= iblock_get_device_type,
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index 8dfedb1..b550725 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -714,13 +714,40 @@ static void *pscsi_allocate_request(
 	struct se_task *task,
 	struct se_device *dev)
 {
+	struct se_cmd *cmd = task->task_se_cmd;
 	struct pscsi_plugin_task *pt;
+	unsigned char *cdb = T_TASK(cmd)->t_task_cdb;
 
 	pt = kzalloc(sizeof(struct pscsi_plugin_task), GFP_KERNEL);
 	if (!(pt)) {
 		printk(KERN_ERR "Unable to allocate struct pscsi_plugin_task\n");
 		return NULL;
 	}
+	/*
+	 * If TCM Core is signaling a > TCM_MAX_COMMAND_SIZE allocation,
+	 * allocate the extended CDB buffer for per struct se_task context
+	 * pt->pscsi_cdb now.
+	 */
+	if (cmd->se_cmd_flags & SCF_ECDB_ALLOCATION) {
+		/*
+		 * PSCSI_MAX_CDB_SIZE is currently set to 240 bytes which
+		 * allows the largest OSD CDB sizes again.
+		 */
+		if (scsi_command_size(cdb) > PSCSI_MAX_CDB_SIZE) {
+			printk(KERN_ERR "pSCSI: Received CDB of size: %u larger"
+				" than PSCSI_MAX_CDB_SIZE: %u\n",
+				scsi_command_size(cdb), PSCSI_MAX_CDB_SIZE);
+			return NULL;
+		}
+
+		pt->pscsi_cdb = kzalloc(scsi_command_size(cdb), GFP_KERNEL);
+		if (!(pt->pscsi_cdb)) {
+			printk(KERN_ERR "pSCSI: Unable to allocate extended"
+					" pt->pscsi_cdb\n");
+			return NULL;
+		}
+	} else
+		pt->pscsi_cdb = &pt->__pscsi_cdb[0];
 
 	return pt;
 }
@@ -818,6 +845,12 @@ static void pscsi_free_task(struct se_task *task)
 {
 	struct pscsi_plugin_task *pt = task->transport_req;
 	/*
+	 * Release the extended CDB allocation from pscsi_allocate_request()
+	 * if one exists.
+	 */
+	if (task->task_se_cmd->se_cmd_flags & SCF_ECDB_ALLOCATION)
+		kfree(pt->pscsi_cdb);
+	/*
 	 * We do not release the bio(s) here associated with this task, as
 	 * this is handled by bio_put() and pscsi_bi_endio().
 	 */
@@ -1366,6 +1399,11 @@ static unsigned char *pscsi_get_cdb(struct se_task *task)
 	return pt->pscsi_cdb;
 }
 
+static u32 pscsi_get_max_cdb_len(struct se_device *dev)
+{
+	return PSCSI_MAX_CDB_SIZE;
+}
+
 /*	pscsi_get_sense_buffer():
  *
  *
@@ -1533,6 +1571,7 @@ static struct se_subsystem_api pscsi_template = {
 	.check_lba		= pscsi_check_lba,
 	.check_for_SG		= pscsi_check_for_SG,
 	.get_cdb		= pscsi_get_cdb,
+	.get_max_cdb_len	= pscsi_get_max_cdb_len,
 	.get_sense_buffer	= pscsi_get_sense_buffer,
 	.get_blocksize		= pscsi_get_blocksize,
 	.get_device_rev		= pscsi_get_device_rev,
diff --git a/drivers/target/target_core_pscsi.h b/drivers/target/target_core_pscsi.h
index d3c8972..e7f07e3 100644
--- a/drivers/target/target_core_pscsi.h
+++ b/drivers/target/target_core_pscsi.h
@@ -9,6 +9,9 @@
 #define INQUIRY_DATA_SIZE	0x24
 #endif
 
+/* Maximum extended CDB size for handling OSD passthrough */
+#define PSCSI_MAX_CDB_SIZE	240
+
 /* used in pscsi_add_device_to_list() */
 #define PSCSI_DEFAULT_QUEUEDEPTH	1
 
@@ -28,7 +31,8 @@ extern int linux_blockdevice_check(int, int);
 #include <linux/kobject.h>
 
 struct pscsi_plugin_task {
-	unsigned char pscsi_cdb[TCM_MAX_COMMAND_SIZE];
+	unsigned char *pscsi_cdb;
+	unsigned char __pscsi_cdb[TCM_MAX_COMMAND_SIZE];
 	unsigned char pscsi_sense[SCSI_SENSE_BUFFERSIZE];
 	int	pscsi_direction;
 	int	pscsi_result;
diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
index 1debdd3..0a54c57 100644
--- a/drivers/target/target_core_rd.c
+++ b/drivers/target/target_core_rd.c
@@ -1392,6 +1392,11 @@ static unsigned char *rd_get_cdb(struct se_task *task)
 	return req->rd_scsi_cdb;
 }
 
+static u32 rd_get_max_cdb_len(struct se_device *dev)
+{
+	return TCM_MAX_COMMAND_SIZE;
+}
+
 /*	rd_get_blocksize(): (Part of se_subsystem_api_t template)
  *
  *
@@ -1475,6 +1480,7 @@ static struct se_subsystem_api rd_dr_template = {
 	.check_lba		= rd_DIRECT_check_lba,
 	.check_for_SG		= rd_check_for_SG,
 	.get_cdb		= rd_get_cdb,
+	.get_max_cdb_len	= rd_get_max_cdb_len,
 	.get_blocksize		= rd_get_blocksize,
 	.get_device_rev		= rd_get_device_rev,
 	.get_device_type	= rd_get_device_type,
diff --git a/drivers/target/target_core_stgt.c b/drivers/target/target_core_stgt.c
index fdcd7eb..ecd4601 100644
--- a/drivers/target/target_core_stgt.c
+++ b/drivers/target/target_core_stgt.c
@@ -751,6 +751,11 @@ static unsigned char *stgt_get_cdb(struct se_task *task)
 	return pt->stgt_cdb;
 }
 
+static u32 stgt_get_max_cdb_len(struct se_device *dev)
+{
+	return TCM_MAX_COMMAND_SIZE;
+}
+
 /*	stgt_get_sense_buffer():
  *
  *
@@ -931,6 +936,7 @@ static struct se_subsystem_api stgt_template = {
 	.check_lba		= stgt_check_lba,
 	.check_for_SG		= stgt_check_for_SG,
 	.get_cdb		= stgt_get_cdb,
+	.get_max_cdb_len	= stgt_get_max_cdb_len,
 	.get_sense_buffer	= stgt_get_sense_buffer,
 	.get_blocksize		= stgt_get_blocksize,
 	.get_device_rev		= stgt_get_device_rev,
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 830c5bb..866b0fa 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2860,6 +2860,11 @@ void transport_free_se_cmd(
 	if (se_cmd->se_tmr_req)
 		core_tmr_release_req(se_cmd->se_tmr_req);
 	/*
+	 * Check and free any extended CDB buffer that was allocated
+	 */
+	if (se_cmd->se_cmd_flags & SCF_ECDB_ALLOCATION)
+		kfree(T_TASK(se_cmd)->t_task_cdb);
+	/*
 	 * Release any optional TCM fabric dependent iovecs allocated by
 	 * transport_allocate_iovecs_for_cmd()
 	 */
@@ -2903,6 +2908,23 @@ int transport_generic_allocate_tasks(
 	if (non_data_cdb < 0)
 		return -1;
 	/*
+	 * If the received CDB is larger than TCM_MAX_COMMAND_SIZE,
+	 * allocate the additional extended CDB buffer now..  Otherwise
+	 * setup the pointer from __t_task_cdb to t_task_cdb.
+	 */
+	if (scsi_command_size(cdb) > TCM_MAX_COMMAND_SIZE) {
+		T_TASK(cmd)->t_task_cdb = kzalloc(scsi_command_size(cdb),
+						GFP_KERNEL);
+		if (!(T_TASK(cmd)->t_task_cdb)) {
+			printk(KERN_ERR "Unable to allocate T_TASK(cmd)->t_task_cdb"
+				" %u > TCM_MAX_COMMAND_SIZE ops\n",
+				scsi_command_size(cdb));
+			return -1;
+		}
+		cmd->se_cmd_flags |= SCF_ECDB_ALLOCATION;
+	} else
+		T_TASK(cmd)->t_task_cdb = &T_TASK(cmd)->__t_task_cdb[0];
+	/*
 	 * Copy the original CDB into T_TASK(cmd).
 	 */
 	memcpy(T_TASK(cmd)->t_task_cdb, cdb, scsi_command_size(cdb));
@@ -5746,13 +5768,15 @@ static int transport_generic_cmd_sequencer(
 		service_action = get_unaligned_be16(&cdb[8]);
 		/*
 		 * Check the additional CDB length (+ 8 bytes for header) does
-		 * not exceed our TCM_MAX_COMMAND_SIZE.
+		 * not exceed our backsores ->get_max_cdb_len()
 		 */
-		if (scsi_varlen_cdb_length(&cdb[0]) > TCM_MAX_COMMAND_SIZE) {
+		if (scsi_varlen_cdb_length(&cdb[0]) >
+				TRANSPORT(dev)->get_max_cdb_len(dev)) {
 			printk(KERN_INFO "Only %u-byte extended CDBs currently"
-				" supported for VARIABLE_LENGTH_CMD, received:"
-				" %d for service action: 0x%04x\n",
-				TCM_MAX_COMMAND_SIZE,
+				" supported for VARIABLE_LENGTH_CMD backstore %s,"
+				" received: %d for service action: 0x%04x\n",
+				TRANSPORT(dev)->get_max_cdb_len(dev),
+				TRANSPORT(dev)->name,
 				scsi_varlen_cdb_length(&cdb[0]), service_action);
 			return TGCS_INVALID_CDB_FIELD;
 		}
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 3905a0e..fc5300c 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -137,7 +137,8 @@ enum se_cmd_flags_table {
 	SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC = 0x00800000,
 	SCF_EMULATE_SYNC_CACHE		= 0x01000000,
 	SCF_EMULATE_CDB_ASYNC		= 0x02000000,
-	SCF_EMULATE_SYNC_UNMAP		= 0x04000000
+	SCF_EMULATE_SYNC_UNMAP		= 0x04000000,
+	SCF_ECDB_ALLOCATION		= 0x08000000
 };
 	
 /* struct se_device->type for known subsystem plugins */
@@ -424,7 +425,8 @@ struct se_queue_obj {
  * drivers/target/target_core_transport.c:__transport_alloc_se_cmd()
  */
 struct se_transport_task {
-	unsigned char		t_task_cdb[MAX_COMMAND_SIZE];
+	unsigned char		*t_task_cdb;
+	unsigned char		__t_task_cdb[MAX_COMMAND_SIZE];
 	unsigned long long	t_task_lba;
 	int			t_tasks_failed;
 	int			t_tasks_fua;
diff --git a/include/target/target_core_transport.h b/include/target/target_core_transport.h
index ab7336b..d45063e 100644
--- a/include/target/target_core_transport.h
+++ b/include/target/target_core_transport.h
@@ -519,6 +519,11 @@ struct se_subsystem_api {
 	 */
 	unsigned char *(*get_cdb)(struct se_task *);
 	/*
+	 * get_max_cdb_len(): Used by subsystems backstoers to signal the
+	 * maximum receivable SCSI CDB size.
+	 */
+	u32 (*get_max_cdb_len)(struct se_device *);
+	/*
 	 * get_blocksize():
 	 */
 	u32 (*get_blocksize)(struct se_device *);
-- 
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux