mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-14 09:11:27 +00:00
Custom commands parsing cleanups
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1455 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
USER SPACE INTERFACE DESCRIPTION.
|
||||
|
||||
Version 1.0.2
|
||||
Version 2.0.0
|
||||
|
||||
|
||||
I. Description.
|
||||
@@ -280,7 +280,7 @@ where:
|
||||
|
||||
- subcode - subcommand code, see 4.1 below
|
||||
|
||||
- preply - pointer to the reply data or, if 0, there is no reply. See
|
||||
- preply - pointer to the reply data or, if 0, there is no reply. See
|
||||
SCST_USER_REPLY_CMD for description of struct scst_user_reply_cmd
|
||||
fields
|
||||
|
||||
@@ -375,6 +375,8 @@ struct scst_user_scsi_cmd_parse
|
||||
int32_t bufflen;
|
||||
int32_t in_bufflen;
|
||||
|
||||
uint32_t op_flags;
|
||||
|
||||
uint8_t queue_type;
|
||||
uint8_t data_direction;
|
||||
|
||||
@@ -403,6 +405,8 @@ where:
|
||||
- in_bufflen - for bidirectional commands command's IN, i.e. from
|
||||
initiator to target, buffer length
|
||||
|
||||
- op_flags - CDB flags, one or more scst_cdb_flags bits, see below.
|
||||
|
||||
- queue_type - SCSI task attribute (queue type)
|
||||
|
||||
- data_direction - command's data flow direction, one of SCST_DATA_*
|
||||
@@ -418,6 +422,30 @@ where:
|
||||
|
||||
- sn - command's SN, which might be used for task management
|
||||
|
||||
Bits of scst_cdb_flags can be:
|
||||
|
||||
- SCST_TRANSFER_LEN_TYPE_FIXED - this command uses fixed blocks addressing
|
||||
|
||||
- SCST_SMALL_TIMEOUT - this command needs small timeout
|
||||
|
||||
- SCST_LONG_TIMEOUT - this command needs a long timeout
|
||||
|
||||
- SCST_UNKNOWN_LENGTH - data buffer lenght for this command is unknown
|
||||
|
||||
- SCST_INFO_VALID - bits of op_flags are valid
|
||||
|
||||
- SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED - mismatch of data_direction with
|
||||
the value supplied by target driver is allowed
|
||||
|
||||
- SCST_IMPLICIT_HQ - this command is an implicit HEAD OF QUEUE command
|
||||
|
||||
- SCST_SKIP_UA - Unit Attentions shouldn't be delivered for this command
|
||||
|
||||
- SCST_WRITE_MEDIUM - this command writes data on the medium, so should be
|
||||
forbidden for read-only devices
|
||||
|
||||
- SCST_LOCAL_CMD - this command can be processed by SCST core.
|
||||
|
||||
In the PARSE state of SCSI commands processing the user space device
|
||||
handler shall check and provide SCST values for command data buffer
|
||||
length, data flow direction and timeout, which it shall reply using the
|
||||
@@ -811,7 +839,8 @@ struct scst_user_scsi_cmd_reply_parse
|
||||
{
|
||||
uint8_t queue_type;
|
||||
uint8_t data_direction;
|
||||
uint8_t write_medium;
|
||||
int16_t cdb_len;
|
||||
uint32_t op_flags;
|
||||
int32_t data_len;
|
||||
int32_t bufflen;
|
||||
},
|
||||
@@ -822,8 +851,9 @@ where:
|
||||
|
||||
- data_direction - command's data flow direction, one of SCST_DATA_* constants
|
||||
|
||||
- write_medium - true, if command writes data to medium, so should be
|
||||
forbidden for read-only devices, false otherwise
|
||||
- cdb_len - length of CDB
|
||||
|
||||
- op_flags - commands flags, one or more scst_cdb_flags bits, see above.
|
||||
|
||||
- data_len - command's data length
|
||||
|
||||
|
||||
@@ -527,21 +527,6 @@ struct scst_aen;
|
||||
|
||||
typedef enum dma_data_direction scst_data_direction;
|
||||
|
||||
enum scst_cdb_flags {
|
||||
/* SCST_TRANSFER_LEN_TYPE_FIXED must be equiv 1 (FIXED_BIT in cdb) */
|
||||
SCST_TRANSFER_LEN_TYPE_FIXED = 0x001,
|
||||
SCST_SMALL_TIMEOUT = 0x002,
|
||||
SCST_LONG_TIMEOUT = 0x004,
|
||||
SCST_UNKNOWN_LENGTH = 0x008,
|
||||
SCST_INFO_NOT_FOUND = 0x010, /* must be single bit */
|
||||
SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED = 0x020,
|
||||
SCST_IMPLICIT_HQ = 0x040,
|
||||
SCST_SKIP_UA = 0x080,
|
||||
SCST_WRITE_MEDIUM = 0x100,
|
||||
SCST_LOCAL_CMD = 0x200,
|
||||
SCST_FULLY_LOCAL_CMD = 0x400,
|
||||
};
|
||||
|
||||
/*
|
||||
* Scsi_Target_Template: defines what functions a target driver will
|
||||
* have to provide in order to work with the target mid-level.
|
||||
|
||||
@@ -124,6 +124,23 @@ enum scst_cmd_queue_type {
|
||||
SCST_CMD_QUEUE_ACA
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
** CDB flags
|
||||
*************************************************************/
|
||||
enum scst_cdb_flags {
|
||||
SCST_TRANSFER_LEN_TYPE_FIXED = 0x001,
|
||||
SCST_SMALL_TIMEOUT = 0x002,
|
||||
SCST_LONG_TIMEOUT = 0x004,
|
||||
SCST_UNKNOWN_LENGTH = 0x008,
|
||||
SCST_INFO_VALID = 0x010, /* must be single bit */
|
||||
SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED = 0x020,
|
||||
SCST_IMPLICIT_HQ = 0x040,
|
||||
SCST_SKIP_UA = 0x080,
|
||||
SCST_WRITE_MEDIUM = 0x100,
|
||||
SCST_LOCAL_CMD = 0x200,
|
||||
SCST_FULLY_LOCAL_CMD = 0x400,
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
** Data direction aliases. Changing it don't forget to change
|
||||
** scst_to_tgt_dma_dir as well!!
|
||||
|
||||
@@ -120,6 +120,8 @@ struct scst_user_scsi_cmd_parse {
|
||||
int32_t bufflen;
|
||||
int32_t in_bufflen;
|
||||
|
||||
uint32_t op_flags;
|
||||
|
||||
uint8_t queue_type;
|
||||
uint8_t data_direction;
|
||||
|
||||
@@ -211,7 +213,8 @@ struct scst_user_get_cmd {
|
||||
struct scst_user_scsi_cmd_reply_parse {
|
||||
uint8_t queue_type;
|
||||
uint8_t data_direction;
|
||||
uint8_t write_medium;
|
||||
int16_t cdb_len;
|
||||
uint32_t op_flags;
|
||||
int32_t data_len;
|
||||
int32_t bufflen;
|
||||
};
|
||||
|
||||
@@ -780,14 +780,14 @@ static int dev_user_parse(struct scst_cmd *cmd)
|
||||
case SCST_USER_PARSE_STANDARD:
|
||||
TRACE_DBG("PARSE STANDARD: ucmd %p", ucmd);
|
||||
rc = dev->generic_parse(cmd, dev_user_get_block);
|
||||
if ((rc != 0) || (cmd->op_flags & SCST_INFO_NOT_FOUND))
|
||||
if ((rc != 0) || !(cmd->op_flags & SCST_INFO_VALID))
|
||||
goto out_invalid;
|
||||
break;
|
||||
|
||||
case SCST_USER_PARSE_EXCEPTION:
|
||||
TRACE_DBG("PARSE EXCEPTION: ucmd %p", ucmd);
|
||||
rc = dev->generic_parse(cmd, dev_user_get_block);
|
||||
if ((rc == 0) && (!(cmd->op_flags & SCST_INFO_NOT_FOUND)))
|
||||
if ((rc == 0) && (cmd->op_flags & SCST_INFO_VALID))
|
||||
break;
|
||||
else if (rc == SCST_CMD_STATE_NEED_THREAD_CTX) {
|
||||
TRACE_MEM("Restarting PARSE to thread context "
|
||||
@@ -821,6 +821,8 @@ static int dev_user_parse(struct scst_cmd *cmd)
|
||||
ucmd->user_cmd.parse_cmd.expected_transfer_len =
|
||||
cmd->expected_transfer_len;
|
||||
ucmd->user_cmd.parse_cmd.sn = cmd->tgt_sn;
|
||||
ucmd->user_cmd.parse_cmd.cdb_len = cmd->cdb_len;
|
||||
ucmd->user_cmd.parse_cmd.op_flags = cmd->op_flags;
|
||||
ucmd->state = UCMD_STATE_PARSING;
|
||||
dev_user_add_to_ready(ucmd);
|
||||
res = SCST_CMD_STATE_STOP;
|
||||
@@ -1266,17 +1268,23 @@ static int dev_user_process_reply_parse(struct scst_user_cmd *ucmd,
|
||||
if (unlikely((preply->bufflen < 0) || (preply->data_len < 0)))
|
||||
goto out_inval;
|
||||
|
||||
if (unlikely(preply->cdb_len > SCST_MAX_CDB_SIZE))
|
||||
goto out_inval;
|
||||
|
||||
TRACE_DBG("ucmd %p, queue_type %x, data_direction, %x, bufflen %d, "
|
||||
"data_len %d, pbuf %llx", ucmd, preply->queue_type,
|
||||
preply->data_direction, preply->bufflen, preply->data_len,
|
||||
reply->alloc_reply.pbuf);
|
||||
"data_len %d, pbuf %llx, cdb_len %d, op_flags %x", ucmd,
|
||||
preply->queue_type, preply->data_direction, preply->bufflen,
|
||||
preply->data_len, reply->alloc_reply.pbuf, preply->cdb_len,
|
||||
preply->op_flags);
|
||||
|
||||
cmd->queue_type = preply->queue_type;
|
||||
cmd->data_direction = preply->data_direction;
|
||||
cmd->bufflen = preply->bufflen;
|
||||
cmd->data_len = preply->data_len;
|
||||
if (preply->write_medium)
|
||||
cmd->op_flags |= SCST_WRITE_MEDIUM;
|
||||
if (preply->cdb_len > 0)
|
||||
cmd->cdb_len = preply->cdb_len;
|
||||
if (preply->op_flags & SCST_INFO_VALID)
|
||||
cmd->op_flags = preply->op_flags;
|
||||
|
||||
out_process:
|
||||
scst_post_parse_process_active_cmd(cmd, false);
|
||||
|
||||
@@ -4030,18 +4030,17 @@ int scst_get_cdb_info(struct scst_cmd *cmd)
|
||||
}
|
||||
|
||||
if (unlikely(ptr == NULL)) {
|
||||
/* opcode not found or now not used !!! */
|
||||
TRACE(TRACE_SCSI, "Unknown opcode 0x%x for type %d", op,
|
||||
/* opcode not found or now not used */
|
||||
TRACE(TRACE_MINOR, "Unknown opcode 0x%x for type %d", op,
|
||||
dev_type);
|
||||
res = -1;
|
||||
cmd->op_flags = SCST_INFO_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmd->cdb_len = SCST_GET_CDB_LEN(op);
|
||||
cmd->op_name = ptr->op_name;
|
||||
cmd->data_direction = ptr->direction;
|
||||
cmd->op_flags = ptr->flags;
|
||||
cmd->op_flags = ptr->flags | SCST_INFO_VALID;
|
||||
res = (*ptr->get_trans_len)(cmd, ptr->off);
|
||||
|
||||
out:
|
||||
|
||||
@@ -93,18 +93,27 @@ struct scst_cmd *scst_rx_cmd(struct scst_session *sess,
|
||||
cmd->tgt = sess->tgt;
|
||||
cmd->tgtt = sess->tgt->tgtt;
|
||||
|
||||
/*
|
||||
* For both wrong lun and CDB defer the error reporting for
|
||||
* scst_cmd_init_done()
|
||||
*/
|
||||
|
||||
cmd->lun = scst_unpack_lun(lun, lun_len);
|
||||
|
||||
if (cdb_len <= SCST_MAX_CDB_SIZE) {
|
||||
memcpy(cmd->cdb, cdb, cdb_len);
|
||||
cmd->cdb_len = cdb_len;
|
||||
if (unlikely(cmd->lun == NO_SUCH_LUN)) {
|
||||
PRINT_ERROR("Wrong LUN %d, finishing cmd", -1);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_lun_not_supported));
|
||||
}
|
||||
|
||||
/*
|
||||
* For cdb_len 0 defer the error reporting until scst_cmd_init_done(),
|
||||
* scst_set_cmd_error() supports nested calls.
|
||||
*/
|
||||
if (unlikely(cdb_len > SCST_MAX_CDB_SIZE)) {
|
||||
PRINT_ERROR("Too big CDB len %d, finishing cmd", cdb_len);
|
||||
cdb_len = SCST_MAX_CDB_SIZE;
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_message));
|
||||
}
|
||||
|
||||
memcpy(cmd->cdb, cdb, cdb_len);
|
||||
cmd->cdb_len = cdb_len;
|
||||
|
||||
TRACE_DBG("cmd %p, sess %p", cmd, sess);
|
||||
scst_sess_get(sess);
|
||||
|
||||
@@ -261,16 +270,8 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
|
||||
|
||||
spin_unlock_irqrestore(&sess->sess_list_lock, flags);
|
||||
|
||||
if (unlikely(cmd->lun == NO_SUCH_LUN)) {
|
||||
PRINT_ERROR("Wrong LUN %d, finishing cmd", -1);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_lun_not_supported));
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
goto active;
|
||||
}
|
||||
|
||||
if (unlikely(cmd->cdb_len == 0)) {
|
||||
PRINT_ERROR("%s", "Wrong CDB len, finishing cmd");
|
||||
PRINT_ERROR("%s", "Wrong CDB len 0, finishing cmd");
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
@@ -281,7 +282,6 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
|
||||
PRINT_ERROR("Unsupported queue type %d", cmd->queue_type);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_message));
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
goto active;
|
||||
}
|
||||
|
||||
@@ -358,95 +358,38 @@ static int scst_pre_parse(struct scst_cmd *cmd)
|
||||
if (unlikely(rc != 0)) {
|
||||
if (rc > 0) {
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
goto out_xmit;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
EXTRACHECKS_BUG_ON(cmd->op_flags & SCST_INFO_VALID);
|
||||
|
||||
cmd->cdb_len = scst_get_cdb_len(cmd->cdb);
|
||||
|
||||
TRACE(TRACE_MINOR, "Unknown opcode 0x%02x for %s. "
|
||||
"Should you update scst_scsi_op_table?",
|
||||
cmd->cdb[0], dev->handler->name);
|
||||
PRINT_BUFF_FLAG(TRACE_MINOR, "Failed CDB", cmd->cdb,
|
||||
cmd->cdb_len);
|
||||
#ifdef CONFIG_SCST_USE_EXPECTED_VALUES
|
||||
if (scst_cmd_is_expected_set(cmd)) {
|
||||
TRACE(TRACE_SCSI, "Using initiator supplied values: "
|
||||
"direction %d, transfer_len %d",
|
||||
cmd->expected_data_direction,
|
||||
cmd->expected_transfer_len);
|
||||
cmd->data_direction = cmd->expected_data_direction;
|
||||
|
||||
cmd->bufflen = cmd->expected_transfer_len;
|
||||
/* Restore (possibly) lost CDB length */
|
||||
cmd->cdb_len = scst_get_cdb_len(cmd->cdb);
|
||||
if (cmd->cdb_len == -1) {
|
||||
PRINT_ERROR("Unable to get CDB length for "
|
||||
"opcode 0x%02x. Returning INVALID "
|
||||
"OPCODE", cmd->cdb[0]);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
goto out_xmit;
|
||||
}
|
||||
} else {
|
||||
PRINT_ERROR("Unknown opcode 0x%02x for %s and "
|
||||
"target %s not supplied expected values",
|
||||
cmd->cdb[0], dev->handler->name, cmd->tgtt->name);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
goto out_xmit;
|
||||
}
|
||||
#else
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
goto out_xmit;
|
||||
#endif
|
||||
} else {
|
||||
TRACE(TRACE_SCSI, "op_name <%s> (cmd %p), direction=%d "
|
||||
"(expected %d, set %s), transfer_len=%d (expected "
|
||||
"len %d), flags=%d", cmd->op_name, cmd,
|
||||
cmd->data_direction, cmd->expected_data_direction,
|
||||
scst_cmd_is_expected_set(cmd) ? "yes" : "no",
|
||||
cmd->bufflen, cmd->expected_transfer_len,
|
||||
cmd->op_flags);
|
||||
|
||||
if (unlikely((cmd->op_flags & SCST_UNKNOWN_LENGTH) != 0)) {
|
||||
if (scst_cmd_is_expected_set(cmd)) {
|
||||
/*
|
||||
* Command data length can't be easily
|
||||
* determined from the CDB. ToDo, all such
|
||||
* commands processing should be fixed. Until
|
||||
* it's done, get the length from the supplied
|
||||
* expected value, but limit it to some
|
||||
* reasonable value (15MB).
|
||||
*/
|
||||
cmd->bufflen = min(cmd->expected_transfer_len,
|
||||
15*1024*1024);
|
||||
cmd->op_flags &= ~SCST_UNKNOWN_LENGTH;
|
||||
} else
|
||||
cmd->bufflen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT)) {
|
||||
PRINT_ERROR("NACA bit in control byte CDB is not supported "
|
||||
"(opcode 0x%02x)", cmd->cdb[0]);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
goto out_xmit;
|
||||
}
|
||||
|
||||
if (unlikely(cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_LINK_BIT)) {
|
||||
PRINT_ERROR("Linked commands are not supported "
|
||||
"(opcode 0x%02x)", cmd->cdb[0]);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
goto out_xmit;
|
||||
EXTRACHECKS_BUG_ON(!(cmd->op_flags & SCST_INFO_VALID));
|
||||
}
|
||||
|
||||
cmd->state = SCST_CMD_STATE_DEV_PARSE;
|
||||
|
||||
TRACE_DBG("op_name <%s> (cmd %p), direction=%d "
|
||||
"(expected %d, set %s), transfer_len=%d (expected "
|
||||
"len %d), flags=%d", cmd->op_name, cmd,
|
||||
cmd->data_direction, cmd->expected_data_direction,
|
||||
scst_cmd_is_expected_set(cmd) ? "yes" : "no",
|
||||
cmd->bufflen, cmd->expected_transfer_len,
|
||||
cmd->op_flags);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_xmit:
|
||||
out_err:
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
res = SCST_CMD_STATE_RES_CONT_SAME;
|
||||
goto out;
|
||||
@@ -457,22 +400,21 @@ static bool scst_is_allowed_to_mismatch_cmd(struct scst_cmd *cmd)
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
/* VERIFY commands with BYTCHK unset shouldn't fail here */
|
||||
if ((cmd->op_flags & SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED) &&
|
||||
(cmd->cdb[1] & BYTCHK) == 0) {
|
||||
res = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (cmd->cdb[0]) {
|
||||
case TEST_UNIT_READY:
|
||||
/* Crazy VMware people sometimes do TUR with READ direction */
|
||||
res = true;
|
||||
break;
|
||||
case VERIFY:
|
||||
case VERIFY_6:
|
||||
case VERIFY_12:
|
||||
case VERIFY_16:
|
||||
/* VERIFY commands with BYTCHK unset shouldn't fail here */
|
||||
if ((cmd->op_flags & SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED) &&
|
||||
(cmd->cdb[1] & BYTCHK) == 0)
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
@@ -490,7 +432,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
if (unlikely(!dev->handler->parse_atomic &&
|
||||
scst_cmd_atomic(cmd))) {
|
||||
/*
|
||||
* It shouldn't be because of SCST_TGT_DEV_AFTER_*
|
||||
* It shouldn't be because of the SCST_TGT_DEV_AFTER_*
|
||||
* optimization.
|
||||
*/
|
||||
TRACE_DBG("Dev handler %s parse() needs thread "
|
||||
@@ -531,15 +473,90 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
} else
|
||||
state = SCST_CMD_STATE_PREPARE_SPACE;
|
||||
|
||||
if (cmd->data_len == -1)
|
||||
cmd->data_len = cmd->bufflen;
|
||||
if (unlikely(state == SCST_CMD_STATE_PRE_XMIT_RESP))
|
||||
goto set_res;
|
||||
|
||||
if (cmd->bufflen == 0) {
|
||||
/*
|
||||
* According to SPC bufflen 0 for data transfer commands isn't
|
||||
* an error, so we need to fix the transfer direction.
|
||||
*/
|
||||
cmd->data_direction = SCST_DATA_NONE;
|
||||
if (unlikely(!(cmd->op_flags & SCST_INFO_VALID))) {
|
||||
#ifdef CONFIG_SCST_USE_EXPECTED_VALUES
|
||||
if (scst_cmd_is_expected_set(cmd)) {
|
||||
TRACE(TRACE_MINOR, "Using initiator supplied values: "
|
||||
"direction %d, transfer_len %d",
|
||||
cmd->expected_data_direction,
|
||||
cmd->expected_transfer_len);
|
||||
cmd->data_direction = cmd->expected_data_direction;
|
||||
cmd->bufflen = cmd->expected_transfer_len;
|
||||
} else {
|
||||
PRINT_ERROR("Unknown opcode 0x%02x for %s and "
|
||||
"target %s not supplied expected values",
|
||||
cmd->cdb[0], dev->handler->name, cmd->tgtt->name);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
goto out_done;
|
||||
}
|
||||
#else
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
goto out_done;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (unlikely(cmd->cdb_len == -1)) {
|
||||
PRINT_ERROR("Unable to get CDB length for "
|
||||
"opcode 0x%02x. Returning INVALID "
|
||||
"OPCODE", cmd->cdb[0]);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
EXTRACHECKS_BUG_ON(cmd->cdb_len == 0);
|
||||
|
||||
TRACE(TRACE_SCSI, "op_name <%s> (cmd %p), direction=%d "
|
||||
"(expected %d, set %s), transfer_len=%d (expected "
|
||||
"len %d), flags=%d", cmd->op_name, cmd,
|
||||
cmd->data_direction, cmd->expected_data_direction,
|
||||
scst_cmd_is_expected_set(cmd) ? "yes" : "no",
|
||||
cmd->bufflen, cmd->expected_transfer_len,
|
||||
cmd->op_flags);
|
||||
|
||||
if (unlikely((cmd->op_flags & SCST_UNKNOWN_LENGTH) != 0)) {
|
||||
if (scst_cmd_is_expected_set(cmd)) {
|
||||
/*
|
||||
* Command data length can't be easily
|
||||
* determined from the CDB. ToDo, all such
|
||||
* commands processing should be fixed. Until
|
||||
* it's done, get the length from the supplied
|
||||
* expected value, but limit it to some
|
||||
* reasonable value (15MB).
|
||||
*/
|
||||
cmd->bufflen = min(cmd->expected_transfer_len,
|
||||
15*1024*1024);
|
||||
cmd->op_flags &= ~SCST_UNKNOWN_LENGTH;
|
||||
} else {
|
||||
PRINT_ERROR("Unknown data transfer length for opcode "
|
||||
"0x%x (handler %s, target %s)", cmd->cdb[0],
|
||||
dev->handler->name, cmd->tgtt->name);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_message));
|
||||
goto out_done;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT)) {
|
||||
PRINT_ERROR("NACA bit in control byte CDB is not supported "
|
||||
"(opcode 0x%02x)", cmd->cdb[0]);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
if (unlikely(cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_LINK_BIT)) {
|
||||
PRINT_ERROR("Linked commands are not supported "
|
||||
"(opcode 0x%02x)", cmd->cdb[0]);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
if (cmd->dh_data_buf_alloced &&
|
||||
@@ -548,19 +565,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
"is less, than required (size %d)", cmd->bufflen,
|
||||
orig_bufflen);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (unlikely(state == SCST_CMD_STATE_PRE_XMIT_RESP))
|
||||
goto set_res;
|
||||
|
||||
if (unlikely((cmd->bufflen == 0) &&
|
||||
(cmd->op_flags & SCST_UNKNOWN_LENGTH))) {
|
||||
PRINT_ERROR("Unknown data transfer length for opcode 0x%x "
|
||||
"(handler %s, target %s)", cmd->cdb[0],
|
||||
dev->handler->name, cmd->tgtt->name);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
goto out_error;
|
||||
goto out_hw_error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
@@ -573,7 +578,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
cmd->data_direction, cmd->bufflen, state, cmd->sg,
|
||||
cmd->cdb[0]);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
goto out_error;
|
||||
goto out_hw_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -582,14 +587,15 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
# ifdef CONFIG_SCST_EXTRACHECKS
|
||||
if ((cmd->data_direction != cmd->expected_data_direction) ||
|
||||
(cmd->bufflen != cmd->expected_transfer_len)) {
|
||||
PRINT_WARNING("Expected values don't match decoded "
|
||||
"ones: data_direction %d, "
|
||||
TRACE(TRACE_MINOR, "Expected values don't match "
|
||||
"decoded ones: data_direction %d, "
|
||||
"expected_data_direction %d, "
|
||||
"bufflen %d, expected_transfer_len %d",
|
||||
cmd->data_direction,
|
||||
cmd->expected_data_direction,
|
||||
cmd->bufflen, cmd->expected_transfer_len);
|
||||
PRINT_BUFFER("Suspicious CDB", cmd->cdb, cmd->cdb_len);
|
||||
PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB",
|
||||
cmd->cdb, cmd->cdb_len);
|
||||
}
|
||||
# endif
|
||||
cmd->data_direction = cmd->expected_data_direction;
|
||||
@@ -606,11 +612,11 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
cmd->expected_data_direction,
|
||||
cmd->cdb[0], dev->handler->name,
|
||||
cmd->tgtt->name, cmd->data_direction);
|
||||
PRINT_BUFFER("Failed CDB",
|
||||
cmd->cdb, cmd->cdb_len);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb,
|
||||
cmd->cdb_len);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_message));
|
||||
goto out_dev_done;
|
||||
goto out_done;
|
||||
}
|
||||
}
|
||||
if (unlikely(cmd->bufflen != cmd->expected_transfer_len)) {
|
||||
@@ -634,10 +640,21 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
"target %s", cmd->cdb[0], dev->handler->name,
|
||||
cmd->tgtt->name);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
goto out_error;
|
||||
goto out_hw_error;
|
||||
}
|
||||
|
||||
set_res:
|
||||
if (cmd->data_len == -1)
|
||||
cmd->data_len = cmd->bufflen;
|
||||
|
||||
if (cmd->bufflen == 0) {
|
||||
/*
|
||||
* According to SPC bufflen 0 for data transfer commands isn't
|
||||
* an error, so we need to fix the transfer direction.
|
||||
*/
|
||||
cmd->data_direction = SCST_DATA_NONE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
switch (state) {
|
||||
case SCST_CMD_STATE_PREPARE_SPACE:
|
||||
@@ -670,7 +687,7 @@ set_res:
|
||||
"error %d (opcode %d)", dev->handler->name,
|
||||
state, cmd->cdb[0]);
|
||||
}
|
||||
goto out_error;
|
||||
goto out_hw_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -681,18 +698,20 @@ set_res:
|
||||
cmd->resp_data_len = 0;
|
||||
}
|
||||
|
||||
/* We already completed (with an error) */
|
||||
if (unlikely(cmd->completed))
|
||||
goto out_done;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_HRES(res);
|
||||
return res;
|
||||
|
||||
out_error:
|
||||
out_hw_error:
|
||||
/* dev_done() will be called as part of the regular cmd's finish */
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
|
||||
#ifndef CONFIG_SCST_USE_EXPECTED_VALUES
|
||||
out_dev_done:
|
||||
#endif
|
||||
cmd->state = SCST_CMD_STATE_PRE_DEV_DONE;
|
||||
out_done:
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
res = SCST_CMD_STATE_RES_CONT_SAME;
|
||||
goto out;
|
||||
}
|
||||
@@ -915,7 +934,7 @@ static int scst_rdy_to_xfer(struct scst_cmd *cmd)
|
||||
|
||||
if (unlikely(!tgtt->rdy_to_xfer_atomic && scst_cmd_atomic(cmd))) {
|
||||
/*
|
||||
* It shouldn't be because of SCST_TGT_DEV_AFTER_*
|
||||
* It shouldn't be because of the SCST_TGT_DEV_AFTER_*
|
||||
* optimization.
|
||||
*/
|
||||
TRACE_DBG("Target driver %s rdy_to_xfer() needs thread "
|
||||
@@ -1976,7 +1995,7 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
|
||||
if (handler->exec) {
|
||||
if (unlikely(!dev->handler->exec_atomic && atomic)) {
|
||||
/*
|
||||
* It shouldn't be because of SCST_TGT_DEV_AFTER_*
|
||||
* It shouldn't be because of the SCST_TGT_DEV_AFTER_*
|
||||
* optimization.
|
||||
*/
|
||||
TRACE_DBG("Dev handler %s exec() needs thread "
|
||||
@@ -2762,7 +2781,7 @@ static int scst_dev_done(struct scst_cmd *cmd)
|
||||
if (unlikely(!dev->handler->dev_done_atomic &&
|
||||
scst_cmd_atomic(cmd))) {
|
||||
/*
|
||||
* It shouldn't be because of SCST_TGT_DEV_AFTER_*
|
||||
* It shouldn't be because of the SCST_TGT_DEV_AFTER_*
|
||||
* optimization.
|
||||
*/
|
||||
TRACE_DBG("Dev handler %s dev_done() needs thread "
|
||||
@@ -2931,7 +2950,7 @@ static int scst_xmit_response(struct scst_cmd *cmd)
|
||||
if (unlikely(!tgtt->xmit_response_atomic &&
|
||||
scst_cmd_atomic(cmd))) {
|
||||
/*
|
||||
* It shouldn't be because of SCST_TGT_DEV_AFTER_*
|
||||
* It shouldn't be because of the SCST_TGT_DEV_AFTER_*
|
||||
* optimization.
|
||||
*/
|
||||
TRACE_DBG("Target driver %s xmit_response() needs thread "
|
||||
|
||||
@@ -191,6 +191,8 @@ static int do_parse(struct vdisk_cmd *vcmd)
|
||||
reply->data_direction = cmd->expected_data_direction;
|
||||
reply->data_len = cmd->expected_transfer_len;
|
||||
reply->bufflen = cmd->expected_transfer_len;
|
||||
reply->cdb_len = cmd->cdb_len;
|
||||
reply->op_flags = reply->op_flags;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
|
||||
Reference in New Issue
Block a user