CDB splitting added

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2115 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2010-09-13 21:02:35 +00:00
parent d5e230f260
commit f6a2a6b4db
6 changed files with 438 additions and 92 deletions

View File

@@ -1120,12 +1120,17 @@ struct scst_dev_type {
* Pay attention to "atomic" attribute of the cmd, which can be get
* by scst_cmd_atomic(): it is true if the function called in the
* atomic (non-sleeping) context.
*
* OPTIONAL
*/
int (*dev_done) (struct scst_cmd *cmd);
/*
* Called to notify dev hander that the command is about to be freed.
*
* Could be called on IRQ context.
*
* OPTIONAL
*/
void (*on_free_cmd) (struct scst_cmd *cmd);
@@ -1140,26 +1145,52 @@ struct scst_dev_type {
* command should be done
*
* Called without any locks held from a thread context.
*
* OPTIONAL
*/
int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd,
struct scst_tgt_dev *tgt_dev);
/*
* Called to notify dev handler that its sg_tablesize is too low to
* satisfy this command's data transfer requirements. Should return
* true if exec() callback will split this command's CDB on smaller
* transfers, false otherwise.
*
* Could be called on SIRQ context.
*
* MUST HAVE, if dev handler supports CDB splitting.
*/
bool (*on_sg_tablesize_low) (struct scst_cmd *cmd);
/*
* Called when new device is attaching to the dev handler
* Returns 0 on success, error code otherwise.
*
* OPTIONAL
*/
int (*attach) (struct scst_device *dev);
/* Called when a device is detaching from the dev handler */
/*
* Called when a device is detaching from the dev handler.
*
* OPTIONAL
*/
void (*detach) (struct scst_device *dev);
/*
* Called when new tgt_dev (session) is attaching to the dev handler.
* Returns 0 on success, error code otherwise.
*
* OPTIONAL
*/
int (*attach_tgt) (struct scst_tgt_dev *tgt_dev);
/* Called when tgt_dev (session) is detaching from the dev handler */
/*
* Called when tgt_dev (session) is detaching from the dev handler.
*
* OPTIONAL
*/
void (*detach_tgt) (struct scst_tgt_dev *tgt_dev);
#ifdef CONFIG_SCST_PROC
@@ -3808,4 +3839,10 @@ char *scst_get_next_token_str(char **input_str);
void scst_init_threads(struct scst_cmd_threads *cmd_threads);
void scst_deinit_threads(struct scst_cmd_threads *cmd_threads);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
void scst_pass_through_cmd_done(void *data, char *sense, int result, int resid);
int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
void (*done)(void *data, char *sense, int result, int resid));
#endif
#endif /* __SCST_H */

View File

@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <scsi/scsi_host.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define LOG_PREFIX "dev_disk"
@@ -47,8 +48,12 @@ struct disk_params {
static int disk_attach(struct scst_device *dev);
static void disk_detach(struct scst_device *dev);
static int disk_parse(struct scst_cmd *cmd);
static int disk_perf_exec(struct scst_cmd *cmd);
static int disk_done(struct scst_cmd *cmd);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
static int disk_exec(struct scst_cmd *cmd);
static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd);
#endif
static struct scst_dev_type disk_devtype = {
.name = DISK_NAME,
@@ -59,6 +64,10 @@ static struct scst_dev_type disk_devtype = {
.attach = disk_attach,
.detach = disk_detach,
.parse = disk_parse,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
.exec = disk_exec,
.on_sg_tablesize_low = disk_on_sg_tablesize_low,
#endif
.dev_done = disk_done,
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
.default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
@@ -74,8 +83,11 @@ static struct scst_dev_type disk_devtype_perf = {
.attach = disk_attach,
.detach = disk_detach,
.parse = disk_parse,
.exec = disk_perf_exec,
.dev_done = disk_done,
.exec = disk_exec,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
.on_sg_tablesize_low = disk_on_sg_tablesize_low,
#endif
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
.default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
.trace_flags = &trace_flag,
@@ -145,15 +157,6 @@ static void __exit exit_scst_disk_driver(void)
module_init(init_scst_disk_driver);
module_exit(exit_scst_disk_driver);
/**************************************************************
* Function: disk_attach
*
* Argument:
*
* Returns : 1 if attached, error code otherwise
*
* Description:
*************************************************************/
static int disk_attach(struct scst_device *dev)
{
int res, rc;
@@ -260,15 +263,6 @@ out:
return res;
}
/************************************************************
* Function: disk_detach
*
* Argument:
*
* Returns : None
*
* Description: Called to detach this device type driver
************************************************************/
static void disk_detach(struct scst_device *dev)
{
struct disk_params *params =
@@ -293,17 +287,6 @@ static int disk_get_block_shift(struct scst_cmd *cmd)
return params->block_shift;
}
/********************************************************************
* Function: disk_parse
*
* Argument:
*
* Returns : The state of the command
*
* Description: This does the parsing of the command
*
* Note: Not all states are allowed on return
********************************************************************/
static int disk_parse(struct scst_cmd *cmd)
{
int res = SCST_CMD_STATE_DEFAULT;
@@ -329,17 +312,6 @@ static void disk_set_block_shift(struct scst_cmd *cmd, int block_shift)
return;
}
/********************************************************************
* Function: disk_done
*
* Argument:
*
* Returns :
*
* Description: This is the completion routine for the command,
* it is used to extract any necessary information
* about a command.
********************************************************************/
static int disk_done(struct scst_cmd *cmd)
{
int res = SCST_CMD_STATE_DEFAULT;
@@ -352,19 +324,315 @@ static int disk_done(struct scst_cmd *cmd)
return res;
}
/********************************************************************
* Function: disk_exec
*
* Argument:
*
* Returns :
*
* Description: Make SCST do nothing for data READs and WRITES.
* Intended for raw line performance testing
********************************************************************/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd)
{
bool res;
TRACE_ENTRY();
switch (cmd-> cdb[0]) {
case WRITE_6:
case READ_6:
case WRITE_10:
case READ_10:
case WRITE_VERIFY:
case WRITE_12:
case READ_12:
case WRITE_VERIFY_12:
case WRITE_16:
case READ_16:
case WRITE_VERIFY_16:
res = true;
/* See comment in disk_exec */
cmd->inc_expected_sn_on_done = 1;
break;
default:
res = false;
break;
}
TRACE_EXIT_RES(res);
return res;
}
struct disk_work {
struct scst_cmd *cmd;
struct completion disk_work_cmpl;
volatile int result;
unsigned int left;
uint64_t save_lba;
unsigned int save_len;
struct scatterlist *save_sg;
int save_sg_cnt;
};
static int disk_cdb_get_transfer_data(const uint8_t *cdb,
uint64_t *out_lba, unsigned int *out_length)
{
int res;
uint64_t lba;
unsigned int len;
TRACE_ENTRY();
switch (cdb[0]) {
case WRITE_6:
case READ_6:
lba = be16_to_cpu(get_unaligned((__be16 *)&cdb[2]));
len = cdb[4];
break;
case WRITE_10:
case READ_10:
case WRITE_VERIFY:
lba = be32_to_cpu(get_unaligned((__be32 *)&cdb[2]));
len = be16_to_cpu(get_unaligned((__be16 *)&cdb[7]));
break;
case WRITE_12:
case READ_12:
case WRITE_VERIFY_12:
lba = be32_to_cpu(get_unaligned((__be32 *)&cdb[2]));
len = be32_to_cpu(get_unaligned((__be32 *)&cdb[6]));
break;
case WRITE_16:
case READ_16:
case WRITE_VERIFY_16:
lba = be64_to_cpu(get_unaligned((__be64 *)&cdb[2]));
len = be32_to_cpu(get_unaligned((__be32 *)&cdb[10]));
break;
default:
res = -EINVAL;
goto out;
}
res = 0;
*out_lba = lba;
*out_length = len;
TRACE_DBG("LBA %lld, length %d", (unsigned long long)lba, len);
out:
TRACE_EXIT_RES(res);
return res;
}
static int disk_cdb_set_transfer_data(uint8_t *cdb,
uint64_t lba, unsigned int len)
{
int res;
TRACE_ENTRY();
switch (cdb[0]) {
case WRITE_6:
case READ_6:
put_unaligned(cpu_to_be16(lba), (__be16 *)&cdb[2]);
cdb[4] = len;
break;
case WRITE_10:
case READ_10:
case WRITE_VERIFY:
put_unaligned(cpu_to_be32(lba), (__be32 *)&cdb[2]);
put_unaligned(cpu_to_be16(len), (__be16 *)&cdb[7]);
break;
case WRITE_12:
case READ_12:
case WRITE_VERIFY_12:
put_unaligned(cpu_to_be32(lba), (__be32 *)&cdb[2]);
put_unaligned(cpu_to_be32(len), (__be32 *)&cdb[6]);
break;
case WRITE_16:
case READ_16:
case WRITE_VERIFY_16:
put_unaligned(cpu_to_be64(lba), (__be64 *)&cdb[2]);
put_unaligned(cpu_to_be32(len), (__be32 *)&cdb[10]);
break;
default:
res = -EINVAL;
goto out;
}
res = 0;
TRACE_DBG("LBA %lld, length %d", (unsigned long long)lba, len);
TRACE_BUFFER("New CDB", cdb, SCST_MAX_CDB_SIZE);
out:
TRACE_EXIT_RES(res);
return res;
}
static void disk_cmd_done(void *data, char *sense, int result, int resid)
{
struct disk_work *work = data;
TRACE_ENTRY();
TRACE_DBG("work %p, cmd %p, left %d, result %d, sense %p, resid %d",
work, work->cmd, work->left, result, sense, resid);
if (result == SAM_STAT_GOOD)
goto out_complete;
work->result = result;
disk_cdb_set_transfer_data(work->cmd->cdb, work->save_lba, work->save_len);
work->cmd->sg = work->save_sg;
work->cmd->sg_cnt = work->save_sg_cnt;
scst_pass_through_cmd_done(work->cmd, sense, result, resid + work->left);
out_complete:
complete_all(&work->disk_work_cmpl);
TRACE_EXIT();
return;
}
/* Executes command and split CDB, if necessary */
static int disk_exec(struct scst_cmd *cmd)
{
int res = SCST_EXEC_NOT_COMPLETED, rc;
int res, rc;
struct disk_params *params = (struct disk_params *)cmd->dev->dh_priv;
struct disk_work work;
unsigned int offset, cur_len;
struct scatterlist *sg, *start_sg;
int cur_sg_cnt;
int sg_tablesize = cmd->dev->scsi_dev->host->sg_tablesize;
int num, j;
TRACE_ENTRY();
if (likely((cmd->sg_cnt <= sg_tablesize) &&
(cmd->out_sg_cnt <= sg_tablesize))) {
res = SCST_EXEC_NOT_COMPLETED;
goto out;
}
memset(&work, 0, sizeof(work));
work.cmd = cmd;
work.save_sg = cmd->sg;
work.save_sg_cnt = cmd->sg_cnt;
rc = disk_cdb_get_transfer_data(cmd->cdb, &work.save_lba,
&work.save_len);
if (rc != 0)
goto out_error;
rc = scst_check_local_events(cmd);
if (unlikely(rc != 0))
goto out_done;
cmd->status = 0;
cmd->msg_status = 0;
cmd->host_status = DID_OK;
cmd->driver_status = 0;
TRACE_DBG("cmd %p, save_sg %p, save_sg_cnt %d, save_lba %lld, "
"save_len %d (sg_tablesize %d, sizeof(*sg) 0x%x)", cmd,
work.save_sg, work.save_sg_cnt,
(unsigned long long)work.save_lba, work.save_len,
sg_tablesize, sizeof(*sg));
/*
* If we submit all chunks async'ly, it will be very not trivial what
* to do if several of them finish with sense or residual. So, let's
* do it synchronously.
*/
num = 1;
j = 0;
offset = 0;
cur_len = 0;
sg = work.save_sg;
start_sg = sg;
cur_sg_cnt = 0;
while (1) {
unsigned int l;
if (unlikely(sg_is_chain(&sg[j]))) {
bool reset_start_sg = (start_sg == &sg[j]);
sg = sg_chain_ptr(&sg[j]);
j = 0;
if (reset_start_sg)
start_sg = sg;
}
l = sg[j].length >> params->block_shift;
cur_len += l;
cur_sg_cnt++;
TRACE_DBG("l %d, j %d, num %d, offset %d, cur_len %d, "
"cur_sg_cnt %d, start_sg %p", l, j, num, offset,
cur_len, cur_sg_cnt, start_sg);
if (((num % sg_tablesize) == 0) || (num == work.save_sg_cnt)) {
TRACE_DBG("%s", "Execing...");
disk_cdb_set_transfer_data(cmd->cdb,
work.save_lba + offset, cur_len);
cmd->sg = start_sg;
cmd->sg_cnt = cur_sg_cnt;
work.left = work.save_len - (offset + cur_len);
init_completion(&work.disk_work_cmpl);
rc = scst_scsi_exec_async(cmd, &work, disk_cmd_done);
if (unlikely(rc != 0)) {
PRINT_ERROR("scst_scsi_exec_async() failed: %d",
rc);
goto out_err_restore;
}
wait_for_completion(&work.disk_work_cmpl);
if (work.result != SAM_STAT_GOOD) {
/* cmd can be already dead */
res = SCST_EXEC_COMPLETED;
goto out;
}
offset += cur_len;
cur_len = 0;
cur_sg_cnt = 0;
start_sg = &sg[j+1];
if (num == work.save_sg_cnt)
break;
}
num++;
j++;
}
cmd->completed = 1;
out_restore:
disk_cdb_set_transfer_data(cmd->cdb, work.save_lba, work.save_len);
cmd->sg = work.save_sg;
cmd->sg_cnt = work.save_sg_cnt;
out_done:
res = SCST_EXEC_COMPLETED;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
out:
TRACE_EXIT_RES(res);
return res;
out_err_restore:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out_restore;
out_error:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out_done;
}
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
static int disk_perf_exec(struct scst_cmd *cmd)
{
int res;
int opcode = cmd->cdb[0];
TRACE_ENTRY();
@@ -387,14 +655,21 @@ static int disk_exec(struct scst_cmd *cmd)
case READ_10:
case READ_12:
case READ_16:
cmd->completed = 1;
goto out_done;
case WRITE_VERIFY:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
goto out_complete;
}
res = SCST_EXEC_NOT_COMPLETED;
out:
TRACE_EXIT_RES(res);
return res;
out_complete:
cmd->completed = 1;
out_done:
res = SCST_EXEC_COMPLETED;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
@@ -405,3 +680,4 @@ MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SCSI disk (type 0) dev handler for SCST");
MODULE_VERSION(SCST_VERSION_STRING);

View File

@@ -4444,6 +4444,43 @@ void scst_release_request(struct scst_cmd *cmd)
}
#endif
static bool scst_on_sg_tablesize_low(struct scst_cmd *cmd, bool out)
{
bool res;
int sg_cnt = out ? cmd->out_sg_cnt : cmd->sg_cnt;
static int ll;
struct scst_tgt_dev *tgt_dev = cmd->tgt_dev;
TRACE_ENTRY();
if (sg_cnt > cmd->tgt->sg_tablesize) {
/* It's the target's side business */
goto failed;
}
if (tgt_dev->dev->handler->on_sg_tablesize_low == NULL)
goto failed;
res = tgt_dev->dev->handler->on_sg_tablesize_low(cmd);
TRACE_DBG("on_sg_tablesize_low(%p) returned %d", cmd, res);
out:
TRACE_EXIT_RES(res);
return res;
failed:
res = false;
if ((ll < 10) || TRACING_MINOR()) {
PRINT_INFO("Unable to complete command due to SG IO count "
"limitation (%srequested %d, available %d, tgt lim %d)",
out ? "OUT buffer, " : "", cmd->sg_cnt,
tgt_dev->max_sg_cnt, cmd->tgt->sg_tablesize);
ll++;
}
goto out;
}
int scst_alloc_space(struct scst_cmd *cmd)
{
gfp_t gfp_mask;
@@ -4451,7 +4488,6 @@ int scst_alloc_space(struct scst_cmd *cmd)
int atomic = scst_cmd_atomic(cmd);
int flags;
struct scst_tgt_dev *tgt_dev = cmd->tgt_dev;
static int ll;
TRACE_ENTRY();
@@ -4466,16 +4502,9 @@ int scst_alloc_space(struct scst_cmd *cmd)
if (cmd->sg == NULL)
goto out;
if (unlikely(cmd->sg_cnt > tgt_dev->max_sg_cnt)) {
if ((ll < 10) || TRACING_MINOR()) {
PRINT_INFO("Unable to complete command due to "
"SG IO count limitation (requested %d, "
"available %d, tgt lim %d)", cmd->sg_cnt,
tgt_dev->max_sg_cnt, cmd->tgt->sg_tablesize);
ll++;
}
goto out_sg_free;
}
if (unlikely(cmd->sg_cnt > tgt_dev->max_sg_cnt))
if (!scst_on_sg_tablesize_low(cmd, false))
goto out_sg_free;
if (cmd->data_direction != SCST_DATA_BIDI)
goto success;
@@ -4486,16 +4515,9 @@ int scst_alloc_space(struct scst_cmd *cmd)
if (cmd->out_sg == NULL)
goto out_sg_free;
if (unlikely(cmd->out_sg_cnt > tgt_dev->max_sg_cnt)) {
if ((ll < 10) || TRACING_MINOR()) {
PRINT_INFO("Unable to complete command due to "
"SG IO count limitation (OUT buffer, requested "
"%d, available %d, tgt lim %d)", cmd->out_sg_cnt,
tgt_dev->max_sg_cnt, cmd->tgt->sg_tablesize);
ll++;
}
goto out_out_sg_free;
}
if (unlikely(cmd->out_sg_cnt > tgt_dev->max_sg_cnt))
if (!scst_on_sg_tablesize_low(cmd, true))
goto out_out_sg_free;
success:
res = 0;
@@ -4718,10 +4740,11 @@ static void scsi_end_async(struct request *req, int error)
/**
* scst_scsi_exec_async - executes a SCSI command in pass-through mode
* @cmd: scst command
* @data: pointer passed to done() as "data"
* @done: callback function when done
*/
int scst_scsi_exec_async(struct scst_cmd *cmd,
void (*done)(void *, char *, int, int))
int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
void (*done)(void *data, char *sense, int result, int resid))
{
int res = 0;
struct request_queue *q = cmd->dev->scsi_dev->request_queue;
@@ -4806,7 +4829,7 @@ int scst_scsi_exec_async(struct scst_cmd *cmd,
done:
TRACE_DBG("sioc %p, cmd %p", sioc, cmd);
sioc->data = cmd;
sioc->data = data;
sioc->done = done;
rq->cmd_len = cmd_len;
@@ -4844,6 +4867,7 @@ out_free_sioc:
kfree(sioc);
goto out;
}
EXPORT_SYMBOL(scst_scsi_exec_async);
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) && defined(SCSI_EXEC_REQ_FIFO_DEFINED) */

View File

@@ -406,12 +406,9 @@ static inline int scst_exec_req(struct scsi_device *sdev,
#endif
}
#else /* i.e. LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
#if defined(SCSI_EXEC_REQ_FIFO_DEFINED)
int scst_scsi_exec_async(struct scst_cmd *cmd,
void (*done)(void *, char *, int, int));
#else
static inline int scst_scsi_exec_async(struct scst_cmd *cmd,
void (*done)(void *, char *, int, int))
#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
static inline int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
void (*done)(void *data, char *sense, int result, int resid))
{
WARN_ON_ONCE(1);
return -1;

View File

@@ -1568,7 +1568,15 @@ out:
return;
}
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */
static void scst_cmd_done(void *data, char *sense, int result, int resid)
/**
* scst_pass_through_cmd_done - done callback for pass-through commands
* @data: private opaque data
* @sense: pointer to the sense data, if any
* @result: command's execution result
* @resid: residual, if any
*/
void scst_pass_through_cmd_done(void *data, char *sense, int result, int resid)
{
struct scst_cmd *cmd;
@@ -1589,6 +1597,8 @@ out:
TRACE_EXIT();
return;
}
EXPORT_SYMBOL_GPL(scst_pass_through_cmd_done);
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */
static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state,
@@ -2602,11 +2612,11 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
rc = scst_exec_req(dev->scsi_dev, cmd->cdb, cmd->cdb_len,
cmd->data_direction, cmd->sg, cmd->bufflen, cmd->sg_cnt,
cmd->timeout, cmd->retries, cmd, scst_cmd_done,
GFP_KERNEL);
cmd->data_direction, cmd->sg, cmd->bufflen,
cmd->sg_cnt, cmd->timeout, cmd->retries, cmd,
scst_pass_through_cmd_done, GFP_KERNEL);
#else
rc = scst_scsi_exec_async(cmd, scst_cmd_done);
rc = scst_scsi_exec_async(cmd, cmd, scst_pass_through_cmd_done);
#endif
if (unlikely(rc != 0)) {
PRINT_ERROR("scst pass-through exec failed: %x", rc);
@@ -4839,6 +4849,7 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
mcmd, mcmd->state, mcmd->fn,
mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count);
spin_unlock_irq(&scst_mcmd_lock);
res = -1;
sBUG();
goto out;
}

View File

@@ -1367,7 +1367,8 @@ static struct scsi_host_template scst_lcl_ini_driver_template = {
.sg_tablesize = 0xFFFF,
.cmd_per_lun = 32,
.max_sectors = 0xffff,
.use_clustering = ENABLE_CLUSTERING,
/* Possible pass-through backend device may not support clustering */
.use_clustering = DISABLE_CLUSTERING,
.skip_settle_delay = 1,
.module = THIS_MODULE,
};