From 78edc5afb9d771b4b00f1c6bb3e0f5b223440633 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 23 Jun 2010 13:57:35 +0000 Subject: [PATCH] Various cleanups and fixes after some errors injection tests. Particularly: - ISCSI preliminary completed commands handling fixes - Write residuals cleanups and fixes - Interface to handle residuals and received from initiators data changed - BIDI cleanups and fixes. - All BIDI IN fields and functions renamed to OUT to be in line with the in-kernel convention + Docs updated git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1780 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- doc/scst_user_spec.txt | 16 +- fcst/ft_cmd.c | 12 +- fcst/ft_io.c | 10 +- iscsi-scst/README | 2 +- iscsi-scst/README_in-tree | 2 +- iscsi-scst/kernel/digest.c | 18 +- iscsi-scst/kernel/iscsi.c | 406 +++++++++++++++++------------ iscsi-scst/kernel/iscsi.h | 2 + iscsi-scst/kernel/nthread.c | 19 +- qla2x00t/qla2x00-target/qla2x00t.c | 73 ++---- scst/README | 67 +++-- scst/README_in-tree | 69 +++-- scst/include/scst.h | 180 +++++++++---- scst/include/scst_user.h | 7 +- scst/src/dev_handlers/scst_user.c | 24 +- scst/src/scst_lib.c | 389 ++++++++++++++++++++++----- scst/src/scst_priv.h | 17 +- scst/src/scst_targ.c | 155 ++++++++--- scst_local/scst_local.c | 6 +- 19 files changed, 999 insertions(+), 475 deletions(-) diff --git a/doc/scst_user_spec.txt b/doc/scst_user_spec.txt index 0ead6519a..333b265bb 100644 --- a/doc/scst_user_spec.txt +++ b/doc/scst_user_spec.txt @@ -373,7 +373,7 @@ struct scst_user_scsi_cmd_parse uint32_t timeout; int32_t bufflen; - int32_t in_bufflen; + int32_t out_bufflen; uint32_t op_flags; @@ -383,6 +383,7 @@ struct scst_user_scsi_cmd_parse uint8_t expected_values_set; uint8_t expected_data_direction; int32_t expected_transfer_len; + int32_t expected_out_transfer_len; uint32_t sn; }, @@ -402,7 +403,7 @@ where: - bufflen - command's buffer length - - in_bufflen - for bidirectional commands command's IN, i.e. from + - out_bufflen - for bidirectional commands command's OUT, i.e. from initiator to target, buffer length - op_flags - CDB flags, one or more scst_cdb_flags bits, see below. @@ -420,6 +421,9 @@ where: - expected_transfer_len - remote initiator supplied transfer length + - expected_out_transfer_len - remote initiator supplied out, i.e. from + initiator to target, transfer length for bidirectional commands. + - sn - command's SN, which might be used for task management Bits of scst_cdb_flags can be: @@ -594,8 +598,8 @@ struct scst_user_scsi_cmd_exec uint8_t partial; uint32_t timeout; - aligned_u64 p_in_buf; - int32_t in_bufflen; + aligned_u64 p_out_buf; + int32_t out_bufflen; uint32_t sn; @@ -641,11 +645,11 @@ where: - timeout - CDB execution timeout - - p_in_buf - for bidirectional commands pointer on command's IN, i.e. from + - p_out_buf - for bidirectional commands pointer on command's OUT, 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 + - out_bufflen - for bidirectional commands command's OUT, i.e. from initiator to target, buffer length - sn - command's SN, which might be used for task management diff --git a/fcst/ft_cmd.c b/fcst/ft_cmd.c index f21a601fd..b50a38cf8 100644 --- a/fcst/ft_cmd.c +++ b/fcst/ft_cmd.c @@ -57,13 +57,13 @@ void ft_cmd_dump(struct scst_cmd *cmd, const char *caller) prefix, cmd, fcmd->write_data_len, fcmd->read_data_len); printk(KERN_INFO "%s exp_dir %x exp_xfer_len %d exp_in_len %d\n", prefix, cmd->expected_data_direction, - cmd->expected_transfer_len, cmd->expected_in_transfer_len); - printk(KERN_INFO "%s dir %x data_len %d bufflen %d in_bufflen %d\n", + cmd->expected_transfer_len, cmd->expected_out_transfer_len); + printk(KERN_INFO "%s dir %x data_len %d bufflen %d out_bufflen %d\n", prefix, cmd->data_direction, cmd->data_len, - cmd->bufflen, cmd->in_bufflen); + cmd->bufflen, cmd->out_bufflen); printk(KERN_INFO "%s sg_cnt reg %d in %d tgt %d tgt_in %d\n", - prefix, cmd->sg_cnt, cmd->in_sg_cnt, - cmd->tgt_sg_cnt, cmd->tgt_in_sg_cnt); + prefix, cmd->sg_cnt, cmd->out_sg_cnt, + cmd->tgt_sg_cnt, cmd->tgt_out_sg_cnt); buf[0] = '\0'; if (cmd->sent_for_exec) @@ -247,7 +247,7 @@ int ft_send_response(struct scst_cmd *cmd) } if (dir == SCST_DATA_BIDI) { - bi_resid = (signed)scst_cmd_get_in_bufflen(cmd) - + bi_resid = (signed)scst_cmd_get_out_bufflen(cmd) - scst_cmd_get_resp_data_len(cmd); if (bi_resid) len += sizeof(__be32); diff --git a/fcst/ft_io.c b/fcst/ft_io.c index fe550aaa5..05fa2e8db 100644 --- a/fcst/ft_io.c +++ b/fcst/ft_io.c @@ -45,8 +45,8 @@ void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp) dir = scst_cmd_get_data_direction(cmd); if (dir == SCST_DATA_BIDI) { - mem_len = scst_get_in_buf_first(cmd, &buf); - bufflen = scst_cmd_get_in_bufflen(cmd); + mem_len = scst_get_out_buf_first(cmd, &buf); + bufflen = scst_cmd_get_out_bufflen(cmd); } else { mem_len = scst_get_buf_first(cmd, &buf); bufflen = scst_cmd_get_bufflen(cmd); @@ -77,8 +77,8 @@ void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp) while (frame_len) { if (!mem_len) { if (dir == SCST_DATA_BIDI) { - scst_put_in_buf(cmd, buf); - mem_len = scst_get_in_buf_next(cmd, &buf); + scst_put_out_buf(cmd, buf); + mem_len = scst_get_out_buf_next(cmd, &buf); } else { scst_put_buf(cmd, buf); mem_len = scst_get_buf_next(cmd, &buf); @@ -109,7 +109,7 @@ void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp) } if (mem_len) { if (dir == SCST_DATA_BIDI) - scst_put_in_buf(cmd, buf); + scst_put_out_buf(cmd, buf); else scst_put_buf(cmd, buf); } diff --git a/iscsi-scst/README b/iscsi-scst/README index 3075d6c55..49d7eface 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -753,7 +753,7 @@ prevent it. Performance advices ------------------- -1. If you use Windows XP or Windows 2003+ as initiators, you should +1. If you use Windows XP or Windows 2003+ as initiators, you can consider to decrease TcpAckFrequency parameter to 1. See http://support.microsoft.com/kb/328890/ or google for "TcpAckFrequency" for more details. diff --git a/iscsi-scst/README_in-tree b/iscsi-scst/README_in-tree index d92beb07e..e50da6738 100644 --- a/iscsi-scst/README_in-tree +++ b/iscsi-scst/README_in-tree @@ -648,7 +648,7 @@ prevent it. Performance advices ------------------- -1. If you use Windows XP or Windows 2003+ as initiators, you should +1. If you use Windows XP or Windows 2003+ as initiators, you can consider to decrease TcpAckFrequency parameter to 1. See http://support.microsoft.com/kb/328890/ or google for "TcpAckFrequency" for more details. diff --git a/iscsi-scst/kernel/digest.c b/iscsi-scst/kernel/digest.c index f9d338e91..4ab187a4d 100644 --- a/iscsi-scst/kernel/digest.c +++ b/iscsi-scst/kernel/digest.c @@ -184,8 +184,24 @@ int digest_rx_data(struct iscsi_cmnd *cmnd) if (unlikely(req->prelim_compl_flags != 0)) goto out; + /* + * Temporary to not crash with write residual overflows. ToDo. Until + * that let's always have succeeded data digests for such overflows. + * In ideal, we should allocate additional one or more sg's for the + * overflowed data and free them here or on req release. It's quite + * not trivial for such virtually never used case, so let's do it, + * when it gets needed. + */ + if (unlikely(offset + cmnd->pdu.datasize > req->bufflen)) { + PRINT_WARNING("Skipping RX data digest check for residual " + "overflow command op %x (data size %d, buffer size %d)", + cmnd_hdr(req)->scb[0], offset + cmnd->pdu.datasize, + req->bufflen); + goto out; + } + crc = digest_data(req, cmnd->pdu.datasize, offset, - cmnd->conn->rpadding); + cmnd->conn->rpadding); if (unlikely(crc != cmnd->ddigest)) { TRACE(TRACE_MINOR|TRACE_MGMT_DEBUG, "%s", "RX data digest " diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 3e9736311..e521b052c 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -72,6 +72,18 @@ static void req_cmnd_release(struct iscsi_cmnd *req); static int cmnd_insert_data_wait_hash(struct iscsi_cmnd *cmnd); static void __cmnd_abort(struct iscsi_cmnd *cmnd); static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags); +static void iscsi_set_resid_no_scst_cmd(struct iscsi_cmnd *rsp); +static void iscsi_set_resid(struct iscsi_cmnd *rsp); + +static void iscsi_set_not_received_data_len(struct iscsi_cmnd *req, + unsigned int not_received) +{ + req->not_received_data_len = not_received; + if (req->scst_cmd != NULL) + scst_cmd_set_write_not_received_data_len(req->scst_cmd, + not_received); + return; +} static void req_del_from_write_timeout_list(struct iscsi_cmnd *req) { @@ -162,7 +174,11 @@ void iscsi_restart_cmnd(struct iscsi_cmnd *cmnd) /* * Let's remove cmnd from the hash earlier to keep it smaller. - * See also corresponding comment in req_cmnd_release(). + * Also we have to remove hashed req from the hash before sending + * response. Otherwise we can have a race, when for some reason cmd's + * release (and, hence, removal from the hash) is delayed after the + * transmission and initiator sends cmd with the same ITT, hence + * the new command will be erroneously rejected as a duplicate. */ if (cmnd->hashed) cmnd_remove_data_wait_hash(cmnd); @@ -236,6 +252,8 @@ void iscsi_fail_data_waiting_cmnd(struct iscsi_cmnd *cmnd) * called from single read thread */ iscsi_extracheck_is_rd_thread(cmnd->conn); + + /* This cmnd is going to die without response */ cmnd->r2t_len_to_receive = 0; cmnd->r2t_len_to_send = 0; @@ -560,13 +578,6 @@ static void req_cmnd_release(struct iscsi_cmnd *req) sBUG_ON(req->parent_req != NULL); - /* - * We have to remove hashed req from the hash list before sending - * response. Otherwise we can have a race, when for some reason cmd's - * release (and, hence, removal from the hash) is delayed after the - * transmission and initiator sends cmd with the same ITT, hence - * the new command will be erroneously rejected as a duplicate. - */ if (unlikely(req->hashed)) { /* It sometimes can happen during errors recovery */ cmnd_remove_data_wait_hash(req); @@ -574,6 +585,12 @@ static void req_cmnd_release(struct iscsi_cmnd *req) if (unlikely(req->main_rsp != NULL)) { TRACE_DBG("Sending main rsp %p", req->main_rsp); + if (cmnd_opcode(req) == ISCSI_OP_SCSI_CMD) { + if (req->scst_cmd != NULL) + iscsi_set_resid(req->main_rsp); + else + iscsi_set_resid_no_scst_cmd(req->main_rsp); + } iscsi_cmnd_init_write(req->main_rsp, ISCSI_INIT_WRITE_WAKE); req->main_rsp = NULL; } @@ -719,123 +736,128 @@ static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags) return; } -static void iscsi_set_uni_resid(struct iscsi_cmnd *rsp, - int exp_transf_len, int transf_len) +static void iscsi_set_resid_no_scst_cmd(struct iscsi_cmnd *rsp) { - int resid; + struct iscsi_cmnd *req = rsp->parent_req; + struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); + struct iscsi_scsi_rsp_hdr *rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; + int resid, out_resid; - resid = exp_transf_len - transf_len; - if (unlikely(resid != 0)) { - struct iscsi_scsi_rsp_hdr *rsp_hdr = - (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; + TRACE_ENTRY(); + + sBUG_ON(req->scst_cmd != NULL); + + TRACE_DBG("req %p, rsp %p, outstanding_r2t %d, r2t_len_to_receive %d, " + "r2t_len_to_send %d, not_received_data_len %d", req, rsp, + req->outstanding_r2t, req->r2t_len_to_receive, + req->r2t_len_to_send, req->not_received_data_len); + + if ((req_hdr->flags & ISCSI_CMD_READ) && + (req_hdr->flags & ISCSI_CMD_WRITE)) { + out_resid = req->not_received_data_len; + if (out_resid > 0) { + rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; + rsp_hdr->residual_count = cpu_to_be32(out_resid); + } else if (out_resid < 0) { + out_resid = -out_resid; + rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW; + rsp_hdr->residual_count = cpu_to_be32(out_resid); + } + + resid = cmnd_read_size(req); + 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 (req_hdr->flags & ISCSI_CMD_READ) { + resid = be32_to_cpu(req_hdr->data_length); if (resid > 0) { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(resid); - } else /* if (resid < 0) */ { + } + } else if (req_hdr->flags & ISCSI_CMD_WRITE) { + resid = req->not_received_data_len; + if (resid > 0) { + rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; + rsp_hdr->residual_count = cpu_to_be32(resid); + } + } + + TRACE_EXIT(); + return; +} + +static void iscsi_set_resid(struct iscsi_cmnd *rsp) +{ + struct iscsi_cmnd *req = rsp->parent_req; + struct scst_cmd *scst_cmd = req->scst_cmd; + struct iscsi_scsi_cmd_hdr *req_hdr; + struct iscsi_scsi_rsp_hdr *rsp_hdr; + int resid, out_resid; + + TRACE_ENTRY(); + + if (likely(!scst_get_resid(scst_cmd, &resid, &out_resid))) { + TRACE_DBG("No residuals for req %p", req); + goto out; + } + + TRACE_DBG("req %p, resid %d, out_resid %d", req, resid, out_resid); + + req_hdr = cmnd_hdr(req); + rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; + + if ((req_hdr->flags & ISCSI_CMD_READ) && + (req_hdr->flags & ISCSI_CMD_WRITE)) { + if (out_resid > 0) { + rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; + rsp_hdr->residual_count = cpu_to_be32(out_resid); + } else if (out_resid < 0) { + out_resid = -out_resid; + rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW; + rsp_hdr->residual_count = cpu_to_be32(out_resid); + } + + 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 (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); } } + +out: + TRACE_EXIT(); return; } -static void iscsi_set_resid(struct iscsi_cmnd *rsp, bool no_read_transfer, - bool no_write_transfer) -{ - struct iscsi_cmnd *req = rsp->parent_req; - struct scst_cmd *scst_cmd = req->scst_cmd; - struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); - int resid, transf_len, exp_transf_len; - - if ((req_hdr->flags & ISCSI_CMD_READ) && - (req_hdr->flags & ISCSI_CMD_WRITE)) { - struct iscsi_scsi_rsp_hdr *rsp_hdr; - int in_transf_len, in_exp_transf_len; - - rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; - - if (likely(scst_cmd != NULL)) { - exp_transf_len = scst_cmd_get_expected_transfer_len(scst_cmd); - transf_len = scst_cmd_get_resp_data_len(scst_cmd); - in_exp_transf_len = scst_cmd_get_expected_in_transfer_len(scst_cmd); - in_transf_len = scst_cmd_get_in_bufflen(scst_cmd); - } else { - exp_transf_len = be32_to_cpu(req_hdr->data_length); - transf_len = 0; - in_exp_transf_len = cmnd_read_size(req); - if (in_exp_transf_len < 0) - in_exp_transf_len = 0; - in_transf_len = 0; - } - if (unlikely(no_read_transfer)) - transf_len = 0; - if (unlikely(no_write_transfer)) - in_transf_len = 0; - - resid = exp_transf_len - transf_len; - if (unlikely(resid != 0)) { - 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 = in_exp_transf_len - in_transf_len; - if (unlikely(resid != 0)) { - 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 (req_hdr->flags & ISCSI_CMD_READ) { - if (likely(scst_cmd != NULL)) { - exp_transf_len = scst_cmd_get_expected_transfer_len(scst_cmd); - transf_len = scst_cmd_get_resp_data_len(scst_cmd); - } else { - exp_transf_len = be32_to_cpu(req_hdr->data_length); - transf_len = 0; - } - if (unlikely(no_read_transfer)) - transf_len = 0; - iscsi_set_uni_resid(rsp, exp_transf_len, transf_len); - } else if (req_hdr->flags & ISCSI_CMD_WRITE) { - if (scst_cmd != NULL) { - exp_transf_len = scst_cmd_get_expected_transfer_len(scst_cmd); - transf_len = scst_cmd_get_bufflen(scst_cmd); - } else { - exp_transf_len = be32_to_cpu(req_hdr->data_length); - transf_len = 0; - } - if (unlikely(no_write_transfer)) - transf_len = 0; - iscsi_set_uni_resid(rsp, exp_transf_len, transf_len); - } - return; -} - - 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, size, offset, sn; + u32 pdusize, size, offset, sn; LIST_HEAD(send); TRACE_DBG("req %p", req); pdusize = req->conn->session->sess_params.max_xmit_data_length; - expsize = scst_cmd_get_expected_transfer_len(req->scst_cmd); - size = min(expsize, (u32)req->bufflen); + size = req->bufflen; offset = 0; sn = 0; @@ -864,7 +886,7 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) rsp_hdr->flags = ISCSI_FLG_FINAL | ISCSI_FLG_STATUS; rsp_hdr->cmd_status = status; - iscsi_set_uni_resid(rsp, expsize, req->bufflen); + iscsi_set_resid(rsp); } list_add_tail(&rsp->write_list_entry, &send); break; @@ -886,8 +908,7 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) } static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp, - int status, const u8 *sense_buf, int sense_len, - bool no_read_transfer, bool no_write_transfer) + int status, const u8 *sense_buf, int sense_len) { struct iscsi_cmnd *req = rsp->parent_req; struct iscsi_scsi_rsp_hdr *rsp_hdr; @@ -922,14 +943,12 @@ static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp, rsp->bufflen = 0; } - iscsi_set_resid(rsp, no_read_transfer, no_write_transfer); - TRACE_EXIT(); return; } static inline struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, - int status, const u8 *sense_buf, int sense_len, bool no_read_transfer) + int status, const u8 *sense_buf, int sense_len) { struct iscsi_cmnd *rsp; @@ -938,38 +957,34 @@ static inline struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, rsp = iscsi_alloc_rsp(req); TRACE_DBG("rsp %p", rsp); - iscsi_init_status_rsp(rsp, status, sense_buf, sense_len, - no_read_transfer, false); - - TRACE_EXIT_HRES((unsigned long)rsp); - return rsp; -} - -static struct iscsi_cmnd *create_prelim_status_rsp(struct iscsi_cmnd *req, - int status, const u8 *sense_buf, int sense_len) -{ - struct iscsi_cmnd *rsp; - - TRACE_ENTRY(); - - rsp = iscsi_alloc_main_rsp(req); - TRACE_DBG("main rsp %p", rsp); - - iscsi_init_status_rsp(rsp, status, sense_buf, sense_len, true, true); + iscsi_init_status_rsp(rsp, status, sense_buf, sense_len); + iscsi_set_resid(rsp); TRACE_EXIT_HRES((unsigned long)rsp); return rsp; } +/* + * Initializes data receive fields. Can be called only when they have not been + * initialized yet. + */ static int iscsi_set_prelim_r2t_len_to_receive(struct iscsi_cmnd *req) { - struct iscsi_hdr *req_hdr = &req->pdu.bhs; + struct iscsi_scsi_cmd_hdr *req_hdr = (struct iscsi_scsi_cmd_hdr *)&req->pdu.bhs; int res = 0; + unsigned int not_received; TRACE_ENTRY(); - if (req_hdr->flags & ISCSI_CMD_FINAL) + if (req_hdr->flags & ISCSI_CMD_FINAL) { + if (req_hdr->flags & ISCSI_CMD_WRITE) + iscsi_set_not_received_data_len(req, + be32_to_cpu(req_hdr->data_length) - + req->pdu.datasize); goto out; + } + + sBUG_ON(req->outstanding_r2t != 0); res = cmnd_insert_data_wait_hash(req); if (res != 0) { @@ -987,26 +1002,30 @@ static int iscsi_set_prelim_r2t_len_to_receive(struct iscsi_cmnd *req) * other code and pretend we need to receive 1 byte. * In data_out_start() we will correct it. */ - if (req->outstanding_r2t == 0) { - req->outstanding_r2t = 1; - req_add_to_write_timeout_list(req); - } + req->outstanding_r2t = 1; + req_add_to_write_timeout_list(req); req->r2t_len_to_receive = 1; req->r2t_len_to_send = 0; + not_received = be32_to_cpu(req_hdr->data_length) - req->pdu.datasize; + not_received -= min_t(unsigned int, not_received, + req->conn->session->sess_params.first_burst_length); + iscsi_set_not_received_data_len(req, not_received); + TRACE_DBG("req %p, op %x, outstanding_r2t %d, r2t_len_to_receive %d, " - "r2t_len_to_send %d", req, cmnd_opcode(req), - req->outstanding_r2t, req->r2t_len_to_receive, - req->r2t_len_to_send); + "r2t_len_to_send %d, not_received_data_len %d", req, + cmnd_opcode(req), req->outstanding_r2t, req->r2t_len_to_receive, + req->r2t_len_to_send, req->not_received_data_len); out: TRACE_EXIT_RES(res); return res; } -static int create_preliminary_status_rsp(struct iscsi_cmnd *req, +static int create_preliminary_no_scst_rsp(struct iscsi_cmnd *req, int status, const u8 *sense_buf, int sense_len) { + struct iscsi_cmnd *rsp; int res = 0; TRACE_ENTRY(); @@ -1018,9 +1037,17 @@ static int create_preliminary_status_rsp(struct iscsi_cmnd *req, req->scst_state = ISCSI_CMD_STATE_OUT_OF_SCST_PRELIM_COMPL; - create_prelim_status_rsp(req, status, sense_buf, sense_len); + sBUG_ON(req->scst_cmd != NULL); + res = iscsi_preliminary_complete(req, req, true); + rsp = iscsi_alloc_main_rsp(req); + TRACE_DBG("main rsp %p", rsp); + + iscsi_init_status_rsp(rsp, status, sense_buf, sense_len); + + /* Resid will be set in req_cmnd_release() */ + out: TRACE_EXIT_RES(res); return res; @@ -1070,9 +1097,17 @@ static int create_reject_rsp(struct iscsi_cmnd *req, int reason, bool get_data) * scst_cmd, so we will then send the REJECT. */ goto out; - } else + } else { + /* + * "In all the cases in which a pre-instantiated SCSI + * task is terminated because of the reject, the target + * MUST issue a proper SCSI command response with CHECK + * CONDITION as described in Section 10.4.3 Response" - + * RFC 3720. + */ set_scst_preliminary_status_rsp(req, get_data, SCST_LOAD_SENSE(scst_sense_invalid_message)); + } } rsp = iscsi_alloc_main_rsp(req); @@ -1237,7 +1272,10 @@ static int cmnd_insert_data_wait_hash(struct iscsi_cmnd *cmnd) u32 itt = cmnd->pdu.bhs.itt; if (unlikely(cmnd->hashed)) { - /* It can be for preliminary completed commands */ + /* + * It can be for preliminary completed commands, when this + * function already failed. + */ goto out; } @@ -1325,25 +1363,26 @@ static void cmnd_prepare_get_rejected_immed_data(struct iscsi_cmnd *cmnd) /* We already checked pdu.datasize in check_segment_length() */ - if (sg == NULL) { - /* - * There are no problems with the safety from concurrent - * accesses to dummy_page in dummy_sg, since data only - * will be read and then discarded. - */ - sg = cmnd->sg = &dummy_sg; + /* + * There are no problems with the safety from concurrent + * accesses to dummy_page in dummy_sg, since data only + * will be read and then discarded. + */ + sg = &dummy_sg; + if (cmnd->sg == NULL) { + /* just in case */ + cmnd->sg = sg; cmnd->bufflen = PAGE_SIZE; cmnd->own_sg = 1; } addr = (char __force __user *)(page_address(sg_page(&sg[0]))); - sBUG_ON(addr == NULL); conn->read_size = size; - for (i = 0; size > PAGE_SIZE; i++, size -= cmnd->bufflen) { + for (i = 0; size > PAGE_SIZE; i++, size -= PAGE_SIZE) { /* We already checked pdu.datasize in check_segment_length() */ sBUG_ON(i >= ISCSI_CONN_IOV_MAX); conn->read_iov[i].iov_base = addr; - conn->read_iov[i].iov_len = cmnd->bufflen; + conn->read_iov[i].iov_len = PAGE_SIZE; } conn->read_iov[i].iov_base = addr; conn->read_iov[i].iov_len = size; @@ -1360,6 +1399,7 @@ int iscsi_preliminary_complete(struct iscsi_cmnd *req, { int res = 0; bool set_r2t_len; + struct iscsi_hdr *orig_req_hdr = &orig_req->pdu.bhs; TRACE_ENTRY(); @@ -1383,19 +1423,40 @@ int iscsi_preliminary_complete(struct iscsi_cmnd *req, get_data = false; } - set_r2t_len = !req->hashed && - (cmnd_opcode(req) == ISCSI_OP_SCSI_CMD) && + /* + * We need to receive all outstanding PDUs, even if direction isn't + * WRITE. Test of PRELIM_COMPLETED is needed, because + * iscsi_set_prelim_r2t_len_to_receive() could also have failed before. + */ + set_r2t_len = !orig_req->hashed && + (cmnd_opcode(orig_req) == ISCSI_OP_SCSI_CMD) && !test_bit(ISCSI_CMD_PRELIM_COMPLETED, &orig_req->prelim_compl_flags); - set_bit(ISCSI_CMD_PRELIM_COMPLETED, &orig_req->prelim_compl_flags); TRACE_DBG("get_data %d, set_r2t_len %d", get_data, set_r2t_len); if (get_data) cmnd_prepare_get_rejected_immed_data(req); + if (test_bit(ISCSI_CMD_PRELIM_COMPLETED, &orig_req->prelim_compl_flags)) + goto out_set; + if (set_r2t_len) res = iscsi_set_prelim_r2t_len_to_receive(orig_req); + else if (orig_req_hdr->flags & ISCSI_CMD_WRITE) { + /* + * We will get here if orig_req prelim completed in the middle + * of data receiving. We won't send more R2T's, so + * r2t_len_to_send is final and won't be updated anymore in + * future. + */ + iscsi_set_not_received_data_len(orig_req, + orig_req->r2t_len_to_send); + } + +out_set: + set_bit(ISCSI_CMD_PRELIM_COMPLETED, &orig_req->prelim_compl_flags); + set_bit(ISCSI_CMD_PRELIM_COMPLETED, &req->prelim_compl_flags); TRACE_EXIT_RES(res); return res; @@ -1499,7 +1560,7 @@ static void send_r2t(struct iscsi_cmnd *req) /* * We don't need to check for PRELIM_COMPLETED here, because for such - * commands we set r2t_len_to_send = 0, hence made sure we won't + * commands we set r2t_len_to_send = 0, hence made sure we won't be * called here. */ @@ -1709,17 +1770,9 @@ int cmnd_rx_continue(struct iscsi_cmnd *req) /* For prelim completed commands sg & K can be already set! */ - 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 (dir & SCST_DATA_WRITE) { + req->bufflen = scst_cmd_get_write_fields(scst_cmd, &req->sg, + &req->sg_cnt); unsolicited_data_expected = !(req_hdr->flags & ISCSI_CMD_FINAL); if (unlikely(session->sess_params.initial_r2t && @@ -1789,10 +1842,14 @@ int cmnd_rx_continue(struct iscsi_cmnd *req) send_r2t(req); } } else { + 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 (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]); + "CDB %x)", cmnd_itt(req), req_hdr->scb[0]); set_scst_preliminary_status_rsp(req, true, SCST_LOAD_SENSE(iscsi_sense_unexpected_unsolicited_data)); } @@ -1839,7 +1896,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) (uint8_t *)&req_hdr->lun, sizeof(req_hdr->lun), req_hdr->scb, sizeof(req_hdr->scb), SCST_NON_ATOMIC); if (scst_cmd == NULL) { - res = create_preliminary_status_rsp(req, SAM_STAT_BUSY, + res = create_preliminary_no_scst_rsp(req, SAM_STAT_BUSY, NULL, 0); goto out; } @@ -1860,7 +1917,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) } else { dir = SCST_DATA_BIDI; scst_cmd_set_expected(scst_cmd, dir, sz); - scst_cmd_set_expected_in_transfer_len(scst_cmd, + scst_cmd_set_expected_out_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); @@ -2060,9 +2117,18 @@ static void data_out_end(struct iscsi_cmnd *cmnd) req->r2t_len_to_receive -= cmnd->pdu.datasize; if (unlikely(req->prelim_compl_flags != 0)) { + /* + * We need to call iscsi_preliminary_complete() again + * to handle the case if we just been aborted. This call must + * be done before zeroing r2t_len_to_send to correctly calc. + * residual. + */ + iscsi_preliminary_complete(cmnd, req, false); + /* * We might need to wait for one or more PDUs. Let's simplify - * other code. + * other code and not perform exact r2t_len_to_receive + * calculation. */ req->r2t_len_to_receive = req->outstanding_r2t; req->r2t_len_to_send = 0; @@ -3180,7 +3246,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd) req->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); } - req->bufflen = scst_cmd_get_resp_data_len(scst_cmd); + req->bufflen = scst_cmd_get_adjusted_resp_data_len(scst_cmd); req->scst_state = ISCSI_CMD_STATE_PROCESSED; @@ -3215,13 +3281,13 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd) send_data_rsp(req, 0, 0); if (is_send_status) { rsp = create_status_rsp(req, status, sense, - sense_len, false); + sense_len); iscsi_cmnd_init_write(rsp, 0); } } } else if (is_send_status) { struct iscsi_cmnd *rsp; - rsp = create_status_rsp(req, status, sense, sense_len, true); + rsp = create_status_rsp(req, status, sense, sense_len); iscsi_cmnd_init_write(rsp, 0); } #ifdef CONFIG_SCST_EXTRACHECKS diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 5db010320..628d18fb8 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -443,6 +443,8 @@ struct iscsi_cmnd { struct list_head cmd_list_entry; struct list_head nop_req_list_entry; + + unsigned int not_received_data_len; }; /* Max time to wait for our response satisfied for aborted commands */ diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index e94718bef..5de59215b 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -776,12 +776,23 @@ static int iscsi_rx_check_ddigest(struct iscsi_conn *conn) cmnd->ddigest_checked = 1; res = digest_rx_data(cmnd); if (unlikely(res != 0)) { - if (unlikely(cmnd->scst_cmd == NULL)) { + struct iscsi_cmnd *orig_req; + if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_DATA_OUT) + orig_req = cmnd->cmd_req; + else + orig_req = cmnd; + if (unlikely(orig_req->scst_cmd == NULL)) { /* Just drop it */ - iscsi_preliminary_complete(cmnd, cmnd, false); - } else - set_scst_preliminary_status_rsp(cmnd, false, + iscsi_preliminary_complete(cmnd, orig_req, false); + } else { + set_scst_preliminary_status_rsp(orig_req, false, SCST_LOAD_SENSE(iscsi_sense_crc_error)); + /* + * Let's prelim complete cmnd too to + * handle the DATA OUT case + */ + iscsi_preliminary_complete(cmnd, orig_req, false); + } res = 0; } } else if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_CMD) { diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index ac5970e6f..2bb5e396e 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -1862,41 +1862,23 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, full_req_cnt = prm->req_cnt; if (xmit_type & Q2T_XMIT_STATUS) { - if ((cmd->data_direction & SCST_DATA_READ) || - (cmd->data_direction & SCST_DATA_WRITE)) { - int expected = scst_cmd_get_expected_transfer_len(scst_cmd); - /* Bidirectional transfers not supported (yet) */ - if (cmd->data_direction & SCST_DATA_READ) - prm->residual = expected - - scst_cmd_get_resp_data_len(scst_cmd); - else if (likely(cmd->write_data_transferred)) - prm->residual = expected - - scst_cmd_get_bufflen(scst_cmd); - else - prm->residual = expected; - if (unlikely(prm->residual != 0)) { - if (prm->residual > 0) { - TRACE_DBG("Residual underflow: %d (tag " - "%lld, op %x, expected %d, " - "resp_data_len %d, bufflen %d, " - "rq_result %x)", prm->residual, - scst_cmd->tag, scst_cmd->cdb[0], - expected, - scst_cmd_get_resp_data_len(scst_cmd), - cmd->bufflen, prm->rq_result); - prm->rq_result |= SS_RESIDUAL_UNDER; - } else /* if (prm->residual < 0) */ { - TRACE_DBG("Residual overflow: %d (tag " - "%lld, op %x, expected %d, " - "resp_data_len %d, bufflen %d, " - "rq_result %x)", prm->residual, - scst_cmd->tag, scst_cmd->cdb[0], - expected, - scst_cmd_get_resp_data_len(scst_cmd), - cmd->bufflen, prm->rq_result); - prm->rq_result |= SS_RESIDUAL_OVER; - prm->residual = -prm->residual; - } + /* Bidirectional transfers not supported (yet) */ + if (unlikely(scst_get_resid(scst_cmd, &prm->residual, NULL))) { + if (prm->residual > 0) { + TRACE_DBG("Residual underflow: %d (tag %lld, " + "op %x, bufflen %d, rq_result %x)", + prm->residual, scst_cmd->tag, + scst_cmd->cdb[0], cmd->bufflen, + prm->rq_result); + prm->rq_result |= SS_RESIDUAL_UNDER; + } else if (prm->residual < 0) { + TRACE_DBG("Residual overflow: %d (tag %lld, " + "op %x, bufflen %d, rq_result %x)", + prm->residual, scst_cmd->tag, + scst_cmd->cdb[0], cmd->bufflen, + prm->rq_result); + prm->rq_result |= SS_RESIDUAL_OVER; + prm->residual = -prm->residual; } } @@ -2138,7 +2120,7 @@ static int q2x_xmit_response(struct scst_cmd *scst_cmd) if (is_send_status) xmit_type |= Q2T_XMIT_STATUS; - cmd->bufflen = scst_cmd_get_resp_data_len(scst_cmd); + cmd->bufflen = scst_cmd_get_adjusted_resp_data_len(scst_cmd); cmd->sg = scst_cmd_get_sg(scst_cmd); cmd->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); cmd->data_direction = scst_cmd_get_data_direction(scst_cmd); @@ -2398,19 +2380,14 @@ static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd) { int res; struct q2t_cmd *cmd; - int exp_len; TRACE_ENTRY(); TRACE(TRACE_SCSI, "tag=%lld", scst_cmd_get_tag(scst_cmd)); cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); - cmd->bufflen = scst_cmd_get_bufflen(scst_cmd); - exp_len = scst_cmd_get_expected_transfer_len(scst_cmd); - if (unlikely(cmd->bufflen > exp_len)) - cmd->bufflen = exp_len; - cmd->sg = scst_cmd_get_sg(scst_cmd); - cmd->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); + cmd->bufflen = scst_cmd_get_write_fields(scst_cmd, &cmd->sg, + &cmd->sg_cnt); cmd->data_direction = scst_cmd_get_data_direction(scst_cmd); cmd->dma_data_direction = scst_to_tgt_dma_dir(cmd->data_direction); @@ -3630,7 +3607,7 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, __q24_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: - cmd->bufflen = scst_cmd_get_resp_data_len(cmd->scst_cmd); + cmd->bufflen = scst_cmd_get_adjusted_resp_data_len(cmd->scst_cmd); if (q2t_has_data(cmd)) { uint32_t offset; int xmit_type; @@ -3653,7 +3630,8 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, } break; case SRR_IU_DATA_OUT: - cmd->bufflen = scst_cmd_get_bufflen(cmd->scst_cmd); + cmd->bufflen = scst_cmd_get_write_fields(cmd->scst_cmd, + &cmd->sg, &cmd->sg_cnt); if (q2t_has_data(cmd)) { uint32_t offset; int xmit_type; @@ -3730,7 +3708,7 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, __q2x_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: - cmd->bufflen = scst_cmd_get_resp_data_len(cmd->scst_cmd); + cmd->bufflen = scst_cmd_get_adjusted_resp_data_len(cmd->scst_cmd); if (q2t_has_data(cmd)) { uint32_t offset; int xmit_type; @@ -3753,7 +3731,8 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, } break; case SRR_IU_DATA_OUT: - cmd->bufflen = scst_cmd_get_bufflen(cmd->scst_cmd); + cmd->bufflen = scst_cmd_get_write_fields(cmd->scst_cmd, + &cmd->sg, &cmd->sg_cnt); if (q2t_has_data(cmd)) { uint32_t offset; int xmit_type; diff --git a/scst/README b/scst/README index 0f6773ab8..673e96e9b 100644 --- a/scst/README +++ b/scst/README @@ -1342,24 +1342,46 @@ correspondingly? Moreover, many HDDs lie about state of their cache and report write through while working in write back mode. They are also successfully used. -Also, at the moment we don't know any SDDs, which are acceptably fast, -i.e. faster than HDDs in average, with write back caching disabled. So -to have acceptable performance their users have to use write back -caching, hence on a power loss all not yet committed to flash chips, but -acknowledged as written, data will be lost. - 2. Most, if not all, modern enterprise level applications are well -prepared to work with write back cached storage. They know well when to -flush the cache and how to flush it to make the lost on crash data -acceptable. +prepared to work with write back cached storage. Particularly, all +transactions-based applications. Those applications flush cache to make +the lost on crash data event acceptable and recoverable. For instance, journaled file systems flush cache on each meta data update, so they survive power/hardware/software failures pretty well. -Note, Linux I/O subsystem guarantees this facility to work reliably only -using data protection barriers, which usually turned off by default (see -http://lwn.net/Articles/283161). Some info about barriers from the XFS -point of view could be found at +Summarizing, locally on initiators write back caching is always on. So, +if an application cares about its data consistency, it does flush the +cache when necessary or on any write, if open files with O_SYNC. If it +doesn't care, it doesn't flush the cache. As soon as the cache flushes +propagated to the storage, write back caching on it doesn't make any +difference. If application doesn't flush the cache, it's doomed to loose +data in case of a crash or power failure doesn't matter where this cache +located, locally or on the storage. + +For example, consider a user who wants to copy /src directory to /dst +directory reliably, i.e. after the copy finished no power failure or +crash could lead to the loss of data in /dst. There are 2 ways to +achieve this: + +1. Slow. Make the device behind /dst working in write through caching +mode and then run "cp -a /src /dst". + +2. Fast. Let the device behind /dst working in write back caching mode +and then run "cp -a /src /dst; sync". The reliability of the result is +the same, but it's much faster than (1). + +Note, Linux I/O subsystem guarantees to propagated cache flushes to the +storage only using data protection barriers, which usually turned off by +default (see http://lwn.net/Articles/283161). Without barriers enabled +Linux doesn't provide a guarantee that after sync()/fsync() all written +data really hit permanent storage. They can be stored in the cache of +your backstorage devices and, hence, lost on a power failure event. +Thus, ever with write-through cache mode, you still either need to +enable barriers on your backend file system on the target (for direct +/dev/sdX devices this is, indeed, impossible), or need a good UPS to +protect yourself from not committed data loss. Some info about barriers +from the XFS point of view could be found at http://oss.sgi.com/projects/xfs/faq.html#wcache. On Linux initiators for Ext3 and ReiserFS file systems the barrier protection could be turned on using "barrier=1" and "barrier=flush" mount options correspondingly. You @@ -1368,19 +1390,12 @@ Windows and, AFAIK, other UNIX'es don't need any special explicit options and do necessary barrier actions on write-back caching devices by default. -But even in case of journaled file systems your unsaved cached data will -still be lost in case of power/hardware/software failures, so you may -need to supply your target server with a good UPS with possibility to -gracefully shutdown your target on power shortage or disable write back -caching using WRITE_THROUGH flag. Note, that without barriers enabled -(i.e. by default) Linux doesn't provide a guarantee that after -sync()/fsync() all written data really hit permanent storage (see -above). They can be stored in the cache of your backstorage devices and, -hence, lost on a power failure event. Thus, ever with write-through -cache mode, you still either need to enable barriers on your backend -file system on the target (for direct /dev/sdX devices this is, indeed, -impossible), or need a good UPS to protect yourself from not committed -data loss. +But even in case of journaled file systems if you are using a not cache +flushing application, your unsaved cached data will still be lost in +case of power/hardware/software failures, so you may need to supply your +target server with a good UPS with possibility to gracefully shutdown +your target on power shortage or disable write back caching using +WRITE_THROUGH flag. Note, on some real-life workloads write through caching might perform better, than write back one with the barrier protection turned on. diff --git a/scst/README_in-tree b/scst/README_in-tree index 6a7fe957d..a75f1637b 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -228,7 +228,7 @@ your favorite kernel configuration Makefile target, e.g. "make xconfig": default. - CONFIG_SCST_MEASURE_LATENCY - if defined, provides in /sys/kernel/scst_tgt - and below statisctics about average commands processing latency. You + and below statistics about average commands processing latency. You can clear already measured results by writing 0 in the corresponding file. Note, you need a non-preemptible kernel to have correct results. @@ -925,24 +925,46 @@ correspondingly? Moreover, many HDDs lie about state of their cache and report write through while working in write back mode. They are also successfully used. -Also, at the moment we don't know any SDDs, which are acceptably fast, -i.e. faster than HDDs in average, with write back caching disabled. So -to have acceptable performance their users have to use write back -caching, hence on a power loss all not yet committed to flash chips, but -acknowledged as written, data will be lost. - 2. Most, if not all, modern enterprise level applications are well -prepared to work with write back cached storage. They know well when to -flush the cache and how to flush it to make the lost on crash data -acceptable. +prepared to work with write back cached storage. Particularly, all +transactions-based applications. Those applications flush cache to make +the lost on crash data event acceptable and recoverable. For instance, journaled file systems flush cache on each meta data update, so they survive power/hardware/software failures pretty well. -Note, Linux I/O subsystem guarantees this facility to work reliably only -using data protection barriers, which usually turned off by default (see -http://lwn.net/Articles/283161). Some info about barriers from the XFS -point of view could be found at +Summarizing, locally on initiators write back caching is always on. So, +if an application cares about its data consistency, it does flush the +cache when necessary or on any write, if open files with O_SYNC. If it +doesn't care, it doesn't flush the cache. As soon as the cache flushes +propagated to the storage, write back caching on it doesn't make any +difference. If application doesn't flush the cache, it's doomed to loose +data in case of a crash or power failure doesn't matter where this cache +located, locally or on the storage. + +For example, consider a user who wants to copy /src directory to /dst +directory reliably, i.e. after the copy finished no power failure or +crash could lead to the loss of data in /dst. There are 2 ways to +achieve this: + +1. Slow. Make the device behind /dst working in write through caching +mode and then run "cp -a /src /dst". + +2. Fast. Let the device behind /dst working in write back caching mode +and then run "cp -a /src /dst; sync". The reliability of the result is +the same, but it's much faster than (1). + +Note, Linux I/O subsystem guarantees to propagated cache flushes to the +storage only using data protection barriers, which usually turned off by +default (see http://lwn.net/Articles/283161). Without barriers enabled +Linux doesn't provide a guarantee that after sync()/fsync() all written +data really hit permanent storage. They can be stored in the cache of +your backstorage devices and, hence, lost on a power failure event. +Thus, ever with write-through cache mode, you still either need to +enable barriers on your backend file system on the target (for direct +/dev/sdX devices this is, indeed, impossible), or need a good UPS to +protect yourself from not committed data loss. Some info about barriers +from the XFS point of view could be found at http://oss.sgi.com/projects/xfs/faq.html#wcache. On Linux initiators for Ext3 and ReiserFS file systems the barrier protection could be turned on using "barrier=1" and "barrier=flush" mount options correspondingly. You @@ -951,19 +973,12 @@ Windows and, AFAIK, other UNIX'es don't need any special explicit options and do necessary barrier actions on write-back caching devices by default. -But even in case of journaled file systems your unsaved cached data will -still be lost in case of power/hardware/software failures, so you may -need to supply your target server with a good UPS with possibility to -gracefully shutdown your target on power shortage or disable write back -caching using WRITE_THROUGH flag. Note, that without barriers enabled -(i.e. by default) Linux doesn't provide a guarantee that after -sync()/fsync() all written data really hit permanent storage (see -above). They can be stored in the cache of your backstorage devices and, -hence, lost on a power failure event. Thus, ever with write-through -cache mode, you still either need to enable barriers on your backend -file system on the target (for direct /dev/sdX devices this is, indeed, -impossible), or need a good UPS to protect yourself from not committed -data loss. +But even in case of journaled file systems if you are using a not cache +flushing application, your unsaved cached data will still be lost in +case of power/hardware/software failures, so you may need to supply your +target server with a good UPS with possibility to gracefully shutdown +your target on power shortage or disable write back caching using +WRITE_THROUGH flag. Note, on some real-life workloads write through caching might perform better, than write back one with the barrier protection turned on. diff --git a/scst/include/scst.h b/scst/include/scst.h index ec952d7b7..f6b681e56 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -1620,7 +1620,7 @@ struct scst_cmd { unsigned int expected_values_set:1; /* - * Set if the SG buffer was modified by scst_set_resp_data_len() + * Set if the SG buffer was modified by scst_adjust_sg() */ unsigned int sg_buff_modified:1; @@ -1667,12 +1667,16 @@ struct scst_cmd { /* Set if tgt_sn field is valid */ unsigned int tgt_sn_set:1; + /* Set if any direction residual is possible */ + unsigned int resid_possible:1; + /* Set if cmd is done */ unsigned int done:1; /* Set if cmd is finished */ unsigned int finished:1; +#ifdef CONFIG_DEBUG_SG /* * Set if the cmd was delayed by task management debugging code. * Used only if CONFIG_SCST_DEBUG_TM is on. @@ -1684,6 +1688,7 @@ struct scst_cmd { * Used only if CONFIG_SCST_DEBUG_TM is on. */ unsigned int tm_dbg_immut:1; +#endif /**************************************************************/ @@ -1746,7 +1751,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 */ + int expected_out_transfer_len; /* for bidi writes */ /* * Cmd data length. Could be different from bufflen for commands like @@ -1765,19 +1770,39 @@ struct scst_cmd { int sg_cnt; /* SG segments count */ /* - * Response data length in data buffer. This field must not be set - * directly, use scst_set_resp_data_len() for that + * Response data length in data buffer. Must not be set + * directly, use scst_set_resp_data_len() for that. */ int resp_data_len; + /* + * Response data length adjusted on residual, i.e. + * min(expected_len, resp_len), if expected len set. + */ + int adjusted_resp_data_len; + + /* + * Data length to write, i.e. transfer from the initiator. Might be + * different from (out_)bufflen, if the initiator asked too big or too + * small expected(_out_)transfer_len. + */ + int write_len; + + /* + * Write sg and sg_cnt to point out either on sg/sg_cnt, or on + * out_sg/out_sg_cnt. + */ + struct scatterlist **write_sg; + int *write_sg_cnt; + /* 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 */ + int out_bufflen; /* WRITE buffer length */ + struct sgv_pool_obj *out_sgv; /* WRITE sgv object */ + struct scatterlist *out_sg; /* WRITE data buffer SG vector */ + int out_sg_cnt; /* WRITE SG segments count */ /* * Used if both target driver and dev handler request own memory @@ -1790,8 +1815,8 @@ struct scst_cmd { */ struct scatterlist *tgt_sg; int tgt_sg_cnt; - struct scatterlist *tgt_in_sg; /* bidirectional */ - int tgt_in_sg_cnt; /* bidirectional */ + struct scatterlist *tgt_out_sg; /* bidirectional */ + int tgt_out_sg_cnt; /* bidirectional */ /* * The status fields in case of errors must be set using @@ -1815,10 +1840,9 @@ struct scst_cmd { /* Used for storage of dev handler private stuff */ void *dh_priv; - /* - * Used to restore the SG vector if it was modified by - * scst_set_resp_data_len() - */ + /* Used to restore sg if it was modified by scst_adjust_sg() */ + struct scatterlist *orig_sg; + int *p_orig_sg_cnt; int orig_sg_cnt, orig_sg_entry, orig_entry_len; /* Used to retry commands in case of double UA */ @@ -2713,6 +2737,12 @@ static inline int scst_cmd_get_resp_data_len(struct scst_cmd *cmd) return cmd->resp_data_len; } +/* Returns cmd's adjusted response data length */ +static inline int scst_cmd_get_adjusted_resp_data_len(struct scst_cmd *cmd) +{ + return cmd->adjusted_resp_data_len; +} + /* Returns if status should be sent for cmd */ static inline int scst_cmd_get_is_send_status(struct scst_cmd *cmd) { @@ -2756,35 +2786,44 @@ static inline unsigned int scst_cmd_get_bufflen(struct scst_cmd *cmd) /* * Returns pointer to cmd's bidirectional in (WRITE) SG data buffer. * - * Usage of this function is not recommended, use scst_get_in_buf_*() + * Usage of this function is not recommended, use scst_get_out_buf_*() * family of functions instead. */ -static inline struct scatterlist *scst_cmd_get_in_sg(struct scst_cmd *cmd) +static inline struct scatterlist *scst_cmd_get_out_sg(struct scst_cmd *cmd) { - return cmd->in_sg; + return cmd->out_sg; } /* * Returns cmd's bidirectional in (WRITE) sg_cnt. * - * Usage of this function is not recommended, use scst_get_in_buf_*() + * Usage of this function is not recommended, use scst_get_out_buf_*() * family of functions instead. */ -static inline int scst_cmd_get_in_sg_cnt(struct scst_cmd *cmd) +static inline int scst_cmd_get_out_sg_cnt(struct scst_cmd *cmd) { - return cmd->in_sg_cnt; + return cmd->out_sg_cnt; +} + +void scst_restore_sg_buff(struct scst_cmd *cmd); + +/* Restores modified sg buffer in the original state, if necessary */ +static inline void scst_check_restore_sg_buff(struct scst_cmd *cmd) +{ + if (unlikely(cmd->sg_buff_modified)) + scst_restore_sg_buff(cmd); } /* * 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_*() + * this function is not recommended, use scst_get_out_buf_*() * family of functions instead. */ -static inline unsigned int scst_cmd_get_in_bufflen(struct scst_cmd *cmd) +static inline unsigned int scst_cmd_get_out_bufflen(struct scst_cmd *cmd) { - return cmd->in_bufflen; + return cmd->out_bufflen; } /* Returns pointer to cmd's target's SG data buffer */ @@ -2808,26 +2847,26 @@ 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) +/* Returns pointer to cmd's target's OUT SG data buffer */ +static inline struct scatterlist *scst_cmd_get_out_tgt_sg(struct scst_cmd *cmd) { - return cmd->tgt_in_sg; + return cmd->tgt_out_sg; } -/* Returns cmd's target's IN sg_cnt */ -static inline int scst_cmd_get_tgt_in_sg_cnt(struct scst_cmd *cmd) +/* Returns cmd's target's OUT sg_cnt */ +static inline int scst_cmd_get_tgt_out_sg_cnt(struct scst_cmd *cmd) { - return cmd->tgt_in_sg_cnt; + return cmd->tgt_out_sg_cnt; } -/* Sets cmd's target's IN SG data buffer */ -static inline void scst_cmd_set_tgt_in_sg(struct scst_cmd *cmd, +/* Sets cmd's target's OUT SG data buffer */ +static inline void scst_cmd_set_tgt_out_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; + cmd->tgt_out_sg = sg; + cmd->tgt_out_sg_cnt = sg_cnt; } /* Returns cmd's data direction */ @@ -2837,6 +2876,32 @@ static inline scst_data_direction scst_cmd_get_data_direction( return cmd->data_direction; } +/* Returns cmd's write len as well as write SG and sg_cnt */ +static inline int scst_cmd_get_write_fields(struct scst_cmd *cmd, + struct scatterlist **sg, int *sg_cnt) +{ + *sg = *cmd->write_sg; + *sg_cnt = *cmd->write_sg_cnt; + return cmd->write_len; +} + +void scst_cmd_set_write_not_received_data_len(struct scst_cmd *cmd, + int not_received); + +bool __scst_get_resid(struct scst_cmd *cmd, int *resid, int *bidi_out_resid); + +/* + * Returns true if cmd has residual(s) and returns them in the corresponding + * parameters(s). + */ +static inline bool scst_get_resid(struct scst_cmd *cmd, + int *resid, int *bidi_out_resid) +{ + if (likely(!cmd->resid_possible)) + return false; + return __scst_get_resid(cmd, resid, bidi_out_resid); +} + /* Returns cmd's status byte from host device */ static inline uint8_t scst_cmd_get_status(struct scst_cmd *cmd) { @@ -3022,10 +3087,10 @@ static inline int scst_cmd_get_expected_transfer_len( return cmd->expected_transfer_len; } -static inline int scst_cmd_get_expected_in_transfer_len( +static inline int scst_cmd_get_expected_out_transfer_len( struct scst_cmd *cmd) { - return cmd->expected_in_transfer_len; + return cmd->expected_out_transfer_len; } static inline void scst_cmd_set_expected(struct scst_cmd *cmd, @@ -3037,11 +3102,11 @@ 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) +static inline void scst_cmd_set_expected_out_transfer_len(struct scst_cmd *cmd, + int expected_out_transfer_len) { WARN_ON(!cmd->expected_values_set); - cmd->expected_in_transfer_len = expected_in_transfer_len; + cmd->expected_out_transfer_len = expected_out_transfer_len; } /* @@ -3273,19 +3338,39 @@ 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) +static inline int scst_get_out_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); + return __scst_get_buf(cmd, cmd->out_sg, cmd->out_sg_cnt, buf); } -static inline int scst_get_in_buf_next(struct scst_cmd *cmd, uint8_t **buf) +static inline int scst_get_out_buf_next(struct scst_cmd *cmd, uint8_t **buf) { - return __scst_get_buf(cmd, cmd->in_sg, cmd->in_sg_cnt, buf); + return __scst_get_buf(cmd, cmd->out_sg, cmd->out_sg_cnt, buf); } -static inline void scst_put_in_buf(struct scst_cmd *cmd, void *buf) +static inline void scst_put_out_buf(struct scst_cmd *cmd, void *buf) +{ + /* Nothing to do */ +} + +static inline int scst_get_sg_buf_first(struct scst_cmd *cmd, uint8_t **buf, + struct scatterlist *sg, int sg_cnt) +{ + cmd->get_sg_buf_entry_num = 0; + cmd->may_need_dma_sync = 1; + return __scst_get_buf(cmd, sg, sg_cnt, buf); +} + +static inline int scst_get_sg_buf_next(struct scst_cmd *cmd, uint8_t **buf, + struct scatterlist *sg, int sg_cnt) +{ + return __scst_get_buf(cmd, sg, sg_cnt, buf); +} + +static inline void scst_put_sg_buf(struct scst_cmd *cmd, void *buf, + struct scatterlist *sg, int sg_cnt) { /* Nothing to do */ } @@ -3301,11 +3386,11 @@ static inline int scst_get_buf_count(struct scst_cmd *cmd) /* * Returns approximate higher rounded buffers count that - * scst_get_in_buf_[first|next]() return. + * scst_get_out_buf_[first|next]() return. */ -static inline int scst_get_in_buf_count(struct scst_cmd *cmd) +static inline int scst_get_out_buf_count(struct scst_cmd *cmd) { - return (cmd->in_sg_cnt == 0) ? 1 : cmd->in_sg_cnt; + return (cmd->out_sg_cnt == 0) ? 1 : cmd->out_sg_cnt; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) && !defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19) @@ -3333,8 +3418,7 @@ void scst_post_alloc_data_buf(struct scst_cmd *cmd); int scst_check_local_events(struct scst_cmd *cmd); -int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd); -void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd); +int scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd); struct scst_trace_log { unsigned int val; diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index 4a5f298c9..8f725b656 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -119,7 +119,7 @@ struct scst_user_scsi_cmd_parse { int32_t timeout; int32_t bufflen; - int32_t in_bufflen; + int32_t out_bufflen; uint32_t op_flags; @@ -129,6 +129,7 @@ struct scst_user_scsi_cmd_parse { uint8_t expected_values_set; uint8_t expected_data_direction; int32_t expected_transfer_len; + int32_t expected_out_transfer_len; uint32_t sn; }; @@ -164,8 +165,8 @@ struct scst_user_scsi_cmd_exec { uint8_t partial; int32_t timeout; - aligned_u64 p_in_buf; - int32_t in_bufflen; + aligned_u64 p_out_buf; + int32_t out_bufflen; uint32_t sn; diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index a8a83f123..d79d859b9 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -552,10 +552,10 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff) orig_bufflen = cmd->bufflen; pool = (struct sgv_pool *)cmd->tgt_dev->dh_priv; } else { - /* Make in_sg->offset 0 */ + /* Make out_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; + orig_bufflen = (out_sg_pages << PAGE_SHIFT) + cmd->out_bufflen; pool = dev->pool; } bufflen = orig_bufflen; @@ -609,11 +609,11 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff) 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->out_sg = &cmd->sg[out_sg_pages]; + cmd->out_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); + TRACE_MEM("cmd %p, out_sg %p, out_sg_cnt %d, sg_cnt %d", + cmd, cmd->out_sg, cmd->out_sg_cnt, cmd->sg_cnt); } if (unlikely(cmd->sg_cnt > cmd->tgt_dev->max_sg_cnt)) { @@ -673,7 +673,7 @@ static int dev_user_alloc_space(struct scst_user_cmd *ucmd) goto out; else if (rc < 0) { scst_set_busy(cmd); - res = scst_get_cmd_abnormal_done_state(cmd); + res = scst_set_cmd_abnormal_done_state(cmd); goto out; } @@ -821,7 +821,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.out_bufflen = cmd->out_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 = @@ -830,6 +830,8 @@ static int dev_user_parse(struct scst_cmd *cmd) cmd->expected_data_direction; ucmd->user_cmd.parse_cmd.expected_transfer_len = cmd->expected_transfer_len; + ucmd->user_cmd.parse_cmd.expected_out_transfer_len = + cmd->expected_out_transfer_len; ucmd->user_cmd.parse_cmd.sn = cmd->tgt_sn; ucmd->user_cmd.parse_cmd.cdb_len = cmd->cdb_len; ucmd->user_cmd.parse_cmd.op_flags = cmd->op_flags; @@ -861,7 +863,7 @@ out_invalid: scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); out_error: - res = scst_get_cmd_abnormal_done_state(cmd); + res = scst_set_cmd_abnormal_done_state(cmd); goto out; } @@ -957,9 +959,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 + + ucmd->user_cmd.exec_cmd.p_out_buf = ucmd->ubuff + (cmd->sg_cnt << PAGE_SHIFT); - ucmd->user_cmd.exec_cmd.in_bufflen = cmd->in_bufflen; + ucmd->user_cmd.exec_cmd.out_bufflen = cmd->out_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 fd8329316..6193a255e 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -71,6 +71,8 @@ static int get_trans_len_2(struct scst_cmd *cmd, uint8_t off); static int get_trans_len_3(struct scst_cmd *cmd, uint8_t off); static int get_trans_len_4(struct scst_cmd *cmd, uint8_t off); +static int get_bidi_trans_len_2(struct scst_cmd *cmd, uint8_t off); + /* for special commands */ static int get_trans_len_block_limit(struct scst_cmd *cmd, uint8_t off); static int get_trans_len_read_capacity(struct scst_cmd *cmd, uint8_t off); @@ -442,6 +444,10 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none}, {0x52, " O ", "READ TRACK INFORMATION", SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2}, + {0x53, "O ", "XDWRITEREAD(10)", + SCST_DATA_READ|SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED| + SCST_WRITE_MEDIUM, + 7, get_bidi_trans_len_2}, {0x53, " O ", "RESERVE TRACK", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0x54, " O ", "SEND OPC INFORMATION", @@ -797,6 +803,7 @@ int scst_set_cmd_error_status(struct scst_cmd *cmd, int status) cmd->data_direction = SCST_DATA_NONE; cmd->resp_data_len = 0; + cmd->resid_possible = 1; cmd->is_send_status = 1; cmd->completed = 1; @@ -1792,13 +1799,7 @@ void scst_check_reassign_sessions(void) return; } -/** - * scst_get_cmd_abnormal_done_state() - get command's next abnormal done state - * - * Returns the next state of the SCSI target state machine in case if command's - * completed abnormally. - */ -int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) +static int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) { int res; @@ -1863,15 +1864,16 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) TRACE_EXIT_RES(res); return res; } -EXPORT_SYMBOL(scst_get_cmd_abnormal_done_state); /** * scst_set_cmd_abnormal_done_state() - set command's next abnormal done state * - * Sets state of the SCSI target state machine in case if command's completed - * abnormally. + * Sets state of the SCSI target state machine to abnormally complete command + * ASAP. + * + * Returns the new state. */ -void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) +int scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) { TRACE_ENTRY(); @@ -1889,6 +1891,36 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) cmd->state = scst_get_cmd_abnormal_done_state(cmd); + switch (cmd->state) { + case SCST_CMD_STATE_INIT_WAIT: + case SCST_CMD_STATE_INIT: + case SCST_CMD_STATE_PRE_PARSE: + case SCST_CMD_STATE_PREPROCESSING_DONE: + case SCST_CMD_STATE_PREPROCESSING_DONE_CALLED: + case SCST_CMD_STATE_PREPARE_SPACE: + case SCST_CMD_STATE_RDY_TO_XFER: + case SCST_CMD_STATE_DATA_WAIT: + cmd->write_len = 0; + cmd->resid_possible = 1; + break; + case SCST_CMD_STATE_TGT_PRE_EXEC: + case SCST_CMD_STATE_SEND_FOR_EXEC: + case SCST_CMD_STATE_LOCAL_EXEC: + case SCST_CMD_STATE_REAL_EXEC: + case SCST_CMD_STATE_REAL_EXECUTING: + case SCST_CMD_STATE_DEV_PARSE: + case SCST_CMD_STATE_DEV_DONE: + case SCST_CMD_STATE_PRE_DEV_DONE: + case SCST_CMD_STATE_MODE_SELECT_CHECKS: + case SCST_CMD_STATE_PRE_XMIT_RESP: + break; + default: + PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)", + cmd->state, cmd, cmd->cdb[0]); + sBUG(); + break; + } + #ifdef CONFIG_SCST_EXTRACHECKS if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) && (cmd->state != SCST_CMD_STATE_PREPROCESSING_DONE)) && @@ -1899,10 +1931,95 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) } #endif + TRACE_EXIT_RES(cmd->state); + return cmd->state; +} +EXPORT_SYMBOL(scst_set_cmd_abnormal_done_state); + +void scst_zero_write_rest(struct scst_cmd *cmd) +{ + int len, offs = 0; + uint8_t *buf; + + TRACE_ENTRY(); + + len = scst_get_sg_buf_first(cmd, &buf, *cmd->write_sg, + *cmd->write_sg_cnt); + while (len > 0) { + int cur_offs; + + if (offs + len <= cmd->write_len) + goto next; + else if (offs >= cmd->write_len) + cur_offs = 0; + else + cur_offs = cmd->write_len - offs; + + memset(&buf[cur_offs], 0, len - cur_offs); + +next: + offs += len; + scst_put_sg_buf(cmd, buf, *cmd->write_sg, *cmd->write_sg_cnt); + len = scst_get_sg_buf_next(cmd, &buf, *cmd->write_sg, + *cmd->write_sg_cnt); + } + TRACE_EXIT(); return; } -EXPORT_SYMBOL(scst_set_cmd_abnormal_done_state); + +static void scst_adjust_sg(struct scst_cmd *cmd, struct scatterlist *sg, + int *sg_cnt, int adjust_len) +{ + int i, l; + + TRACE_ENTRY(); + + l = 0; + for (i = 0; i < *sg_cnt; i++) { + l += sg[i].length; + if (l >= adjust_len) { + int left = adjust_len - (l - sg[i].length); +#ifdef CONFIG_SCST_DEBUG + TRACE(TRACE_SG_OP|TRACE_MEMORY, "cmd %p (tag %llu), " + "sg %p, sg_cnt %d, adjust_len %d, i %d, " + "sg[i].length %d, left %d", + cmd, (long long unsigned int)cmd->tag, + sg, *sg_cnt, adjust_len, i, + sg[i].length, left); +#endif + cmd->orig_sg = sg; + cmd->p_orig_sg_cnt = sg_cnt; + cmd->orig_sg_cnt = *sg_cnt; + cmd->orig_sg_entry = i; + cmd->orig_entry_len = sg[i].length; + *sg_cnt = (left > 0) ? i+1 : i; + sg[i].length = left; + cmd->sg_buff_modified = 1; + break; + } + } + + TRACE_EXIT(); + return; +} + +/** + * scst_restore_sg_buff() - restores modified sg buffer + * + * Restores modified sg buffer in the original state. + */ +void scst_restore_sg_buff(struct scst_cmd *cmd) +{ + TRACE_MEM("cmd %p, sg %p, orig_sg_entry %d, " + "orig_entry_len %d, orig_sg_cnt %d", cmd, cmd->orig_sg, + cmd->orig_sg_entry, cmd->orig_entry_len, + cmd->orig_sg_cnt); + cmd->orig_sg[cmd->orig_sg_entry].length = cmd->orig_entry_len; + *cmd->p_orig_sg_cnt = cmd->orig_sg_cnt; + cmd->sg_buff_modified = 0; +} +EXPORT_SYMBOL(scst_restore_sg_buff); /** * scst_set_resp_data_len() - set response data length @@ -1914,8 +2031,6 @@ EXPORT_SYMBOL(scst_set_cmd_abnormal_done_state); */ void scst_set_resp_data_len(struct scst_cmd *cmd, int resp_data_len) { - int i, l; - TRACE_ENTRY(); scst_check_restore_sg_buff(cmd); @@ -1924,28 +2039,9 @@ void scst_set_resp_data_len(struct scst_cmd *cmd, int resp_data_len) if (resp_data_len == cmd->bufflen) goto out; - l = 0; - for (i = 0; i < cmd->sg_cnt; i++) { - l += cmd->sg[i].length; - if (l >= resp_data_len) { - int left = resp_data_len - (l - cmd->sg[i].length); -#ifdef CONFIG_SCST_DEBUG - TRACE(TRACE_SG_OP|TRACE_MEMORY, "cmd %p (tag %llu), " - "resp_data_len %d, i %d, cmd->sg[i].length %d, " - "left %d", - cmd, (long long unsigned int)cmd->tag, - resp_data_len, i, - cmd->sg[i].length, left); -#endif - cmd->orig_sg_cnt = cmd->sg_cnt; - cmd->orig_sg_entry = i; - cmd->orig_entry_len = cmd->sg[i].length; - cmd->sg_cnt = (left > 0) ? i+1 : i; - cmd->sg[i].length = left; - cmd->sg_buff_modified = 1; - break; - } - } + scst_adjust_sg(cmd, cmd->sg, &cmd->sg_cnt, resp_data_len); + + cmd->resid_possible = 1; out: TRACE_EXIT(); @@ -1953,6 +2049,126 @@ out: } EXPORT_SYMBOL(scst_set_resp_data_len); +void scst_limit_sg_write_len(struct scst_cmd *cmd) +{ + TRACE_ENTRY(); + + TRACE_MEM("Limiting sg write len to %d (cmd %p, sg %p, sg_cnt %d)", + cmd->write_len, cmd, *cmd->write_sg, *cmd->write_sg_cnt); + + scst_check_restore_sg_buff(cmd); + scst_adjust_sg(cmd, *cmd->write_sg, cmd->write_sg_cnt, cmd->write_len); + + TRACE_EXIT(); + return; +} + +void scst_adjust_resp_data_len(struct scst_cmd *cmd) +{ + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(!cmd->expected_values_set); + + cmd->adjusted_resp_data_len = min(cmd->resp_data_len, + cmd->expected_transfer_len); + + if (cmd->adjusted_resp_data_len != cmd->resp_data_len) { + TRACE_MEM("Abjusting resp_data_len to %d (cmd %p, sg %p, " + "sg_cnt %d)", cmd->adjusted_resp_data_len, cmd, cmd->sg, + cmd->sg_cnt); + scst_check_restore_sg_buff(cmd); + scst_adjust_sg(cmd, cmd->sg, &cmd->sg_cnt, + cmd->adjusted_resp_data_len); + } + + TRACE_EXIT(); + return; +} + +/** + * scst_cmd_set_write_not_received_data_len() - sets cmd's not received len + * + * Sets cmd's not received data length. Also automatically sets resid_possible. + */ +void scst_cmd_set_write_not_received_data_len(struct scst_cmd *cmd, + int not_received) +{ + TRACE_ENTRY(); + + sBUG_ON(!cmd->expected_values_set); + + cmd->resid_possible = 1; + + if ((cmd->expected_data_direction & SCST_DATA_READ) && + (cmd->expected_data_direction & SCST_DATA_WRITE)) { + cmd->write_len = cmd->expected_out_transfer_len - not_received; + if (cmd->write_len == cmd->out_bufflen) + goto out; + } else if (cmd->expected_data_direction & SCST_DATA_WRITE) { + cmd->write_len = cmd->expected_transfer_len - not_received; + if (cmd->write_len == cmd->bufflen) + goto out; + } + + /* + * Write len now can be bigger cmd->(out_)bufflen, but that's OK, + * because it will be used to only calculate write residuals. + */ + + TRACE_DBG("cmd %p, not_received %d, write_len %d", cmd, not_received, + cmd->write_len); + + if (cmd->data_direction & SCST_DATA_WRITE) + scst_limit_sg_write_len(cmd); + +out: + TRACE_EXIT(); + return; +} +EXPORT_SYMBOL(scst_cmd_set_write_not_received_data_len); + +/** + * __scst_get_resid() - returns residuals for cmd + * + * Returns residuals for command. Must not be called directly, use + * scst_get_resid() instead. + */ +bool __scst_get_resid(struct scst_cmd *cmd, int *resid, int *bidi_out_resid) +{ + TRACE_ENTRY(); + + *resid = 0; + if (bidi_out_resid != NULL) + *bidi_out_resid = 0; + + sBUG_ON(!cmd->expected_values_set); + + if (cmd->expected_data_direction & SCST_DATA_READ) { + *resid = cmd->expected_transfer_len - cmd->resp_data_len; + if ((cmd->expected_data_direction & SCST_DATA_WRITE) && bidi_out_resid) { + if (cmd->write_len < cmd->expected_out_transfer_len) + *bidi_out_resid = cmd->expected_out_transfer_len - + cmd->write_len; + else + *bidi_out_resid = cmd->write_len - cmd->out_bufflen; + } + } else if (cmd->expected_data_direction & SCST_DATA_WRITE) { + if (cmd->write_len < cmd->expected_transfer_len) + *resid = cmd->expected_transfer_len - cmd->write_len; + else + *resid = cmd->write_len - cmd->bufflen; + } + + TRACE_DBG("cmd %p, resid %d, bidi_out_resid %d (resp_data_len %d, " + "expected_data_direction %d, write_len %d, bufflen %d)", cmd, + *resid, bidi_out_resid ? *bidi_out_resid : 0, cmd->resp_data_len, + cmd->expected_data_direction, cmd->write_len, cmd->bufflen); + + TRACE_EXIT_RES(1); + return true; +} +EXPORT_SYMBOL(__scst_get_resid); + /* No locks */ int scst_queue_retry_cmd(struct scst_cmd *cmd, int finished_cmds) { @@ -3890,6 +4106,8 @@ struct scst_cmd *scst_alloc_cmd(gfp_t gfp_mask) cmd->data_len = -1; cmd->is_send_status = 1; cmd->resp_data_len = -1; + cmd->write_sg = &cmd->sg; + cmd->write_sg_cnt = &cmd->sg_cnt; cmd->dbl_ua_orig_data_direction = SCST_DATA_UNKNOWN; cmd->dbl_ua_orig_resp_data_len = -1; @@ -4197,21 +4415,21 @@ int scst_alloc_space(struct scst_cmd *cmd) if (cmd->data_direction != SCST_DATA_BIDI) goto success; - cmd->in_sg = sgv_pool_alloc(tgt_dev->pool, cmd->in_bufflen, gfp_mask, - flags, &cmd->in_sg_cnt, &cmd->in_sgv, + cmd->out_sg = sgv_pool_alloc(tgt_dev->pool, cmd->out_bufflen, gfp_mask, + flags, &cmd->out_sg_cnt, &cmd->out_sgv, &cmd->dev->dev_mem_lim, NULL); - if (cmd->in_sg == NULL) + if (cmd->out_sg == NULL) goto out_sg_free; - if (unlikely(cmd->in_sg_cnt > tgt_dev->max_sg_cnt)) { + if (unlikely(cmd->out_sg_cnt > tgt_dev->max_sg_cnt)) { if ((ll < 10) || is_report_sg_limitation()) { PRINT_INFO("Unable to complete command due to " - "SG IO count limitation (IN buffer, requested " - "%d, available %d, tgt lim %d)", cmd->in_sg_cnt, + "SG IO count limitation (OUT buffer, requested " + "%d, available %d, tgt lim %d)", cmd->out_sg_cnt, tgt_dev->max_sg_cnt, cmd->tgt->sg_tablesize); ll++; } - goto out_in_sg_free; + goto out_out_sg_free; } success: @@ -4221,11 +4439,11 @@ out: TRACE_EXIT(); return res; -out_in_sg_free: - sgv_pool_free(cmd->in_sgv, &cmd->dev->dev_mem_lim); - cmd->in_sgv = NULL; - cmd->in_sg = NULL; - cmd->in_sg_cnt = 0; +out_out_sg_free: + sgv_pool_free(cmd->out_sgv, &cmd->dev->dev_mem_lim); + cmd->out_sgv = NULL; + cmd->out_sg = NULL; + cmd->out_sg_cnt = 0; out_sg_free: sgv_pool_free(cmd->sgv, &cmd->dev->dev_mem_lim); @@ -4255,12 +4473,12 @@ static void scst_release_space(struct scst_cmd *cmd) goto out; } - if (cmd->in_sgv != NULL) { - sgv_pool_free(cmd->in_sgv, &cmd->dev->dev_mem_lim); - cmd->in_sgv = NULL; - cmd->in_sg_cnt = 0; - cmd->in_sg = NULL; - cmd->in_bufflen = 0; + if (cmd->out_sgv != NULL) { + sgv_pool_free(cmd->out_sgv, &cmd->dev->dev_mem_lim); + cmd->out_sgv = NULL; + cmd->out_sg_cnt = 0; + cmd->out_sg = NULL; + cmd->out_bufflen = 0; } sgv_pool_free(cmd->sgv, &cmd->dev->dev_mem_lim); @@ -4482,20 +4700,21 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->cmd_flags |= REQ_QUIET; - if (cmd->sg != NULL) { - res = blk_rq_map_kern_sg(rq, cmd->sg, cmd->sg_cnt, gfp); - if (res) { - TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res); - goto out_free_rq; - } - } + if (cmd->sg == NULL) + goto done; if (cmd->data_direction == SCST_DATA_BIDI) { struct request *next_rq; if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) { res = -EOPNOTSUPP; - goto out_free_unmap; + goto out_free_rq; + } + + res = blk_rq_map_kern_sg(rq, cmd->out_sg, cmd->out_sg_cnt, gfp); + if (res != 0) { + TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res); + goto out_free_rq; } next_rq = blk_get_request(q, READ, gfp); @@ -4506,12 +4725,20 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, rq->next_rq = next_rq; next_rq->cmd_type = rq->cmd_type; - res = blk_rq_map_kern_sg(next_rq, cmd->in_sg, - cmd->in_sg_cnt, gfp); - if (res != 0) + res = blk_rq_map_kern_sg(next_rq, cmd->sg, cmd->sg_cnt, gfp); + if (res != 0) { + TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res); goto out_free_unmap; + } + } else { + res = blk_rq_map_kern_sg(rq, cmd->sg, cmd->sg_cnt, gfp); + if (res != 0) { + TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res); + goto out_free_rq; + } } +done: TRACE_DBG("sioc %p, cmd %p", sioc, cmd); sioc->data = cmd; @@ -4576,9 +4803,9 @@ void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir) to_copy = cmd->bufflen; } else { TRACE_MEM("BIDI cmd %p", cmd); - src_sg = cmd->tgt_in_sg; - dst_sg = cmd->in_sg; - to_copy = cmd->in_bufflen; + src_sg = cmd->tgt_out_sg; + dst_sg = cmd->out_sg; + to_copy = cmd->out_bufflen; } } else { src_sg = cmd->sg; @@ -4887,6 +5114,19 @@ static int get_trans_len_none(struct scst_cmd *cmd, uint8_t off) return 0; } +static int get_bidi_trans_len_2(struct scst_cmd *cmd, uint8_t off) +{ + const uint8_t *p = cmd->cdb + off; + + cmd->bufflen = 0; + cmd->bufflen |= ((u32)p[0]) << 8; + cmd->bufflen |= ((u32)p[1]); + + cmd->out_bufflen = cmd->bufflen; + + return 0; +} + /** * scst_get_cdb_info() - fill various info about the command's CDB * @@ -5159,6 +5399,7 @@ int scst_sbc_generic_parse(struct scst_cmd *cmd, * called, when there are existing commands. */ cmd->bufflen = cmd->bufflen << get_block_shift(cmd); + cmd->out_bufflen = cmd->out_bufflen << get_block_shift(cmd); } set_timeout: @@ -5215,8 +5456,10 @@ int scst_cdrom_generic_parse(struct scst_cmd *cmd, break; } - if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED) + if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED) { cmd->bufflen = cmd->bufflen << get_block_shift(cmd); + cmd->out_bufflen = cmd->out_bufflen << get_block_shift(cmd); + } set_timeout: if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0) @@ -5272,8 +5515,10 @@ int scst_modisk_generic_parse(struct scst_cmd *cmd, break; } - if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED) + if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED) { cmd->bufflen = cmd->bufflen << get_block_shift(cmd); + cmd->out_bufflen = cmd->out_bufflen << get_block_shift(cmd); + } set_timeout: if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0) @@ -5326,8 +5571,10 @@ int scst_tape_generic_parse(struct scst_cmd *cmd, } } - if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED & cmd->cdb[1]) + if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED & cmd->cdb[1]) { cmd->bufflen = cmd->bufflen * get_block_size(cmd); + cmd->out_bufflen = cmd->out_bufflen * get_block_size(cmd); + } if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0) cmd->timeout = SCST_GENERIC_TAPE_REG_TIMEOUT; diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index d79a85546..522b016b2 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -296,6 +296,10 @@ int scst_init_thread(void *arg); int scst_tm_thread(void *arg); int scst_global_mgmt_thread(void *arg); +void scst_zero_write_rest(struct scst_cmd *cmd); +void scst_limit_sg_write_len(struct scst_cmd *cmd); +void scst_adjust_resp_data_len(struct scst_cmd *cmd); + int scst_queue_retry_cmd(struct scst_cmd *cmd, int finished_cmds); static inline void scst_tgtt_cleanup(struct scst_tgt_template *tgtt) { } @@ -695,19 +699,6 @@ static inline void __scst_cmd_put(struct scst_cmd *cmd) extern void scst_throttle_cmd(struct scst_cmd *cmd); extern void scst_unthrottle_cmd(struct scst_cmd *cmd); -static inline void scst_check_restore_sg_buff(struct scst_cmd *cmd) -{ - if (cmd->sg_buff_modified) { - TRACE_MEM("cmd %p, sg %p, orig_sg_entry %d, " - "orig_entry_len %d, orig_sg_cnt %d", cmd, cmd->sg, - cmd->orig_sg_entry, cmd->orig_entry_len, - cmd->orig_sg_cnt); - cmd->sg[cmd->orig_sg_entry].length = cmd->orig_entry_len; - cmd->sg_cnt = cmd->orig_sg_cnt; - cmd->sg_buff_modified = 0; - } -} - #ifdef CONFIG_SCST_DEBUG_TM extern void tm_dbg_check_released_cmds(void); extern int tm_dbg_check_cmd(struct scst_cmd *cmd); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 78999e612..fe15462e1 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -445,12 +445,12 @@ static int scst_pre_parse(struct scst_cmd *cmd) cmd->state = SCST_CMD_STATE_DEV_PARSE; TRACE_DBG("op_name <%s> (cmd %p), direction=%d " - "(expected %d, set %s), transfer_len=%d (expected " - "len %d), flags=%d", cmd->op_name, cmd, + "(expected %d, set %s), bufflen=%d, out_bufflen=%d (expected " + "len %d, out expected len %d), flags=%d", cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", - cmd->bufflen, cmd->expected_transfer_len, - cmd->op_flags); + cmd->bufflen, cmd->out_bufflen, cmd->expected_transfer_len, + cmd->expected_out_transfer_len, cmd->op_flags); out: TRACE_EXIT_RES(res); @@ -478,7 +478,9 @@ static bool scst_is_allowed_to_mismatch_cmd(struct scst_cmd *cmd) switch (cmd->cdb[0]) { case TEST_UNIT_READY: /* Crazy VMware people sometimes do TUR with READ direction */ - res = true; + if ((cmd->expected_data_direction == SCST_DATA_READ) || + (cmd->expected_data_direction == SCST_DATA_NONE)) + res = true; break; } @@ -548,11 +550,13 @@ static int scst_parse_cmd(struct scst_cmd *cmd) #ifdef CONFIG_SCST_USE_EXPECTED_VALUES if (scst_cmd_is_expected_set(cmd)) { TRACE(TRACE_MINOR, "Using initiator supplied values: " - "direction %d, transfer_len %d", + "direction %d, transfer_len %d/%d", cmd->expected_data_direction, - cmd->expected_transfer_len); + cmd->expected_transfer_len, + cmd->expected_out_transfer_len); cmd->data_direction = cmd->expected_data_direction; cmd->bufflen = cmd->expected_transfer_len; + cmd->out_bufflen = cmd->expected_out_transfer_len; } else { PRINT_ERROR("Unknown opcode 0x%02x for %s and " "target %s not supplied expected values", @@ -581,12 +585,12 @@ static int scst_parse_cmd(struct scst_cmd *cmd) EXTRACHECKS_BUG_ON(cmd->cdb_len == 0); TRACE(TRACE_SCSI, "op_name <%s> (cmd %p), direction=%d " - "(expected %d, set %s), transfer_len=%d (expected " - "len %d), flags=%d", cmd->op_name, cmd, + "(expected %d, set %s), bufflen=%d, out_bufflen=%d, (expected " + "len %d, out expected len %d), flags=%x", cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", - cmd->bufflen, cmd->expected_transfer_len, - cmd->op_flags); + cmd->bufflen, cmd->out_bufflen, cmd->expected_transfer_len, + cmd->expected_out_transfer_len, cmd->op_flags); if (unlikely((cmd->op_flags & SCST_UNKNOWN_LENGTH) != 0)) { if (scst_cmd_is_expected_set(cmd)) { @@ -600,6 +604,9 @@ static int scst_parse_cmd(struct scst_cmd *cmd) */ cmd->bufflen = min(cmd->expected_transfer_len, 15*1024*1024); + if (cmd->data_direction == SCST_DATA_BIDI) + cmd->out_bufflen = min(cmd->expected_out_transfer_len, + 15*1024*1024); cmd->op_flags &= ~SCST_UNKNOWN_LENGTH; } else { PRINT_ERROR("Unknown data transfer length for opcode " @@ -653,22 +660,25 @@ static int scst_parse_cmd(struct scst_cmd *cmd) if (scst_cmd_is_expected_set(cmd)) { #ifdef CONFIG_SCST_USE_EXPECTED_VALUES -# ifdef CONFIG_SCST_EXTRACHECKS if (unlikely((cmd->data_direction != cmd->expected_data_direction) || - (cmd->bufflen != cmd->expected_transfer_len))) { + (cmd->bufflen != cmd->expected_transfer_len) || + (cmd->out_bufflen != cmd->expected_out_transfer_len))) { TRACE(TRACE_MINOR, "Expected values don't match " "decoded ones: data_direction %d, " "expected_data_direction %d, " - "bufflen %d, expected_transfer_len %d", + "bufflen %d, expected_transfer_len %d, " + "out_bufflen %d, expected_out_transfer_len %d", cmd->data_direction, cmd->expected_data_direction, - cmd->bufflen, cmd->expected_transfer_len); + cmd->bufflen, cmd->expected_transfer_len, + cmd->out_bufflen, cmd->expected_out_transfer_len); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); + cmd->data_direction = cmd->expected_data_direction; + cmd->bufflen = cmd->expected_transfer_len; + cmd->out_bufflen = cmd->expected_out_transfer_len; + cmd->resid_possible = 1; } -# endif - cmd->data_direction = cmd->expected_data_direction; - cmd->bufflen = cmd->expected_transfer_len; #else if (unlikely(cmd->data_direction != cmd->expected_data_direction)) { @@ -700,6 +710,23 @@ static int scst_parse_cmd(struct scst_cmd *cmd) cmd->bufflen); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); + if ((cmd->data_direction & SCST_DATA_READ) || + (cmd->data_direction & SCST_DATA_WRITE)) + cmd->resid_possible = 1; + } + if (unlikely(cmd->out_bufflen != cmd->expected_out_transfer_len)) { + TRACE(TRACE_MINOR, "Warning: expected bidirectional OUT " + "transfer length %d for opcode 0x%02x " + "(handler %s, target %s) doesn't match " + "decoded value %d. Faulty initiator " + "(e.g. VMware is known to be such) or " + "scst_scsi_op_table should be updated?", + cmd->expected_out_transfer_len, cmd->cdb[0], + dev->handler->name, cmd->tgtt->name, + cmd->out_bufflen); + PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", + cmd->cdb, cmd->cdb_len); + cmd->resid_possible = 1; } #endif } @@ -785,6 +812,45 @@ out_done: goto out; } +static void scst_set_write_len(struct scst_cmd *cmd) +{ + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(!(cmd->data_direction & SCST_DATA_WRITE)); + + if (cmd->data_direction & SCST_DATA_READ) { + cmd->write_len = cmd->out_bufflen; + cmd->write_sg = &cmd->out_sg; + cmd->write_sg_cnt = &cmd->out_sg_cnt; + } else { + cmd->write_len = cmd->bufflen; + /* write_sg and write_sg_cnt already initialized correctly */ + } + + TRACE_MEM("cmd %p, write_len %d, write_sg %p, write_sg_cnt %d, " + "resid_possible %d", cmd, cmd->write_len, *cmd->write_sg, + *cmd->write_sg_cnt, cmd->resid_possible); + + if (unlikely(cmd->resid_possible)) { + if (cmd->data_direction & SCST_DATA_READ) { + cmd->write_len = min(cmd->out_bufflen, + cmd->expected_out_transfer_len); + if (cmd->write_len == cmd->out_bufflen) + goto out; + } else { + cmd->write_len = min(cmd->bufflen, + cmd->expected_transfer_len); + if (cmd->write_len == cmd->bufflen) + goto out; + } + scst_limit_sg_write_len(cmd); + } + +out: + TRACE_EXIT(); + return; +} + static int scst_prepare_space(struct scst_cmd *cmd) { int r = 0, res = SCST_CMD_STATE_RES_CONT_SAME; @@ -887,8 +953,8 @@ alloc: 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; + cmd->out_sg = cmd->tgt_out_sg; + cmd->out_sg_cnt = cmd->tgt_out_sg_cnt; r = 0; } else { TRACE_MEM("Both *_data_buf_alloced set (cmd %p, sg %p, " @@ -909,11 +975,14 @@ check: } done: - if (cmd->preprocessing_only) + if (cmd->preprocessing_only) { cmd->state = SCST_CMD_STATE_PREPROCESSING_DONE; - else if (cmd->data_direction & SCST_DATA_WRITE) + if (cmd->data_direction & SCST_DATA_WRITE) + scst_set_write_len(cmd); + } else if (cmd->data_direction & SCST_DATA_WRITE) { cmd->state = SCST_CMD_STATE_RDY_TO_XFER; - else + scst_set_write_len(cmd); + } else cmd->state = SCST_CMD_STATE_TGT_PRE_EXEC; out: @@ -1253,10 +1322,10 @@ void scst_rx_data(struct scst_cmd *cmd, int status, if (trace_flag & TRACE_RCV_BOT) { int i; struct scatterlist *sg; - if (cmd->in_sg != NULL) - sg = cmd->in_sg; - else if (cmd->tgt_in_sg != NULL) - sg = cmd->tgt_in_sg; + if (cmd->out_sg != NULL) + sg = cmd->out_sg; + else if (cmd->tgt_out_sg != NULL) + sg = cmd->tgt_out_sg; else if (cmd->tgt_sg != NULL) sg = cmd->tgt_sg; else @@ -1321,6 +1390,23 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd) TRACE_ENTRY(); + if (unlikely(cmd->resid_possible)) { + if (cmd->data_direction & SCST_DATA_WRITE) { + bool do_zero = false; + if (cmd->data_direction & SCST_DATA_READ) { + if (cmd->write_len != cmd->out_bufflen) + do_zero = true; + } else { + if (cmd->write_len != cmd->bufflen) + do_zero = true; + } + if (do_zero) { + scst_check_restore_sg_buff(cmd); + scst_zero_write_rest(cmd); + } + } + } + cmd->state = SCST_CMD_STATE_SEND_FOR_EXEC; if ((cmd->tgtt->pre_exec == NULL) || unlikely(cmd->internal)) @@ -1372,7 +1458,7 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result, (resid > 0) && (resid < cmd->resp_data_len)) scst_set_resp_data_len(cmd, cmd->resp_data_len - resid); /* - * We ignore write direction residue, because from our + * We ignore write direction residue, because from the * initiator's POV we already transferred all the data. */ } @@ -1770,8 +1856,8 @@ static int scst_request_sense_local(struct scst_cmd *cmd) sl = tgt_dev->tgt_dev_valid_sense_len; else { sl = buffer_size; - PRINT_WARNING("%s: Being returned sense truncated to " - "size %d (needed %d)", cmd->op_name, + TRACE(TRACE_MINOR, "%s: Being returned sense truncated " + "to size %d (needed %d)", cmd->op_name, buffer_size, tgt_dev->tgt_dev_valid_sense_len); } memcpy(buffer, tgt_dev->tgt_dev_sense, sl); @@ -3338,6 +3424,11 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd) goto out; } + if (unlikely(cmd->resid_possible)) + scst_adjust_resp_data_len(cmd); + else + cmd->adjusted_resp_data_len = cmd->resp_data_len; + cmd->state = SCST_CMD_STATE_XMIT_RESP; res = SCST_CMD_STATE_RES_CONT_SAME; @@ -6569,7 +6660,7 @@ static struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess, * * Finds a command based on user supplied data and comparision * callback function, that should return true, if the command is found. - * Returns the command on success or NULL otherwise + * Returns the command on success or NULL otherwise. */ struct scst_cmd *scst_find_cmd(struct scst_session *sess, void *data, int (*cmp_fn) (struct scst_cmd *cmd, @@ -6616,7 +6707,7 @@ EXPORT_SYMBOL(scst_find_cmd); * * Finds a command based on the supplied tag comparing it with one * that previously set by scst_cmd_set_tag(). Returns the found command on - * success or NULL otherwise + * success or NULL otherwise. */ struct scst_cmd *scst_find_cmd_by_tag(struct scst_session *sess, uint64_t tag) diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 97f60bdeb..1e8868e96 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -580,11 +580,11 @@ static int scst_local_queuecommand(struct scsi_cmnd *SCpnt, /* Some of these symbols are only defined after 2.6.24 */ dir = SCST_DATA_BIDI; scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt)); - scst_cmd_set_expected_in_transfer_len(scst_cmd, + scst_cmd_set_expected_out_transfer_len(scst_cmd, scsi_in(SCpnt)->length); - scst_cmd_set_tgt_sg(scst_cmd, sgl, sgl_count); - scst_cmd_set_tgt_in_sg(scst_cmd, scsi_in(SCpnt)->table.sgl, + scst_cmd_set_tgt_sg(scst_cmd, scsi_in(SCpnt)->table.sgl, scsi_in(SCpnt)->table.nents); + scst_cmd_set_tgt_out_sg(scst_cmd, sgl, sgl_count); break; #endif case DMA_NONE: