Bidirectional data transfers added
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Fri, 3 Apr 2009 17:49:26 +0000 (17:49 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Fri, 3 Apr 2009 17:49:26 +0000 (17:49 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@744 d57e44dd-8a1f-0410-8b47-8ef2f437770f

doc/scst_user_spec.txt
iscsi-scst/kernel/iscsi.c
iscsi-scst/kernel/iscsi.h
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_main.c
scst/src/scst_targ.c
scst_local/scst_local.c

index 97ea7a8..54c8096 100644 (file)
@@ -2,7 +2,7 @@
 
          USER SPACE INTERFACE DESCRIPTION.
 
-                  Version 1.0.0
+                  Version 1.0.2
 
 
                I. Description.
@@ -341,6 +341,7 @@ struct scst_user_scsi_cmd_parse
 
        uint32_t timeout;
        int32_t bufflen;
+       int32_t in_bufflen;
 
        uint8_t queue_type;
        uint8_t data_direction;
@@ -367,6 +368,9 @@ where:
 
  - bufflen - command's buffer length
  
+ - in_bufflen - for bidirectional commands command's IN, i.e. from
+   initiator to target, buffer length
  - queue_type - SCSI task attribute (queue type)
  
  - data_direction - command's data flow direction, one of SCST_DATA_*
@@ -536,6 +540,9 @@ struct scst_user_scsi_cmd_exec
        uint8_t partial;
        uint32_t timeout;
 
+       aligned_u64 p_in_buf;
+       int32_t in_bufflen;
+
        uint32_t sn;
 
        uint32_t parent_cmd_h;
@@ -580,6 +587,13 @@ where:
 
  - timeout - CDB execution timeout
 
+ - p_in_buf - for bidirectional commands pointer on command's IN, i.e. from
+   initiator to target, data buffer or 0 for SCSI commands without data
+   transfer
+
+ - in_bufflen - for bidirectional commands command's IN, i.e. from
+   initiator to target, buffer length
+
  - sn - command's SN, which might be used for task management
 
  - parent_cmd_h - has the same unique value for all partial data
index 57f50a2..697e212 100644 (file)
@@ -55,9 +55,6 @@ DECLARE_WAIT_QUEUE_HEAD(iscsi_wr_waitQ);
 static struct page *dummy_page;
 static struct scatterlist dummy_sg;
 
-static uint8_t sense_fixed_unexpected_unsolicited_data[SCST_STANDARD_SENSE_LEN];
-static uint8_t sense_descr_unexpected_unsolicited_data[SCST_STANDARD_SENSE_LEN];
-
 struct iscsi_thread_t {
        struct task_struct *thr;
        struct list_head threads_list_entry;
@@ -81,7 +78,7 @@ static inline u32 cmnd_write_size(struct iscsi_cmnd *cmnd)
        return 0;
 }
 
-static inline u32 cmnd_read_size(struct iscsi_cmnd *cmnd)
+static inline int cmnd_read_size(struct iscsi_cmnd *cmnd)
 {
        struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd);
 
@@ -112,6 +109,7 @@ static inline u32 cmnd_read_size(struct iscsi_cmnd *cmnd)
                                p += s;
                        } while (size < cmnd->pdu.ahssize);
                }
+               return -1;
        }
        return 0;
 }
@@ -611,13 +609,13 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status)
        struct iscsi_cmnd *rsp;
        struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
        struct iscsi_data_in_hdr *rsp_hdr;
-       u32 pdusize, expsize, scsisize, size, offset, sn;
+       u32 pdusize, expsize, size, offset, sn;
        LIST_HEAD(send);
 
        TRACE_DBG("req %p", req);
 
        pdusize = req->conn->session->sess_param.max_xmit_data_length;
-       expsize = cmnd_read_size(req);
+       expsize = req->read_size;
        size = min(expsize, (u32)req->bufflen);
        offset = 0;
        sn = 0;
@@ -640,21 +638,26 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status)
                        TRACE_DBG("offset %d, size %d", offset, size);
                        rsp->pdu.datasize = size;
                        if (send_status) {
+                               int scsisize;
+
                                TRACE_DBG("status %x", status);
-                               rsp_hdr->flags =
-                                       ISCSI_FLG_FINAL | ISCSI_FLG_STATUS;
+
+                               EXTRACHECKS_BUG_ON((cmnd_hdr(req)->flags & ISCSI_CMD_WRITE) != 0);
+
+                               rsp_hdr->flags = ISCSI_FLG_FINAL | ISCSI_FLG_STATUS;
                                rsp_hdr->cmd_status = status;
+
+                               scsisize = req->bufflen;
+                               if (scsisize < expsize) {
+                                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+                                       size = expsize - scsisize;
+                               } else if (scsisize > expsize) {
+                                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW;
+                                       size = scsisize - expsize;
+                               } else
+                                       size = 0;
+                               rsp_hdr->residual_count = cpu_to_be32(size);
                        }
-                       scsisize = req->bufflen;
-                       if (scsisize < expsize) {
-                               rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
-                               size = expsize - scsisize;
-                       } else if (scsisize > expsize) {
-                               rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW;
-                               size = scsisize - expsize;
-                       } else
-                               size = 0;
-                       rsp_hdr->residual_count = cpu_to_be32(size);
                        list_add_tail(&rsp->write_list_entry, &send);
                        break;
                }
@@ -969,11 +972,71 @@ static void cmnd_prepare_get_rejected_cmd_data(struct iscsi_cmnd *cmnd)
        return;
 }
 
+static void iscsi_set_resid(struct iscsi_cmnd *req, struct iscsi_cmnd *rsp,
+       bool data_used)
+{
+       struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
+       struct iscsi_scsi_rsp_hdr *rsp_hdr;
+       int resid, resp_len, in_resp_len;
+
+       if ((req_hdr->flags & ISCSI_CMD_READ) &&
+           (req_hdr->flags & ISCSI_CMD_WRITE)) {
+               rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
+
+               if (data_used) {
+                       resp_len = req->bufflen;
+                       if (req->scst_cmd != NULL)
+                               in_resp_len = scst_cmd_get_in_bufflen(req->scst_cmd);
+                       else
+                               in_resp_len = 0;
+               } else {
+                       resp_len = 0;
+                       in_resp_len = 0;
+               }
+
+               resid = be32_to_cpu(req_hdr->data_length) - in_resp_len;
+               if (resid > 0) {
+                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+                       rsp_hdr->residual_count = cpu_to_be32(resid);
+               } else if (resid < 0) {
+                       resid = -resid;
+                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW;
+                       rsp_hdr->residual_count = cpu_to_be32(resid);
+               }
+
+               resid = req->read_size - resp_len;
+               if (resid > 0) {
+                       rsp_hdr->flags |= ISCSI_FLG_BIRESIDUAL_UNDERFLOW;
+                       rsp_hdr->bi_residual_count = cpu_to_be32(resid);
+               } else if (resid < 0) {
+                       resid = -resid;
+                       rsp_hdr->flags |= ISCSI_FLG_BIRESIDUAL_OVERFLOW;
+                       rsp_hdr->bi_residual_count = cpu_to_be32(resid);
+               }
+       } else {
+               if (data_used)
+                       resp_len = req->bufflen;
+               else
+                       resp_len = 0;
+
+               resid = req->read_size - resp_len;
+               if (resid > 0) {
+                       rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
+                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+                       rsp_hdr->residual_count = cpu_to_be32(resid);
+               } else if (resid < 0) {
+                       rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
+                       resid = -resid;
+                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW;
+                       rsp_hdr->residual_count = cpu_to_be32(resid);
+               }
+       }
+       return;
+}
+
 static void cmnd_reject_scsi_cmd(struct iscsi_cmnd *req)
 {
        struct iscsi_cmnd *rsp;
-       struct iscsi_scsi_rsp_hdr *rsp_hdr;
-       u32 size;
 
        TRACE_DBG("%p", req);
 
@@ -987,25 +1050,9 @@ static void cmnd_reject_scsi_cmd(struct iscsi_cmnd *req)
                goto out_reject;
        }
 
-       rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
-
        sBUG_ON(cmnd_opcode(rsp) != ISCSI_OP_SCSI_RSP);
 
-       size = cmnd_write_size(req);
-       if (size) {
-               rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
-               rsp_hdr->residual_count = cpu_to_be32(size);
-       }
-       size = cmnd_read_size(req);
-       if (size) {
-               if (cmnd_hdr(req)->flags & ISCSI_CMD_WRITE) {
-                       rsp_hdr->flags |= ISCSI_FLG_BIRESIDUAL_UNDERFLOW;
-                       rsp_hdr->bi_residual_count = cpu_to_be32(size);
-               } else {
-                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
-                       rsp_hdr->residual_count = cpu_to_be32(size);
-               }
-       }
+       iscsi_set_resid(req, rsp, false);
 
        iscsi_cmnd_init_write(rsp, ISCSI_INIT_WRITE_REMOVE_HASH |
                                         ISCSI_INIT_WRITE_WAKE);
@@ -1152,11 +1199,6 @@ static int iscsi_pre_exec(struct scst_cmd *scst_cmd)
 
        EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd));
 
-       if (scst_cmd_get_data_direction(scst_cmd) == SCST_DATA_READ) {
-               EXTRACHECKS_BUG_ON(!list_empty(&req->rx_ddigest_cmd_list));
-               goto out;
-       }
-
        /* If data digest isn't used this list will be empty */
        list_for_each_entry_safe(c, t, &req->rx_ddigest_cmd_list,
                                rx_ddigest_cmd_list_entry) {
@@ -1326,17 +1368,44 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
        scst_cmd_set_tag(scst_cmd, req_hdr->itt);
        scst_cmd_set_tgt_priv(scst_cmd, req);
 
-       if (req_hdr->flags & ISCSI_CMD_READ) {
+       if ((req_hdr->flags & ISCSI_CMD_READ) &&
+           (req_hdr->flags & ISCSI_CMD_WRITE)) {
+               int sz = cmnd_read_size(req);
+               if (unlikely(sz < 0)) {
+                       PRINT_ERROR("%s", "BIDI data transfer, but initiator "
+                               "not supplied Bidirectional Read Expected Data "
+                               "Transfer Length AHS");
+                       scst_set_cmd_error(scst_cmd,
+                          SCST_LOAD_SENSE(scst_sense_parameter_value_invalid));
+                       /*
+                        * scst_cmd_init_done() will handle commands with
+                        * set status as preliminary completed
+                        */
+               } else {
+                       req->read_size = sz;
+                       dir = SCST_DATA_BIDI;
+                       scst_cmd_set_expected(scst_cmd, dir, sz);
+                       scst_cmd_set_expected_in_transfer_len(scst_cmd,
+                               be32_to_cpu(req_hdr->data_length));
+#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
+                       scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd);
+#endif
+               }
+       } else if (req_hdr->flags & ISCSI_CMD_READ) {
+               req->read_size = be32_to_cpu(req_hdr->data_length);
                dir = SCST_DATA_READ;
+               scst_cmd_set_expected(scst_cmd, dir, req->read_size);
 #if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
                scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd);
 #endif
-       } else if (req_hdr->flags & ISCSI_CMD_WRITE)
+       } else if (req_hdr->flags & ISCSI_CMD_WRITE) {
                dir = SCST_DATA_WRITE;
-       else
+               scst_cmd_set_expected(scst_cmd, dir,
+                       be32_to_cpu(req_hdr->data_length));
+       } else {
                dir = SCST_DATA_NONE;
-       scst_cmd_set_expected(scst_cmd, dir,
-               be32_to_cpu(req_hdr->data_length));
+               scst_cmd_set_expected(scst_cmd, dir, 0);
+       }
 
        switch (req_hdr->flags & ISCSI_CMD_ATTR_MASK) {
        case ISCSI_CMD_SIMPLE:
@@ -1410,35 +1479,40 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
        }
 
        dir = scst_cmd_get_data_direction(scst_cmd);
-       if (dir != SCST_DATA_WRITE) {
+       if (dir & SCST_DATA_WRITE) {
+               req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL);
+               req->r2t_length = be32_to_cpu(req_hdr->data_length) -
+                                       req->pdu.datasize;
+               if (req->r2t_length > 0)
+                       req->data_waiting = 1;
+       } else {
                if (unlikely(!(req_hdr->flags & ISCSI_CMD_FINAL) ||
                             req->pdu.datasize)) {
                        PRINT_ERROR("Unexpected unsolicited data (ITT %x "
                                "CDB %x", cmnd_itt(req), req_hdr->scb[0]);
-                       if (scst_get_cmd_dev_d_sense(scst_cmd))
+                       scst_set_cmd_error(scst_cmd,
+                               SCST_LOAD_SENSE(iscsi_sense_unexpected_unsolicited_data));
+                       if (scst_cmd_get_sense_buffer(scst_cmd) != NULL)
                                create_status_rsp(req, SAM_STAT_CHECK_CONDITION,
-                                       sense_descr_unexpected_unsolicited_data,
-                                       sizeof(sense_descr_unexpected_unsolicited_data));
+                                   scst_cmd_get_sense_buffer(scst_cmd),
+                                   scst_cmd_get_sense_buffer_len(scst_cmd));
                        else
-                               create_status_rsp(req, SAM_STAT_CHECK_CONDITION,
-                                       sense_fixed_unexpected_unsolicited_data,
-                                       sizeof(sense_fixed_unexpected_unsolicited_data));
+                               create_status_rsp(req, SAM_STAT_BUSY, NULL, 0);
                        cmnd_reject_scsi_cmd(req);
                        goto out;
                }
        }
-
-       if (dir == SCST_DATA_WRITE) {
-               req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL);
-               req->r2t_length = be32_to_cpu(req_hdr->data_length) -
-                                       req->pdu.datasize;
-               if (req->r2t_length > 0)
-                       req->data_waiting = 1;
-       }
+       
        req->target_task_tag = get_next_ttt(conn);
-       req->sg = scst_cmd_get_sg(scst_cmd);
-       req->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd);
-       req->bufflen = scst_cmd_get_bufflen(scst_cmd);
+       if (dir != SCST_DATA_BIDI) {
+               req->sg = scst_cmd_get_sg(scst_cmd);
+               req->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd);
+               req->bufflen = scst_cmd_get_bufflen(scst_cmd);
+       } else {
+               req->sg = scst_cmd_get_in_sg(scst_cmd);
+               req->sg_cnt = scst_cmd_get_in_sg_cnt(scst_cmd);
+               req->bufflen = scst_cmd_get_in_bufflen(scst_cmd);
+       }
        if (unlikely(req->r2t_length > req->bufflen)) {
                PRINT_ERROR("req->r2t_length %d > req->bufflen %d",
                        req->r2t_length, req->bufflen);
@@ -1469,23 +1543,8 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
                goto out;
        }
 
-       if (req->pdu.datasize) {
-               if (unlikely(dir != SCST_DATA_WRITE)) {
-                       PRINT_ERROR("pdu.datasize(%d) >0, but dir(%x) isn't "
-                               "WRITE", req->pdu.datasize, dir);
-                       if (scst_get_cmd_dev_d_sense(scst_cmd))
-                               create_status_rsp(req, SAM_STAT_CHECK_CONDITION,
-                                       sense_descr_unexpected_unsolicited_data,
-                                       sizeof(sense_descr_unexpected_unsolicited_data));
-                       else
-                               create_status_rsp(req, SAM_STAT_CHECK_CONDITION,
-                                       sense_fixed_unexpected_unsolicited_data,
-                                       sizeof(sense_fixed_unexpected_unsolicited_data));
-                       cmnd_reject_scsi_cmd(req);
-               } else
-                       res = cmnd_prepare_recv_pdu(conn, req, 0,
-                               req->pdu.datasize);
-       }
+       if (req->pdu.datasize)
+               res = cmnd_prepare_recv_pdu(conn, req, 0, req->pdu.datasize);
 out:
        /* Aborted commands will be freed in cmnd_rx_end() */
        TRACE_EXIT_RES(res);
@@ -2518,7 +2577,7 @@ static int iscsi_alloc_data_buf(struct scst_cmd *cmd)
         * by the network layers. It is possible only if we
         * don't use SGV cache.
         */
-       EXTRACHECKS_BUG_ON(scst_cmd_get_data_direction(cmd) != SCST_DATA_READ);
+       EXTRACHECKS_BUG_ON(!(scst_cmd_get_data_direction(cmd) & SCST_DATA_READ));
        scst_cmd_set_no_sgv(cmd);
        return 1;
 }
@@ -2685,45 +2744,25 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
                 * so status is valid here, but in future that could change.
                 * ToDo
                 */
-               if (status != SAM_STAT_CHECK_CONDITION) {
+               if ((status != SAM_STAT_CHECK_CONDITION) &&
+                   ((cmnd_hdr(req)->flags & (ISCSI_CMD_WRITE|ISCSI_CMD_READ)) !=
+                               (ISCSI_CMD_WRITE|ISCSI_CMD_READ))) {
                        send_data_rsp(req, status, is_send_status);
                } else {
                        struct iscsi_cmnd *rsp;
-                       struct iscsi_scsi_rsp_hdr *rsp_hdr;
-                       int resid;
                        send_data_rsp(req, 0, 0);
                        if (is_send_status) {
                                rsp = create_status_rsp(req, status, sense,
                                        sense_len);
-                               rsp_hdr =
-                                   (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
-                               resid = cmnd_read_size(req) - req->bufflen;
-                               if (resid > 0) {
-                                       rsp_hdr->flags |=
-                                               ISCSI_FLG_RESIDUAL_UNDERFLOW;
-                                       rsp_hdr->residual_count =
-                                               cpu_to_be32(resid);
-                               } else if (resid < 0) {
-                                       rsp_hdr->flags |=
-                                               ISCSI_FLG_RESIDUAL_OVERFLOW;
-                                       rsp_hdr->residual_count =
-                                               cpu_to_be32(-resid);
-                               }
+                               iscsi_set_resid(req, rsp, true);
                                iscsi_cmnd_init_write(rsp,
                                        ISCSI_INIT_WRITE_REMOVE_HASH);
                        }
                }
        } else if (is_send_status) {
                struct iscsi_cmnd *rsp;
-               struct iscsi_scsi_rsp_hdr *rsp_hdr;
-               u32 resid;
                rsp = create_status_rsp(req, status, sense, sense_len);
-               rsp_hdr = (struct iscsi_scsi_rsp_hdr *) &rsp->pdu.bhs;
-               resid = cmnd_read_size(req);
-               if (resid != 0) {
-                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
-                       rsp_hdr->residual_count = cpu_to_be32(resid);
-               }
+               iscsi_set_resid(req, rsp, false);
                iscsi_cmnd_init_write(rsp, ISCSI_INIT_WRITE_REMOVE_HASH);
        }
 #ifdef CONFIG_SCST_EXTRACHECKS
@@ -3095,17 +3134,6 @@ static int __init iscsi_init(void)
 
        PRINT_INFO("iSCSI SCST Target - version %s", ISCSI_VERSION_STRING);
 
-       sense_fixed_unexpected_unsolicited_data[0] = 0x70;
-       sense_fixed_unexpected_unsolicited_data[2] = ABORTED_COMMAND;
-       sense_fixed_unexpected_unsolicited_data[7] = 6;
-       sense_fixed_unexpected_unsolicited_data[12] = 0xc;
-       sense_fixed_unexpected_unsolicited_data[13] = 0xc;
-
-       sense_descr_unexpected_unsolicited_data[0] = 0x72;
-       sense_descr_unexpected_unsolicited_data[1] = ABORTED_COMMAND;
-       sense_descr_unexpected_unsolicited_data[2] = 0xc;
-       sense_descr_unexpected_unsolicited_data[3] = 0xc;
-
        dummy_page = alloc_pages(GFP_KERNEL, 0);
        if (dummy_page == NULL) {
                PRINT_ERROR("%s", "Dummy page allocation failed");
index 4255d61..680efa6 100644 (file)
@@ -30,7 +30,8 @@
 
 #include "iscsi_dbg.h"
 
-#define iscsi_sense_crc_error          ABORTED_COMMAND, 0x47, 0x5
+#define iscsi_sense_crc_error                  ABORTED_COMMAND, 0x47, 0x5
+#define iscsi_sense_unexpected_unsolicited_data        ABORTED_COMMAND, 0xC, 0xC
 
 struct iscsi_sess_param {
        int initial_r2t;
@@ -350,6 +351,7 @@ struct iscsi_cmnd {
                                struct scst_cmd *scst_cmd;
                                struct scst_aen *scst_aen;
                        };
+                       int read_size;
                };
 
                /* Response only fields */
index eca46de..060f508 100644 (file)
@@ -1253,6 +1253,7 @@ struct scst_cmd {
        /* Remote initiator supplied values, if any */
        scst_data_direction expected_data_direction;
        int expected_transfer_len;
+       int expected_in_transfer_len; /* for bidi writes */
 
        /*
         * Cmd data length. Could be different from bufflen for commands like
@@ -1266,20 +1267,25 @@ struct scst_cmd {
                enum scst_exec_context pref_context);
 
        struct sgv_pool_obj *sgv;       /* sgv object */
-
        int bufflen;                    /* cmd buffer length */
        struct scatterlist *sg;         /* cmd data buffer SG vector */
        int sg_cnt;                     /* SG segments count */
 
-       /* scst_get_sg_buf_[first,next]() support */
-       int get_sg_buf_entry_num;
-
        /*
         * Response data length in data buffer. This field must not be set
         * directly, use scst_set_resp_data_len() for that
         */
        int resp_data_len;
 
+       /* scst_get_sg_buf_[first,next]() support */
+       int get_sg_buf_entry_num;
+
+       /* Bidirectional transfers support */
+       int in_bufflen;                 /* WRITE buffer length */
+       struct sgv_pool_obj *in_sgv;    /* WRITE sgv object */
+       struct scatterlist *in_sg;      /* WRITE data buffer SG vector */
+       int in_sg_cnt;                  /* WRITE SG segments count */
+
        /*
         * Used if both target driver and dev handler request own memory
         * allocation. In other cases, both are equal to sg and sg_cnt
@@ -1291,6 +1297,8 @@ struct scst_cmd {
         */
        struct scatterlist *tgt_sg;
        int tgt_sg_cnt;
+       struct scatterlist *tgt_in_sg;  /* bidirectional */
+       int tgt_in_sg_cnt;              /* bidirectional */
 
        /*
         * The status fields in case of errors must be set using
@@ -2021,25 +2029,16 @@ struct scst_cmd *scst_find_cmd(struct scst_session *sess, void *data,
                                              void *data));
 
 /*
- * Translates SCST's data direction to DMA one
+ * Translates SCST's data direction to DMA one from backend storage
+ * perspective.
  */
-static inline int scst_to_dma_dir(int scst_dir)
-{
-       return scst_dir;
-}
+enum dma_data_direction scst_to_dma_dir(int scst_dir);
 
 /*
- * Translates SCST data direction to DMA one from the perspective
+ * Translates SCST data direction to DMA data direction from the perspective
  * of the target device.
  */
-static inline int scst_to_tgt_dma_dir(int scst_dir)
-{
-       if (scst_dir == SCST_DATA_WRITE)
-               return DMA_FROM_DEVICE;
-       else if (scst_dir == SCST_DATA_READ)
-               return DMA_TO_DEVICE;
-       return scst_dir;
-}
+enum dma_data_direction scst_to_tgt_dma_dir(int scst_dir);
 
 /*
  * Returns 1, if cmd's CDB is locally handled by SCST and 0 otherwise.
@@ -2242,6 +2241,40 @@ static inline unsigned int scst_cmd_get_bufflen(struct scst_cmd *cmd)
        return cmd->bufflen;
 }
 
+/*
+ * Returns pointer to cmd's bidirectional in (WRITE) SG data buffer.
+ *
+ * Usage of this function is not recommended, use scst_get_in_buf_*()
+ * family of functions instead.
+ */
+static inline struct scatterlist *scst_cmd_get_in_sg(struct scst_cmd *cmd)
+{
+       return cmd->in_sg;
+}
+
+/*
+ * Returns cmd's bidirectional in (WRITE) sg_cnt.
+ *
+ * Usage of this function is not recommended, use scst_get_in_buf_*()
+ * family of functions instead.
+ */
+static inline int scst_cmd_get_in_sg_cnt(struct scst_cmd *cmd)
+{
+       return cmd->in_sg_cnt;
+}
+
+/*
+ * Returns cmd's bidirectional in (WRITE) data buffer length.
+ *
+ * In case if you need to iterate over data in the buffer, usage of
+ * this function is not recommended, use scst_get_in_buf_*()
+ * family of functions instead.
+ */
+static inline unsigned int scst_cmd_get_in_bufflen(struct scst_cmd *cmd)
+{
+       return cmd->in_bufflen;
+}
+
 /* Returns pointer to cmd's target's SG data buffer */
 static inline struct scatterlist *scst_cmd_get_tgt_sg(struct scst_cmd *cmd)
 {
@@ -2263,6 +2296,28 @@ static inline void scst_cmd_set_tgt_sg(struct scst_cmd *cmd,
        cmd->tgt_data_buf_alloced = 1;
 }
 
+/* Returns pointer to cmd's target's IN SG data buffer */
+static inline struct scatterlist *scst_cmd_get_in_tgt_sg(struct scst_cmd *cmd)
+{
+       return cmd->tgt_in_sg;
+}
+
+/* Returns cmd's target's IN sg_cnt */
+static inline int scst_cmd_get_tgt_in_sg_cnt(struct scst_cmd *cmd)
+{
+       return cmd->tgt_in_sg_cnt;
+}
+
+/* Sets cmd's target's IN SG data buffer */
+static inline void scst_cmd_set_tgt_in_sg(struct scst_cmd *cmd,
+       struct scatterlist *sg, int sg_cnt)
+{
+       WARN_ON(!cmd->tgt_data_buf_alloced);
+
+       cmd->tgt_in_sg = sg;
+       cmd->tgt_in_sg_cnt = sg_cnt;
+}
+
 /* Returns cmd's data direction */
 static inline scst_data_direction scst_cmd_get_data_direction(
        struct scst_cmd *cmd)
@@ -2443,6 +2498,12 @@ static inline int scst_cmd_get_expected_transfer_len(
        return cmd->expected_transfer_len;
 }
 
+static inline int scst_cmd_get_expected_in_transfer_len(
+       struct scst_cmd *cmd)
+{
+       return cmd->expected_in_transfer_len;
+}
+
 static inline void scst_cmd_set_expected(struct scst_cmd *cmd,
        scst_data_direction expected_data_direction,
        int expected_transfer_len)
@@ -2452,6 +2513,13 @@ static inline void scst_cmd_set_expected(struct scst_cmd *cmd,
        cmd->expected_values_set = 1;
 }
 
+static inline void scst_cmd_set_expected_in_transfer_len(struct scst_cmd *cmd,
+       int expected_in_transfer_len)
+{
+       WARN_ON(!cmd->expected_values_set);
+       cmd->expected_in_transfer_len = expected_in_transfer_len;
+}
+
 /*
  * Get/clear functions for cmd's may_need_dma_sync
  */
@@ -2623,15 +2691,15 @@ static inline void sg_clear(struct scatterlist *sg)
  *
  * The "put" function unmaps the buffer.
  */
-static inline int __scst_get_buf(struct scst_cmd *cmd, uint8_t **buf)
+static inline int __scst_get_buf(struct scst_cmd *cmd, struct scatterlist *sg,
+       int sg_cnt, uint8_t **buf)
 {
        int res = 0;
-       struct scatterlist *sg = cmd->sg;
        int i = cmd->get_sg_buf_entry_num;
 
        *buf = NULL;
 
-       if ((i >= cmd->sg_cnt) || unlikely(sg == NULL))
+       if ((i >= sg_cnt) || unlikely(sg == NULL))
                goto out;
 
        *buf = page_address(sg_page(&sg[i]));
@@ -2648,12 +2716,12 @@ static inline int scst_get_buf_first(struct scst_cmd *cmd, uint8_t **buf)
 {
        cmd->get_sg_buf_entry_num = 0;
        cmd->may_need_dma_sync = 1;
-       return __scst_get_buf(cmd, buf);
+       return __scst_get_buf(cmd, cmd->sg, cmd->sg_cnt, buf);
 }
 
 static inline int scst_get_buf_next(struct scst_cmd *cmd, uint8_t **buf)
 {
-       return __scst_get_buf(cmd, buf);
+       return __scst_get_buf(cmd, cmd->sg, cmd->sg_cnt, buf);
 }
 
 static inline void scst_put_buf(struct scst_cmd *cmd, void *buf)
@@ -2661,6 +2729,23 @@ static inline void scst_put_buf(struct scst_cmd *cmd, void *buf)
        /* Nothing to do */
 }
 
+static inline int scst_get_in_buf_first(struct scst_cmd *cmd, uint8_t **buf)
+{
+       cmd->get_sg_buf_entry_num = 0;
+       cmd->may_need_dma_sync = 1;
+       return __scst_get_buf(cmd, cmd->in_sg, cmd->in_sg_cnt, buf);
+}
+
+static inline int scst_get_in_buf_next(struct scst_cmd *cmd, uint8_t **buf)
+{
+       return __scst_get_buf(cmd, cmd->in_sg, cmd->in_sg_cnt, buf);
+}
+
+static inline void scst_put_in_buf(struct scst_cmd *cmd, void *buf)
+{
+       /* Nothing to do */
+}
+
 /*
  * Returns approximate higher rounded buffers count that
  * scst_get_buf_[first|next]() return.
@@ -2670,6 +2755,15 @@ static inline int scst_get_buf_count(struct scst_cmd *cmd)
        return (cmd->sg_cnt == 0) ? 1 : cmd->sg_cnt;
 }
 
+/*
+ * Returns approximate higher rounded buffers count that
+ * scst_get_in_buf_[first|next]() return.
+ */
+static inline int scst_get_in_buf_count(struct scst_cmd *cmd)
+{
+       return (cmd->in_sg_cnt == 0) ? 1 : cmd->in_sg_cnt;
+}
+
 /*
  * Suspends and resumes any activity.
  * Function scst_suspend_activity() doesn't return 0, until there are any
index aed2c2c..4410e27 100644 (file)
@@ -114,12 +114,14 @@ enum scst_cmd_queue_type {
 };
 
 /*************************************************************
- ** Data direction aliases
+ ** Data direction aliases. Changing it don't forget to change
+ ** scst_to_tgt_dma_dir as well!!
  *************************************************************/
-#define SCST_DATA_UNKNOWN                      0
-#define SCST_DATA_WRITE                                1
-#define SCST_DATA_READ                         2
-#define SCST_DATA_NONE                         3
+#define SCST_DATA_UNKNOWN              0
+#define SCST_DATA_WRITE                        1
+#define SCST_DATA_READ                 2
+#define SCST_DATA_BIDI                 (SCST_DATA_WRITE | SCST_DATA_READ)
+#define SCST_DATA_NONE                 4
 
 /*************************************************************
  ** Name of the "Default" security group
@@ -152,6 +154,7 @@ static inline int scst_is_ua_sense(const uint8_t *sense)
 #define scst_sense_invalid_opcode              ILLEGAL_REQUEST, 0x20, 0
 #define scst_sense_invalid_field_in_cdb                ILLEGAL_REQUEST, 0x24, 0
 #define scst_sense_invalid_field_in_parm_list  ILLEGAL_REQUEST, 0x26, 0
+#define scst_sense_parameter_value_invalid     ILLEGAL_REQUEST, 0x26, 2
 #define scst_sense_reset_UA                    UNIT_ATTENTION,  0x29, 0
 #define scst_sense_nexus_loss_UA               UNIT_ATTENTION,  0x29, 0x7
 #define scst_sense_saving_params_unsup         ILLEGAL_REQUEST, 0x39, 0
index acf487a..aabd2e4 100644 (file)
@@ -113,6 +113,7 @@ struct scst_user_scsi_cmd_parse {
 
        int32_t timeout;
        int32_t bufflen;
+       int32_t in_bufflen;
 
        uint8_t queue_type;
        uint8_t data_direction;
@@ -155,6 +156,9 @@ struct scst_user_scsi_cmd_exec {
        uint8_t partial;
        int32_t timeout;
 
+       aligned_u64 p_in_buf;
+       int32_t in_bufflen;
+
        uint32_t sn;
 
        uint32_t parent_cmd_h;
index a67f421..55cf904 100644 (file)
@@ -510,18 +510,32 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
        int res = 0;
        struct scst_cmd *cmd = ucmd->cmd;
        struct scst_user_dev *dev = ucmd->dev;
+       struct sgv_pool *pool;
        gfp_t gfp_mask;
        int flags = 0;
-       int bufflen = cmd->bufflen;
+       int bufflen, orig_bufflen;
        int last_len = 0;
+       int out_sg_pages = 0;
 
        TRACE_ENTRY();
 
-       sBUG_ON(bufflen == 0);
-
        gfp_mask = __GFP_NOWARN;
        gfp_mask |= (scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL);
 
+       if (cmd->data_direction != SCST_DATA_BIDI) {
+               orig_bufflen = cmd->bufflen;
+               pool = (struct sgv_pool *)cmd->tgt_dev->dh_priv;
+       } else {
+               /* Make in_sg->offset 0 */
+               int len = cmd->bufflen + ucmd->first_page_offset;
+               out_sg_pages = (len >> PAGE_SHIFT) + ((len & ~PAGE_MASK) != 0);
+               orig_bufflen = (out_sg_pages << PAGE_SHIFT) + cmd->in_bufflen;
+               pool = dev->pool;
+       }
+       bufflen = orig_bufflen;
+
+       EXTRACHECKS_BUG_ON(bufflen == 0);
+
        if (cached_buff) {
                flags |= SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL;
                if (ucmd->ubuff == 0)
@@ -534,18 +548,17 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
                        goto out;
                }
                bufflen += ucmd->first_page_offset;
-               if (is_need_offs_page(ucmd->ubuff, cmd->bufflen))
+               if (is_need_offs_page(ucmd->ubuff, orig_bufflen))
                        last_len = bufflen & ~PAGE_MASK;
                else
-                       last_len = cmd->bufflen & ~PAGE_MASK;
+                       last_len = orig_bufflen & ~PAGE_MASK;
                if (last_len == 0)
                        last_len = PAGE_SIZE;
        }
        ucmd->buff_cached = cached_buff;
 
-       cmd->sg = sgv_pool_alloc((struct sgv_pool *)cmd->tgt_dev->dh_priv,
-                       bufflen, gfp_mask, flags, &cmd->sg_cnt, &ucmd->sgv,
-                       &dev->udev_mem_lim, ucmd);
+       cmd->sg = sgv_pool_alloc(pool, bufflen, gfp_mask, flags, &cmd->sg_cnt,
+                       &ucmd->sgv, &dev->udev_mem_lim, ucmd);
        if (cmd->sg != NULL) {
                struct scst_user_cmd *buf_ucmd =
                        (struct scst_user_cmd *)sgv_get_priv(ucmd->sgv);
@@ -569,14 +582,21 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
                        "last seg len %d)", ucmd, cached_buff, ucmd->ubuff,
                        cmd->sg[cmd->sg_cnt-1].length);
 
+               if (cmd->data_direction == SCST_DATA_BIDI) {
+                       cmd->in_sg = &cmd->sg[out_sg_pages];
+                       cmd->in_sg_cnt = cmd->sg_cnt - out_sg_pages;
+                       cmd->sg_cnt = out_sg_pages;
+                       TRACE_MEM("cmd %p, in_sg %p, in_sg_cnt %d, sg_cnt %d",
+                               cmd, cmd->in_sg, cmd->in_sg_cnt, cmd->sg_cnt);
+               }
+
                if (unlikely(cmd->sg_cnt > cmd->tgt_dev->max_sg_cnt)) {
                        static int ll;
                        if (ll < 10) {
                                PRINT_INFO("Unable to complete command due to "
                                        "SG IO count limitation (requested %d, "
                                        "available %d, tgt lim %d)",
-                                       cmd->sg_cnt,
-                                       cmd->tgt_dev->max_sg_cnt,
+                                       cmd->sg_cnt, cmd->tgt_dev->max_sg_cnt,
                                        cmd->tgt->sg_tablesize);
                                ll++;
                        }
@@ -771,6 +791,7 @@ static int dev_user_parse(struct scst_cmd *cmd)
                ucmd->user_cmd.parse_cmd.ext_cdb_len = cmd->ext_cdb_len;
                ucmd->user_cmd.parse_cmd.timeout = cmd->timeout / HZ;
                ucmd->user_cmd.parse_cmd.bufflen = cmd->bufflen;
+               ucmd->user_cmd.parse_cmd.in_bufflen = cmd->in_bufflen;
                ucmd->user_cmd.parse_cmd.queue_type = cmd->queue_type;
                ucmd->user_cmd.parse_cmd.data_direction = cmd->data_direction;
                ucmd->user_cmd.parse_cmd.expected_values_set =
@@ -903,6 +924,9 @@ static int dev_user_exec(struct scst_cmd *cmd)
        ucmd->user_cmd.exec_cmd.data_direction = cmd->data_direction;
        ucmd->user_cmd.exec_cmd.partial = 0;
        ucmd->user_cmd.exec_cmd.timeout = cmd->timeout / HZ;
+       ucmd->user_cmd.exec_cmd.p_in_buf = ucmd->ubuff + 
+                                               (cmd->sg_cnt << PAGE_SHIFT);
+       ucmd->user_cmd.exec_cmd.in_bufflen = cmd->in_bufflen;
        ucmd->user_cmd.exec_cmd.sn = cmd->tgt_sn;
 
        ucmd->state = UCMD_STATE_EXECING;
index afe5bbc..d4e76a2 100644 (file)
@@ -156,23 +156,42 @@ EXPORT_SYMBOL(scst_set_cmd_error);
 void scst_set_sense(uint8_t *buffer, int len, bool d_sense,
        int key, int asc, int ascq)
 {
-       sBUG_ON(len < SCST_STANDARD_SENSE_LEN);
+       sBUG_ON(len == 0);
 
        memset(buffer, 0, len);
 
        if (d_sense) {
                /* Descriptor format */
-               buffer[0] = 0x72;       /* Response Code                */
-               buffer[1] = key;        /* Sense Key                    */
-               buffer[2] = asc;        /* ASC                          */
-               buffer[3] = ascq;       /* ASCQ                         */
+
+               if (len < 4) {
+                       PRINT_ERROR("Length %d of sense buffer too small to "
+                               "fit sense %x:%x:%x", len, key, asc, ascq);
+               }
+
+               buffer[0] = 0x72;               /* Response Code        */
+               if (len > 1)
+                       buffer[1] = key;        /* Sense Key            */
+               if (len > 2)
+                       buffer[2] = asc;        /* ASC                  */
+               if (len > 3)
+                       buffer[3] = ascq;       /* ASCQ                 */
        } else {
                /* Fixed format */
-               buffer[0] = 0x70;       /* Response Code                */
-               buffer[2] = key;        /* Sense Key                    */
-               buffer[7] = 0x0a;       /* Additional Sense Length      */
-               buffer[12] = asc;       /* ASC                          */
-               buffer[13] = ascq;      /* ASCQ                         */
+
+               if (len < 14) {
+                       PRINT_ERROR("Length %d of sense buffer too small to "
+                               "fit sense %x:%x:%x", len, key, asc, ascq);
+               }
+
+               buffer[0] = 0x70;               /* Response Code        */
+               if (len > 2)
+                       buffer[2] = key;        /* Sense Key            */
+               if (len > 7)
+                       buffer[7] = 0x0a;       /* Additional Sense Length */
+               if (len > 12)
+                       buffer[12] = asc;       /* ASC                  */
+               if (len > 13)
+                       buffer[13] = ascq;      /* ASCQ                 */
        }
 
        TRACE_BUFFER("Sense set", buffer, len);
@@ -652,6 +671,8 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd)
                break;
 
        default:
+               PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)",
+                       cmd->state, cmd, cmd->cdb[0]);
                sBUG();
        }
 
@@ -666,12 +687,11 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd)
 
 #ifdef CONFIG_SCST_EXTRACHECKS
        switch (cmd->state) {
-       case SCST_CMD_STATE_PRE_XMIT_RESP:
        case SCST_CMD_STATE_XMIT_RESP:
        case SCST_CMD_STATE_FINISHED:
        case SCST_CMD_STATE_FINISHED_INTERNAL:
        case SCST_CMD_STATE_XMIT_WAIT:
-               PRINT_CRIT_ERROR("Wrong cmd state %x (cmd %p, op %x)",
+               PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)",
                        cmd->state, cmd, cmd->cdb[0]);
                sBUG();
        }
@@ -2258,10 +2278,18 @@ void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir)
        TRACE_ENTRY();
 
        if (copy_dir == SCST_SG_COPY_FROM_TARGET) {
-               src_sg = cmd->tgt_sg;
-               src_sg_cnt = cmd->tgt_sg_cnt;
-               dst_sg = cmd->sg;
-               to_copy = cmd->bufflen;
+               if (cmd->data_direction != SCST_DATA_BIDI) {
+                       src_sg = cmd->tgt_sg;
+                       src_sg_cnt = cmd->tgt_sg_cnt;
+                       dst_sg = cmd->sg;
+                       to_copy = cmd->bufflen;
+               } else {
+                       TRACE_MEM("BIDI cmd %p", cmd);
+                       src_sg = cmd->tgt_in_sg;
+                       src_sg_cnt = cmd->tgt_in_sg_cnt;
+                       dst_sg = cmd->in_sg;
+                       to_copy = cmd->in_bufflen;
+               }
        } else {
                src_sg = cmd->sg;
                src_sg_cnt = cmd->sg_cnt;
@@ -3099,6 +3127,24 @@ static void scst_check_internal_sense(struct scst_device *dev, int result,
        return;
 }
 
+enum dma_data_direction scst_to_dma_dir(int scst_dir)
+{
+       static const enum dma_data_direction tr_tbl[] = { DMA_NONE,
+               DMA_TO_DEVICE, DMA_FROM_DEVICE, DMA_BIDIRECTIONAL, DMA_NONE };
+
+       return tr_tbl[scst_dir];
+}
+EXPORT_SYMBOL(scst_to_dma_dir);
+
+enum dma_data_direction scst_to_tgt_dma_dir(int scst_dir)
+{
+       static const enum dma_data_direction tr_tbl[] = { DMA_NONE,
+               DMA_FROM_DEVICE, DMA_TO_DEVICE, DMA_BIDIRECTIONAL, DMA_NONE };
+
+       return tr_tbl[scst_dir];
+}
+EXPORT_SYMBOL(scst_to_tgt_dma_dir);
+
 int scst_obtain_device_parameters(struct scst_device *dev)
 {
        int res = 0, i;
index 038fa3c..348c226 100644 (file)
@@ -1691,11 +1691,6 @@ static int __init init_scst(void)
                BUILD_BUG_ON(sizeof(c->sn) != sizeof(t->expected_sn));
        }
 
-       BUILD_BUG_ON(SCST_DATA_UNKNOWN != DMA_BIDIRECTIONAL);
-       BUILD_BUG_ON(SCST_DATA_WRITE != DMA_TO_DEVICE);
-       BUILD_BUG_ON(SCST_DATA_READ != DMA_FROM_DEVICE);
-       BUILD_BUG_ON(SCST_DATA_NONE != DMA_NONE);
-
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
 #if !defined(SCST_IO_CONTEXT)
        PRINT_WARNING("%s", "Patch io_context was not applied on "
index b03efc0..712df0a 100644 (file)
@@ -135,7 +135,7 @@ static int scst_init_cmd(struct scst_cmd *cmd, enum scst_exec_context *context)
             (*context == SCST_CONTEXT_DIRECT_ATOMIC) ||
             ((*context == SCST_CONTEXT_SAME) && scst_cmd_atomic(cmd))) &&
              scst_cmd_is_expected_set(cmd)) {
-               if (cmd->expected_data_direction == SCST_DATA_WRITE) {
+               if (cmd->expected_data_direction & SCST_DATA_WRITE) {
                        if (!test_bit(SCST_TGT_DEV_AFTER_INIT_WR_ATOMIC,
                                        &cmd->tgt_dev->tgt_dev_flags))
                                *context = SCST_CONTEXT_THREAD;
@@ -290,6 +290,9 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
        if (unlikely(rc < 0))
                goto out;
 
+       if (unlikely(cmd->status != SAM_STAT_GOOD))
+               scst_set_cmd_abnormal_done_state(cmd);
+
 active:
        /* Here cmd must not be in any cmd list, no locks */
        switch (pref_context) {
@@ -664,7 +667,7 @@ set_res:
        }
 
        if (cmd->resp_data_len == -1) {
-               if (cmd->data_direction == SCST_DATA_READ)
+               if (cmd->data_direction & SCST_DATA_READ)
                        cmd->resp_data_len = cmd->bufflen;
                else
                         cmd->resp_data_len = 0;
@@ -728,17 +731,15 @@ static int scst_prepare_space(struct scst_cmd *cmd)
 alloc:
        if (!cmd->tgt_data_buf_alloced && !cmd->dh_data_buf_alloced) {
                r = scst_alloc_space(cmd);
-               cmd->tgt_sg = cmd->sg;
-               cmd->tgt_sg_cnt = cmd->sg_cnt;
        } else if (cmd->dh_data_buf_alloced && !cmd->tgt_data_buf_alloced) {
                TRACE_MEM("dh_data_buf_alloced set (cmd %p)", cmd);
-               cmd->tgt_sg = cmd->sg;
-               cmd->tgt_sg_cnt = cmd->sg_cnt;
                r = 0;
        } else if (cmd->tgt_data_buf_alloced && !cmd->dh_data_buf_alloced) {
                TRACE_MEM("tgt_data_buf_alloced set (cmd %p)", cmd);
                cmd->sg = cmd->tgt_sg;
                cmd->sg_cnt = cmd->tgt_sg_cnt;
+               cmd->in_sg = cmd->tgt_in_sg;
+               cmd->in_sg_cnt = cmd->tgt_in_sg_cnt;
                r = 0;
        } else {
                TRACE_MEM("Both *_data_buf_alloced set (cmd %p, sg %p, "
@@ -778,15 +779,10 @@ prep_done:
 
        }
 
-       switch (cmd->data_direction) {
-       case SCST_DATA_WRITE:
+       if (cmd->data_direction & SCST_DATA_WRITE)
                cmd->state = SCST_CMD_STATE_RDY_TO_XFER;
-               break;
-
-       default:
+       else
                cmd->state = SCST_CMD_STATE_TGT_PRE_EXEC;
-               break;
-       }
 
 out:
        TRACE_EXIT_HRES(res);
@@ -830,14 +826,10 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status,
 
        switch (status) {
        case SCST_PREPROCESS_STATUS_SUCCESS:
-               switch (cmd->data_direction) {
-               case SCST_DATA_WRITE:
+               if (cmd->data_direction & SCST_DATA_WRITE)
                        cmd->state = SCST_CMD_STATE_RDY_TO_XFER;
-                       break;
-               default:
+               else
                        cmd->state = SCST_CMD_STATE_TGT_PRE_EXEC;
-                       break;
-               }
                if (cmd->set_sn_on_restart_cmd)
                        scst_cmd_set_sn(cmd);
                /* Small context optimization */
@@ -845,7 +837,7 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status,
                    (pref_context == SCST_CONTEXT_DIRECT_ATOMIC) ||
                    ((pref_context == SCST_CONTEXT_SAME) &&
                     scst_cmd_atomic(cmd))) {
-                       if (cmd->data_direction == SCST_DATA_WRITE) {
+                       if (cmd->data_direction & SCST_DATA_WRITE) {
                                if (!test_bit(SCST_TGT_DEV_AFTER_RESTART_WR_ATOMIC,
                                                &cmd->tgt_dev->tgt_dev_flags))
                                        pref_context = SCST_CONTEXT_THREAD;
@@ -865,8 +857,9 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status,
                set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags);
                /* go through */
        case SCST_PREPROCESS_STATUS_ERROR:
-               scst_set_cmd_error(cmd,
-                          SCST_LOAD_SENSE(scst_sense_hardw_error));
+               if (cmd->sense != NULL)
+                       scst_set_cmd_error(cmd,
+                               SCST_LOAD_SENSE(scst_sense_hardw_error));
                scst_set_cmd_abnormal_done_state(cmd);
                break;
 
@@ -1097,15 +1090,26 @@ void scst_rx_data(struct scst_cmd *cmd, int status,
        switch (status) {
        case SCST_RX_STATUS_SUCCESS:
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
-               if (cmd->tgt_sg) {
+               if (trace_flag & TRACE_RCV_BOT) {
                        int i;
-                       struct scatterlist *sg = cmd->tgt_sg;
-                       TRACE_RECV_BOT("RX data for cmd %p "
-                               "(sg_cnt %d, sg %p, sg[0].page %p)", cmd,
-                               cmd->tgt_sg_cnt, sg, (void *)sg_page(&sg[0]));
-                       for (i = 0; i < cmd->tgt_sg_cnt; ++i) {
-                               PRINT_BUFF_FLAG(TRACE_RCV_BOT, "RX sg",
-                                       sg_virt(&sg[i]), sg[i].length);
+                       struct scatterlist *sg;
+                       if (cmd->in_sg != NULL)
+                               sg = cmd->in_sg;
+                       else if (cmd->tgt_in_sg != NULL)
+                               sg = cmd->tgt_in_sg;
+                       else if (cmd->tgt_sg != NULL)
+                               sg = cmd->tgt_sg;
+                       else
+                               sg = cmd->sg;
+                       if (sg != NULL) {
+                               TRACE_RECV_BOT("RX data for cmd %p "
+                                       "(sg_cnt %d, sg %p, sg[0].page %p)",
+                                       cmd, cmd->tgt_sg_cnt, sg,
+                                       (void *)sg_page(&sg[0]));
+                               for (i = 0; i < cmd->tgt_sg_cnt; ++i) {
+                                       PRINT_BUFF_FLAG(TRACE_RCV_BOT, "RX sg",
+                                               sg_virt(&sg[i]), sg[i].length);
+                               }
                        }
                }
 #endif
@@ -1355,7 +1359,7 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state,
 
 #if defined(CONFIG_SCST_DEBUG)
        if (next_state == SCST_CMD_STATE_PRE_DEV_DONE) {
-               if (cmd->sg) {
+               if ((trace_flag & TRACE_RCV_TOP) && (cmd->sg != NULL)) {
                        int i;
                        struct scatterlist *sg = cmd->sg;
                        TRACE_RECV_TOP("Exec'd %d S/G(s) at %p sg[0].page at "
@@ -2140,7 +2144,7 @@ static int scst_exec(struct scst_cmd **active_cmd)
                cmd->state = SCST_CMD_STATE_LOCAL_EXEC;
 
                if (cmd->tgt_data_buf_alloced && cmd->dh_data_buf_alloced &&
-                   (cmd->data_direction == SCST_DATA_WRITE))
+                   (cmd->data_direction & SCST_DATA_WRITE))
                        scst_copy_sg(cmd, SCST_SG_COPY_FROM_TARGET);
 
                rc = scst_do_local_exec(cmd);
@@ -2774,7 +2778,7 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd)
                atomic_dec(&cmd->tgt_dev->tgt_dev_cmd_count);
                atomic_dec(&cmd->dev->dev_cmd_count);
                /* If expected values not set, expected direction is UNKNOWN */
-               if (cmd->expected_data_direction == SCST_DATA_WRITE)
+               if (cmd->expected_data_direction & SCST_DATA_WRITE)
                        atomic_dec(&cmd->dev->write_cmd_count);
 
                if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))
@@ -2816,7 +2820,7 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd)
        }
 
        if (cmd->tgt_data_buf_alloced && cmd->dh_data_buf_alloced &&
-           (cmd->data_direction == SCST_DATA_READ))
+           (cmd->data_direction & SCST_DATA_READ))
                scst_copy_sg(cmd, SCST_SG_COPY_TO_TARGET);
 
        cmd->state = SCST_CMD_STATE_XMIT_RESP;
@@ -2883,15 +2887,23 @@ static int scst_xmit_response(struct scst_cmd *cmd)
                TRACE_DBG("Calling xmit_response(%p)", cmd);
 
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
-               if (cmd->tgt_sg) {
+               if (trace_flag & TRACE_SND_BOT) {
                        int i;
-                       struct scatterlist *sg = cmd->tgt_sg;
-                       TRACE(TRACE_SND_BOT, "Xmitting data for cmd %p "
-                               "(sg_cnt %d, sg %p, sg[0].page %p)", cmd,
-                               cmd->tgt_sg_cnt, sg, (void *)sg_page(&sg[0]));
-                       for (i = 0; i < cmd->tgt_sg_cnt; ++i) {
-                               PRINT_BUFF_FLAG(TRACE_SND_BOT, "Xmitting sg",
-                                       sg_virt(&sg[i]), sg[i].length);
+                       struct scatterlist *sg;
+                       if (cmd->tgt_sg != NULL)
+                               sg = cmd->tgt_sg;
+                       else
+                               sg = cmd->sg;
+                       if (sg != NULL) {
+                               TRACE(TRACE_SND_BOT, "Xmitting data for cmd %p "
+                                       "(sg_cnt %d, sg %p, sg[0].page %p)",
+                                       cmd, cmd->tgt_sg_cnt, sg,
+                                       (void *)sg_page(&sg[0]));
+                               for (i = 0; i < cmd->tgt_sg_cnt; ++i) {
+                                       PRINT_BUFF_FLAG(TRACE_SND_BOT,
+                                               "Xmitting sg", sg_virt(&sg[i]),
+                                               sg[i].length);
+                               }
                        }
                }
 #endif
@@ -3235,7 +3247,7 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
                }
 
                /* If expected values not set, expected direction is UNKNOWN */
-               if (cmd->expected_data_direction == SCST_DATA_WRITE)
+               if (cmd->expected_data_direction & SCST_DATA_WRITE)
                        atomic_inc(&cmd->dev->write_cmd_count);
 
                if (unlikely(failure))
index 1254bda..200dd6d 100644 (file)
@@ -494,23 +494,32 @@ static int scst_local_queuecommand(struct scsi_cmnd *SCpnt,
        switch (SCpnt->sc_data_direction) {
        case DMA_TO_DEVICE:
                dir = SCST_DATA_WRITE;
+               scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
+               scst_cmd_set_tgt_sg(scst_cmd, scsi_sglist(SCpnt),
+                       scsi_sg_count(SCpnt));
                break;
        case DMA_FROM_DEVICE:
                dir = SCST_DATA_READ;
+               scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
+               scst_cmd_set_tgt_sg(scst_cmd, scsi_sglist(SCpnt),
+                       scsi_sg_count(SCpnt));
                break;
        case DMA_BIDIRECTIONAL:
-               printk(KERN_ERR "%s: DMA_BIDIRECTIONAL not allowed!\n",
-                      __func__);
-               return scst_local_send_resp(SCpnt, NULL, done,
-                                           DID_ERROR << 16);
-               /*dir = SCST_DATA_UNKNOWN;*/
+               dir = SCST_DATA_BIDI;
+               scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
+               scst_cmd_set_expected_in_transfer_len(scst_cmd,
+                       scsi_in(SCpnt)->length);
+               scst_cmd_set_tgt_sg(scst_cmd, scsi_sglist(SCpnt),
+                       scsi_sg_count(SCpnt));
+               scst_cmd_set_tgt_in_sg(scst_cmd, scsi_in(SCpnt)->table.sgl,
+                       scsi_in(SCpnt)->table.nents);
                break;
        case DMA_NONE:
        default:
                dir = SCST_DATA_NONE;
+               scst_cmd_set_expected(scst_cmd, dir, 0);
                break;
        }
-       scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
 
        /*
         * Defer allocating memory until all error paths are done
@@ -526,9 +535,6 @@ static int scst_local_queuecommand(struct scsi_cmnd *SCpnt,
 
        scst_cmd_set_tgt_priv(scst_cmd, tgt_specific);
 
-       /* Set the SGL things directly ... */
-       scst_cmd_set_tgt_sg(scst_cmd, scsi_sglist(SCpnt), scsi_sg_count(SCpnt));
-
 #ifdef CONFIG_SCST_LOCAL_FORCE_DIRECT_PROCESSING
        {
                struct Scsi_Host *h = SCpnt->device->host;