Custom commands parsing cleanups
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 12 Jan 2010 19:05:27 +0000 (19:05 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 12 Jan 2010 19:05:27 +0000 (19:05 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@1455 d57e44dd-8a1f-0410-8b47-8ef2f437770f

doc/scst_user_spec.txt
scst/include/scst.h
scst/include/scst_const.h
scst/include/scst_user.h
scst/src/dev_handlers/scst_user.c
scst/src/scst_lib.c
scst/src/scst_targ.c
usr/fileio/common.c

index 3c38ab7..48ac3c2 100644 (file)
@@ -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
  
index a68ee01..5c2be59 100644 (file)
@@ -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.
index 2dbf389..5256fd6 100644 (file)
@@ -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!!
index ca83ce1..2277b60 100644 (file)
@@ -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;
 };
index a2d7750..3ecedec 100644 (file)
@@ -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);
index 07a6f2e..3315d35 100644 (file)
@@ -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:
index 578d262..b748d5a 100644 (file)
@@ -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 (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));
+       }
 
-       if (cdb_len <= SCST_MAX_CDB_SIZE) {
-               memcpy(cmd->cdb, cdb, cdb_len);
-               cmd->cdb_len = cdb_len;
+       /*
+        * 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 "
index 07c1b90..735b18e 100644 (file)
@@ -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);