From e62b39196bccddc4eeac8f7432779912e5157f6e Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Fri, 3 Apr 2009 17:49:26 +0000 Subject: [PATCH] Bidirectional data transfers added git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@744 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- doc/scst_user_spec.txt | 16 +- iscsi-scst/kernel/iscsi.c | 274 ++++++++++++++++-------------- iscsi-scst/kernel/iscsi.h | 4 +- scst/include/scst.h | 140 ++++++++++++--- scst/include/scst_const.h | 13 +- scst/include/scst_user.h | 4 + scst/src/dev_handlers/scst_user.c | 44 +++-- scst/src/scst_lib.c | 78 +++++++-- scst/src/scst_main.c | 5 - scst/src/scst_targ.c | 98 ++++++----- scst_local/scst_local.c | 24 ++- 11 files changed, 464 insertions(+), 236 deletions(-) diff --git a/doc/scst_user_spec.txt b/doc/scst_user_spec.txt index 97ea7a8ff..54c80965f 100644 --- a/doc/scst_user_spec.txt +++ b/doc/scst_user_spec.txt @@ -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 diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 57f50a25c..697e21228 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -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 (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)) - 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); - goto out; - } - } - - 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]); + 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, + scst_cmd_get_sense_buffer(scst_cmd), + scst_cmd_get_sense_buffer_len(scst_cmd)); + else + create_status_rsp(req, SAM_STAT_BUSY, NULL, 0); + cmnd_reject_scsi_cmd(req); + goto out; + } } + 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"); diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 4255d61c0..680efa652 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -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 */ diff --git a/scst/include/scst.h b/scst/include/scst.h index eca46de11..060f508d1 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -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 diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index aed2c2c9f..4410e277d 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -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 diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index acf487a77..aabd2e4e6 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -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; diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index a67f42134..55cf90462 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -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; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index afe5bbc9c..d4e76a21e 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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; diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 038fa3c5e..348c22638 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -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 " diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index b03efc044..712df0a8b 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -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)) diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 1254bda07..200dd6d46 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -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;