From 8c05a7f3505f294695ced22d06c35ba37471282b Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Fri, 4 Jun 2010 19:43:02 +0000 Subject: [PATCH] Residuals and write residuals counting fixes git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1742 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/iscsi.c | 212 ++++++++++++++++------------- iscsi-scst/kernel/iscsi.h | 1 - qla2x00t/qla2x00-target/qla2x00t.c | 68 +++++---- qla2x00t/qla2x00-target/qla2x00t.h | 1 + scst/src/scst_targ.c | 13 +- 5 files changed, 164 insertions(+), 131 deletions(-) diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 2d9d557ad..5e08ab9bf 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -71,7 +71,6 @@ static void iscsi_check_send_delayed_tm_resp(struct iscsi_session *sess); 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_set_resid(struct iscsi_cmnd *rsp, bool bufflen_set); static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags); static void req_del_from_write_timeout_list(struct iscsi_cmnd *req) @@ -720,6 +719,110 @@ 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) +{ + int 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; + 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); + } + } + 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; @@ -731,7 +834,7 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) TRACE_DBG("req %p", req); pdusize = req->conn->session->sess_params.max_xmit_data_length; - expsize = req->read_size; + expsize = scst_cmd_get_expected_transfer_len(req->scst_cmd); size = min(expsize, (u32)req->bufflen); offset = 0; sn = 0; @@ -754,8 +857,6 @@ 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) { - unsigned int scsisize; - TRACE_DBG("status %x", status); EXTRACHECKS_BUG_ON((cmnd_hdr(req)->flags & ISCSI_CMD_WRITE) != 0); @@ -763,16 +864,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; - 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); + iscsi_set_uni_resid(rsp, expsize, req->bufflen); } list_add_tail(&rsp->write_list_entry, &send); break; @@ -794,7 +886,8 @@ 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 bufflen_set) + int status, const u8 *sense_buf, int sense_len, + bool no_read_transfer, bool no_write_transfer) { struct iscsi_cmnd *req = rsp->parent_req; struct iscsi_scsi_rsp_hdr *rsp_hdr; @@ -829,14 +922,14 @@ static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp, rsp->bufflen = 0; } - iscsi_set_resid(rsp, bufflen_set); + 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 bufflen_set) + int status, const u8 *sense_buf, int sense_len, bool no_read_transfer) { struct iscsi_cmnd *rsp; @@ -845,7 +938,8 @@ 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, bufflen_set); + iscsi_init_status_rsp(rsp, status, sense_buf, sense_len, + no_read_transfer, false); TRACE_EXIT_HRES((unsigned long)rsp); return rsp; @@ -861,7 +955,7 @@ static struct iscsi_cmnd *create_prelim_status_rsp(struct iscsi_cmnd *req, rsp = iscsi_alloc_main_rsp(req); TRACE_DBG("main rsp %p", rsp); - iscsi_init_status_rsp(rsp, status, sense_buf, sense_len, false); + iscsi_init_status_rsp(rsp, status, sense_buf, sense_len, true, true); TRACE_EXIT_HRES((unsigned long)rsp); return rsp; @@ -914,7 +1008,6 @@ static int create_preliminary_status_rsp(struct iscsi_cmnd *req, int status, const u8 *sense_buf, int sense_len) { int res = 0; - struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); TRACE_ENTRY(); @@ -925,14 +1018,6 @@ static int create_preliminary_status_rsp(struct iscsi_cmnd *req, req->scst_state = ISCSI_CMD_STATE_OUT_OF_SCST_PRELIM_COMPL; - if ((req_hdr->flags & ISCSI_CMD_READ) && - (req_hdr->flags & ISCSI_CMD_WRITE)) { - int sz = cmnd_read_size(req); - if (sz > 0) - req->read_size = sz; - } else if (req_hdr->flags & ISCSI_CMD_READ) - req->read_size = be32_to_cpu(req_hdr->data_length); - create_prelim_status_rsp(req, status, sense_buf, sense_len); res = iscsi_preliminary_complete(req, req, true); @@ -1270,68 +1355,6 @@ out: return; } -static void iscsi_set_resid(struct iscsi_cmnd *rsp, bool bufflen_set) -{ - struct iscsi_cmnd *req = rsp->parent_req; - 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 (bufflen_set) { - 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 (bufflen_set) - 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; -} - int iscsi_preliminary_complete(struct iscsi_cmnd *req, struct iscsi_cmnd *orig_req, bool get_data) { @@ -1835,7 +1858,6 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) set_scst_preliminary_status_rsp(req, true, SCST_LOAD_SENSE(scst_sense_parameter_value_invalid)); } 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, @@ -1845,9 +1867,9 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) #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); + scst_cmd_set_expected(scst_cmd, dir, + 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 @@ -3193,13 +3215,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, true); + sense_len, false); iscsi_cmnd_init_write(rsp, 0); } } } else if (is_send_status) { struct iscsi_cmnd *rsp; - rsp = create_status_rsp(req, status, sense, sense_len, false); + rsp = create_status_rsp(req, status, sense, sense_len, true); 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 3cb20c4da..5db010320 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -412,7 +412,6 @@ struct iscsi_cmnd { struct scst_cmd *scst_cmd; struct scst_aen *scst_aen; }; - unsigned int read_size; struct iscsi_cmnd *main_rsp; }; diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index b3c3a97a4..7d97bcc6b 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -1862,35 +1862,41 @@ 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) { - int expected; - if (IS_FWI2_CAPABLE(ha)) - expected = be32_to_cpu(cmd-> - atio.atio7.fcp_cmnd.data_length); + 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 - expected = le32_to_cpu(cmd-> - atio.atio2x.data_length); - prm->residual = expected - - scst_cmd_get_resp_data_len(scst_cmd); - 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; + 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; + } } } @@ -2392,6 +2398,7 @@ static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd) { int res; struct q2t_cmd *cmd; + int exp_len; TRACE_ENTRY(); @@ -2399,6 +2406,9 @@ static int q2t_rdy_to_xfer(struct scst_cmd *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->data_direction = scst_cmd_get_data_direction(scst_cmd); @@ -2885,6 +2895,8 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, if (unlikely(status != CTIO_SUCCESS)) rx_status = SCST_RX_STATUS_ERROR; + else + cmd->write_data_transferred = 1; TRACE_DBG("Data received, context %x, rx_status %d", context, rx_status); diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index b1e8f6817..1a90db6cc 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -198,6 +198,7 @@ struct q2t_cmd { unsigned int conf_compl_supported:1;/* to save extra sess dereferences */ unsigned int free_sg:1; unsigned int aborted:1; /* Needed in case of SRR */ + unsigned int write_data_transferred:1; struct scatterlist *sg; /* cmd data buffer SG vector */ int sg_cnt; /* SG segments count */ diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 0a4480f92..78999e612 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -1368,14 +1368,13 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result, cmd->host_status = host_byte(result); cmd->driver_status = driver_byte(result); if (unlikely(resid != 0)) { -#ifdef CONFIG_SCST_EXTRACHECKS - if ((resid < 0) || (resid > cmd->resp_data_len)) { - PRINT_ERROR("Wrong resid %d (cmd->resp_data_len=%d, " - "op %x)", resid, cmd->resp_data_len, - cmd->cdb[0]); - } else -#endif + if ((cmd->data_direction & SCST_DATA_READ) && + (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 + * initiator's POV we already transferred all the data. + */ } if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) {