Re: [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]

 



On 10/02/2010 11:43 PM, Nicholas A. Bellinger wrote:
> 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.
     ^^ ==> ->get_max_cdb_len()
> 

I'm commenting on the change log here. Comments on actual code is below.

First: there are bigger then ->get_max_cdb_len() which are not VARIABLE_LENGTH_CMD, and
there are VARIABLE_LENGTH_CMD which are smaller then 16 bytes. So the VARIABLE_LENGTH_CMD
has nothing to do with it. It's all a matter of scsi_command_size(cmnd);

Second: I don't like that ->get_max_cdb_len(), it means nothing. The fact that the size
check passed, does not mean the actual SCSI-COMMAND is supported. So plugins should be
ready to receive unsupported commands and properly return the correct error. If an
FILEIO does not support OSD_ACT_CREATE it's because it's an OSD command and not because
it is big. Maintaining the proper ->get_max_cdb_len() as code evolves is just a maintenance
headache that buys you nothing!

Note that in pSCSI passthrough plugin the request will fail soon enough and the proper
error handling should be in place, already.

> 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.

You have a magic size number which is sizeof(->__t_task_cdb) bigger then "that" alloc.
Then also bigger then "that" free. No need for a flag. 
But I'd even do more. if (->t_task_cdb != ->__t_task_cdb) free

> 
> 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

No! the max CDB size is SCSI_MAX_VARLEN_CDB_SIZE (260) defined in scsi/scsi.h. Including
the varlen_cdb 8 bytes header. This is do to the fact that the header has a single byte
to denote payload size and must be 4 bytes aligned so 252 + 8 bytes header.
See "struct scsi_varlen_cdb_hdr" in scsi.h

> 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) {

Just check the size here, Why do you need the flag.

> +		/*
> +		 * 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];

What? another copy and another pointer. Why? Why can't you just pass to
block layer the command from T_TASK(cmd)->t_task_cdb. Don't you keep the
T_TASK(cmd) associated with the pscsi_plugin_task up until completion.
(The complete hunk looks pointless)

>  
>  	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);
> +	/*

Here! you have the struct se_task up to the end. pt->pscsi_cdb is totally
redundant.

>  	 * 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;
> +}
> +

I don't think you need that, but if you do why not return the scsi_host->max_cmd_len ?

>  /*	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
> +

Wrong and redundant use SCSI_MAX_VARLEN_CDB_SIZE

>  /* 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];

As said above just drop it.

>  	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;
> +}
> +

Surely not maximum for stgt. Or I missed the meaning of this completely.

>  /*	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);

What about if (T_TASK(se_cmd)->t_task_cdb != 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) {

                                       ^^ sizeof(T_TASK(cmd)->__t_task_cdb)

> +		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];

		T_TASK(cmd)->t_task_cdb = T_TASK(cmd)->__t_task_cdb;

> +	/*
>  	 * 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)) {

Again VARIABLE_LENGTH_CMD can be small as well as none-VARIABLE_LENGTH_CMD
can be big. The check should be more generic and against scsi_command_size(cdb).

BUT!! Please just drop that ->get_max_cdb_len(dev) it's useless.

>  			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 *);

Thanks
Boaz
--
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