fcst: Handle frame send failures properly

Retry sending XFER_RDY, data and response frames if the network
driver reports that sending failed (-ENOMEM) instead of reporting
a kernel warning (WARN_ON(1)). If sending XFER_RDY or data frames
failed for another reason, report this to the initiator as a
write error (ASC = 03; ASCQ = 00 which stands for PERIPHERAL
DEVICE WRITE FAULT). If sending a response frame failed with
another error code than -ENOMEM, do not send a response.


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5607 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2014-06-14 17:18:34 +00:00
parent d937c0886d
commit 732ce5ca89
3 changed files with 68 additions and 19 deletions

View File

@@ -174,4 +174,26 @@ struct ft_tpg *ft_lport_find_tpg(struct fc_lport *);
struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *);
void ft_cmd_dump(struct scst_cmd *, const char *);
/* #define FCST_INJECT_SEND_ERRORS 2 */
#ifdef FCST_INJECT_SEND_ERRORS
#define FCST_INJ_SEND_ERR(e) \
({ \
int _error = 0; \
\
if (scst_random() % 62929 == 0) \
_error = -ENOMEM; \
if (FCST_INJECT_SEND_ERRORS >= 2 && scst_random() % 69491 == 0) \
_error = -ENXIO; \
if (_error) \
pr_warn("%s: injected seq_send() error %d\n", __func__, \
_error); \
else \
_error = (e); \
_error; \
})
#else
#define FCST_INJ_SEND_ERR(e) (e)
#endif
#endif /* __SCSI_FCST_H__ */

View File

@@ -291,6 +291,7 @@ int ft_send_response(struct scst_cmd *cmd)
struct fc_exch *ep;
unsigned int slen;
size_t len;
enum ft_cmd_state prev_state;
int resid = 0;
int bi_resid = 0;
int error;
@@ -303,7 +304,7 @@ int ft_send_response(struct scst_cmd *cmd)
lport = ep->lp;
WARN_ON(fcmd->state != FT_STATE_NEW && fcmd->state != FT_STATE_DATA_IN);
ft_set_cmd_state(fcmd, FT_STATE_CMD_RSP_SENT);
prev_state = ft_set_cmd_state(fcmd, FT_STATE_CMD_RSP_SENT);
if (scst_cmd_aborted_on_xmit(cmd)) {
FT_IO_DBG("cmd aborted did %x oxid %x\n", ep->did, ep->oxid);
@@ -313,7 +314,8 @@ int ft_send_response(struct scst_cmd *cmd)
if (!scst_cmd_get_is_send_status(cmd)) {
FT_IO_DBG("send status not set. feature not implemented\n");
return SCST_TGT_RES_FATAL_ERROR;
error = SCST_TGT_RES_FATAL_ERROR;
goto err;
}
status = scst_cmd_get_status(cmd);
@@ -333,7 +335,7 @@ int ft_send_response(struct scst_cmd *cmd)
error = ft_send_read_data(cmd);
if (error) {
FT_ERR("ft_send_read_data returned %d\n", error);
return error;
goto err;
}
if (dir == SCST_DATA_BIDI) {
@@ -347,8 +349,10 @@ int ft_send_response(struct scst_cmd *cmd)
}
fp = fc_frame_alloc(lport, len);
if (!fp)
return SCST_TGT_RES_QUEUE_FULL;
if (!fp) {
error = SCST_TGT_RES_QUEUE_FULL;
goto err;
}
fcp = fc_frame_payload_get(fp, len);
memset(fcp, 0, sizeof(*fcp));
@@ -384,13 +388,26 @@ int ft_send_response(struct scst_cmd *cmd)
fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
error = lport->tt.seq_send(lport, fcmd->seq, fp);
if (error < 0)
error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, fp));
if (error < 0) {
pr_err("Sending response for exchange with OX_ID %#x and RX_ID"
" %#x failed: %d\n", ep->oxid, ep->rxid, error);
error = error == -ENOMEM ? SCST_TGT_RES_QUEUE_FULL :
SCST_TGT_RES_FATAL_ERROR;
goto err;
}
done:
scst_tgt_cmd_done(cmd, SCST_CONTEXT_SAME);
return SCST_TGT_RES_SUCCESS;
err:
ft_set_cmd_state(fcmd, prev_state);
WARN_ONCE(error != SCST_TGT_RES_QUEUE_FULL &&
error != SCST_TGT_RES_FATAL_ERROR,
"%s: invalid error code %d\n",
__func__, error);
return error;
}
/*
@@ -451,6 +468,7 @@ int ft_send_xfer_rdy(struct scst_cmd *cmd)
struct fcp_txrdy *txrdy;
struct fc_lport *lport;
struct fc_exch *ep;
int error;
fcmd = scst_cmd_get_tgt_priv(cmd);
@@ -471,8 +489,17 @@ int ft_send_xfer_rdy(struct scst_cmd *cmd)
fcmd->seq = lport->tt.seq_start_next(fcmd->seq);
fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
lport->tt.seq_send(lport, fcmd->seq, fp);
return SCST_TGT_RES_SUCCESS;
error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, fp));
switch (error) {
case 0:
return SCST_TGT_RES_SUCCESS;
case -ENOMEM:
ft_set_cmd_state(fcmd, FT_STATE_NEW);
return SCST_TGT_RES_QUEUE_FULL;
default:
ft_set_cmd_state(fcmd, FT_STATE_NEW);
return SCST_TGT_RES_FATAL_ERROR;
}
}
/*
@@ -708,13 +735,6 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
scst_cmd_set_tgt_priv(cmd, fcmd);
cmd->state = FT_STATE_NEW;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
sp = fr_seq(fp);
#else
sp = lport->tt.seq_assign(lport, fp);
if (!sp)
goto busy;
#endif
fcmd->seq = sp;
lport->tt.seq_set_resp(sp, ft_recv_seq, cmd);

View File

@@ -175,10 +175,17 @@ int ft_send_read_data(struct scst_cmd *cmd)
remaining ? (FC_FC_EX_CTX | FC_FC_REL_OFF) :
(FC_FC_EX_CTX | FC_FC_REL_OFF | FC_FC_END_SEQ),
fh_off);
error = lport->tt.seq_send(lport, fcmd->seq, fp);
error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq,
fp));
if (error) {
WARN_ON(1);
/* XXX For now, initiator will retry */
pr_warn("Sending frame with oid %#x oxid %#x resp_len"
" %d failed at frame_off %u / remaining %zu"
" with error code %d - %s", ep->oid, ep->oxid,
scst_cmd_get_resp_data_len(cmd), frame_off,
remaining, error, error == -ENOMEM ?
"retrying" : "giving up");
return error == -ENOMEM ? SCST_TGT_RES_QUEUE_FULL :
SCST_TGT_RES_FATAL_ERROR;
} else
fcmd->read_data_len = frame_off;
}