mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-17 04:36:22 +00:00
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
This commit is contained in:
+10
-6
@@ -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
|
||||
|
||||
+6
-6
@@ -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);
|
||||
|
||||
+5
-5
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 "
|
||||
|
||||
+236
-170
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+41
-26
@@ -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.
|
||||
|
||||
+42
-27
@@ -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.
|
||||
|
||||
+132
-48
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+318
-71
@@ -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;
|
||||
|
||||
+4
-13
@@ -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);
|
||||
|
||||
+123
-32
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user