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:
Vladislav Bolkhovitin
2010-06-23 13:57:35 +00:00
parent e730fac09d
commit 78edc5afb9
19 changed files with 999 additions and 475 deletions
+10 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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.
+17 -1
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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 */
+15 -4
View File
@@ -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) {
+26 -47
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+4 -3
View File
@@ -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;
+13 -11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+3 -3
View File
@@ -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: