From Erik Habbinga:

This patch further fixes sense handling for the LSI driver in the non-packetized SCSI case:

- this version doesn't crash if a REQUEST SENSE command is sent.
- added a big comment explaining sense caching implementation.
- INQUIRY responses are inspected and tagged command queuing bits are cleared if LSI is a SCSI controller, per previous discussions.
- pending sense is cleared on a bus reset.



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@78 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2007-01-16 14:23:44 +00:00
parent 4b84541a21
commit c7b6d79352
2 changed files with 190 additions and 28 deletions
+184 -28
View File
@@ -269,6 +269,8 @@ static int mpt_target_detect(struct scst_tgt_template *temp1);
static int mpt_target_release(struct scst_tgt *scst_tgt);
static int stmapp_pending_sense(struct mpt_cmd *mpt_cmd);
static int mpt_xmit_response(struct scst_cmd *scst_cmd);
static void mpt_inquiry_no_tagged_commands(MPT_STM_PRIV *priv,
struct scst_cmd *scst_cmd);
static int mpt_rdy_to_xfer(struct scst_cmd *scst_cmd);
static void mpt_on_free_cmd(struct scst_cmd *scst_cmd);
static void mpt_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd);
@@ -793,16 +795,45 @@ stm_tgt_reply(MPT_ADAPTER *ioc, u32 reply_word)
}
/*
* don't go through SCST if we've sent cached sense.
* We're done sending the sense, so clear the pending sense flag
* figure out how we're handling cached sense data.
*/
if (IsScsi(priv) &&
(atomic_read(&priv->pending_sense[init_index]) == 1) &&
(scst_cmd->cdb[0] == REQUEST_SENSE)) {
TRACE_DBG("%s: clearing pending sense", ioc->name);
atomic_set(&priv->pending_sense[init_index], 0);
mpt_on_free_cmd(scst_cmd);
kfree(scst_cmd); /* created in stmapp_pending_sense */
if (IsScsi(priv)) {
switch (atomic_read(&priv->pending_sense[init_index])) {
/* attempt to send status and sense succeeded */
case MPT_STATUS_SENSE_ATTEMPT:
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
scst_tgt_cmd_done(scst_cmd);
break;
/* we tried to send status and sense
* simltaneously and failed. Prepare to handle
* the next command without SCST if it is
* REQUEST_SENSE */
case MPT_STATUS_SENSE_NOT_SENT:
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_HANDLE_RQ);
scst_tgt_cmd_done(scst_cmd);
break;
/* we've handled REQUEST_SENSE ourselves and
* we're done with the command. Clean up */
case MPT_STATUS_SENSE_HANDLE_RQ:
TRACE_DBG("%s: clearing pending sense",
ioc->name);
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
mpt_on_free_cmd(scst_cmd);
/* scst_cmd alloced in stmapp_pending_sense */
kfree(scst_cmd);
break;
default:
/* nothing much to do here, we aren't
* handling cached sense/status */
scst_tgt_cmd_done(scst_cmd);
break;
}
} else {
scst_tgt_cmd_done(scst_cmd);
}
@@ -1396,10 +1427,65 @@ mpt_send_target_data(struct mpt_prm *prm, int flags)
}
/*
* this function checks if any pending sense is available for
* a SCSI device. This sense wasn't able to be sent for the
* last command with a check condition, so it needs to either
* get sent for a REQUEST SENSE command or forgotten.
* this function checks if we need to handle REQUEST_SENSE on behalf of the
* target device. If the sense wasn't able to be sent simultaneously
* with the status for the last command with a check condition, it needs
* to either get sent for a REQUEST_SENSE command or forgotten.
*
* The pending_sense state and a buffer for holding sense is created for
* each possible initiator. The pending_sense state is used to tell if
* sending sense failed and to track the progress of the following
* REQUEST_SENSE command.
*
* There are four values for the pending_sense state:
* - STATUS_SENSE_IDLE: no caching of sense data is in progress
* - STATUS_SENSE_ATTEMPT: an attempt is being made to send status and
* sense in the same bus transaction.
* - STATUS_SENSE_NOT_SENT: the attempt to send simultanous status and
* sense failed.
* - STATUS_SENSE_HANDLE_RQ: the next command from the initiator needs
* to be handled by the LSI driver if it is REQUEST_SENSE using cached
* sense data, otherwise the cached sense data is ignored and the
* command is sent to SCST.
*
* In stmapp_pending_sense, if pending_sense state for an initiator ==
* SENSE_HANDLE_RQ, the incoming command is inspected. If it is
* REQUEST_SENSE, the command is handled by the LSI driver without
* involving SCST. The cached sense data from the immediately previous
* command is used. This sense data was not sent along with the status
* for that command (see below). If the command is not REQUEST_SENSE,
* the cached sense data is discarded and the command is sent to SCST
* for processing.
*
* In stm_send_target_status, sense data about to be sent is saved in a
* buffer and the pending_sense state for that initiator is set to
* MPT_STATUS_SENSE_ATTEMPT. The sense and status are sent to the LSI
* hardware.
*
* If the LSI hardware determines that the sense and status could not be
* sent in one operation, stm_reply is called with state == STATUS_SENT
* and with IOCStatus of MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x6B).
* This condition only happens in the non-packetized SCSI operating mode.
* When this happens, the pending_sense state is advanced to
* MPT_STATUS_SENSE_NOT_SENT.
*
* In stm_tgt_reply, if io_state == STATUS_SENT:
* - if pending_sense state == SENSE_ATTEMPT, the status and sense attempt was
* successful, so the pending_sense state is set to IDLE and the command
* is sent to SCST for completion.
* - if pending_sense state == SENSE_NOT_SENT, the status and sense attempt
* failed. The pending_sense state is advanced to SENSE_HANDLE_RQ and the
* current command is sent to SCST for completion. The next command from
* this initiator will enter stmapp_pending_sense as described above.
* - if pending_sense state == SENSE_HANDLE_RQ, the REQUEST_SENSE command
* handled alone by the LSI driver is done and resources need to be
* released. pending_sense state is set to IDLE.
* - if pending_sense state == SENSE_IDLE, a normal SCST command is done
* and is sent to SCST for completion.
*
* Because of this caching sense behaviour, we think we need to turn off
* tagged command queueing. The mpt_inquiry_no_tagged_commands function
* does this by modifying INQUIRY data before sending it over the wire.
*/
static int
stmapp_pending_sense(struct mpt_cmd *mpt_cmd)
@@ -1417,15 +1503,12 @@ stmapp_pending_sense(struct mpt_cmd *mpt_cmd)
TRACE_ENTRY();
/*
* check for pending sense if we're a SCSI controller, and
* send it back in response to REQUEST SENSE or clear it
*/
priv = mpt_cmd->priv;
if (IsScsi(priv)) {
index = GET_IO_INDEX(mpt_cmd->reply_word);
init_index = GET_INITIATOR_INDEX(mpt_cmd->reply_word);
if (atomic_read(&priv->pending_sense[init_index]) != 0) {
if (atomic_read(&priv->pending_sense[init_index]) ==
MPT_STATUS_SENSE_HANDLE_RQ) {
cmd = &priv->hw->cmd_buf[index];
scsi_cmd = (SCSI_CMD *)cmd->cmd;
cdb = scsi_cmd->CDB;
@@ -1477,12 +1560,25 @@ stmapp_pending_sense(struct mpt_cmd *mpt_cmd)
mpt_send_target_data(&prm, flags);
res = 1;
} else {
/* we couldn't create a scst_cmd, so
* we can't do anything. Send the
* command to SCST. */
atomic_set(&priv->pending_sense[init_index],
0);
MPT_STATUS_SENSE_IDLE);
}
} else {
atomic_set(&priv->pending_sense[init_index], 0);
/* next command immediately after check
* condition is not REQUEST_SENSE, so we can
* discard the cached sense and send the
* command to SCST. */
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
}
} else {
/* we don't need to perform REQUEST_SENSE and can
* send the command to SCST */
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
}
}
@@ -1595,6 +1691,46 @@ mpt_xmit_response(struct scst_cmd *scst_cmd)
goto out;
}
/*
* modifiy the response for an INQUIRY command to turn off
* support for tagged command queuing if we're on a SCSI bus.
* It's doubtful that caching sense data will work correctly
* if tagging is enabled.
*/
static void
mpt_inquiry_no_tagged_commands(MPT_STM_PRIV *priv, struct scst_cmd *scst_cmd)
{
int32_t length;
uint8_t *address;
TRACE_ENTRY();
/*
* only modify INQUIRY if we're on a SCSI bus,
* and we are handling a standard INQUIRY command
* (EVPD = 0)
*/
if (IsScsi(priv) && (scst_cmd->cdb[0] == INQUIRY) &&
!(scst_cmd->cdb[1] & 0x1)) {
if (!scst_cmd->sg_cnt) {
address = (uint8_t *)scst_cmd->sg;
length = scst_cmd->bufflen;
} else {
length = scst_get_buf_first(scst_cmd, &address);
}
if (length >= 8) {
TRACE_DBG("clearing BQUE + CMDQUE 0x%p", address);
address[6] &= ~0x80; /* turn off BQUE */
address[7] &= ~0x02; /* turn off CMDQUE */
}
if (scst_cmd->sg_cnt) {
scst_put_buf(scst_cmd, address);
}
}
TRACE_EXIT();
}
/*
* this function
* informs the driver that data buffer corresponding to the said command
@@ -1621,6 +1757,7 @@ static int mpt_rdy_to_xfer(struct scst_cmd *scst_cmd)
prm.cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
sess = (struct mpt_sess *)
scst_sess_get_tgt_priv(scst_cmd_get_session(scst_cmd));
mpt_inquiry_no_tagged_commands(sess->tgt->priv, scst_cmd);
prm.sg = (struct scatterlist *)NULL;
prm.bufflen = scst_cmd->bufflen;
@@ -2383,6 +2520,15 @@ stm_event_process(MPT_ADAPTER *ioc,
scsi_data = (EventDataScsi_t *)rep->Data;
printk("%s Ext Bus Reset on port %d\n",
ioc->name, scsi_data->BusPort);
/*
* clear any pending sense flags on bus reset
*/
if (IsScsi(priv)) {
for (i = 0; i < NUM_SCSI_DEVICES; i++) {
atomic_set(&priv->pending_sense[i],
MPT_STATUS_SENSE_IDLE);
}
}
break;
case MPI_EVENT_LINK_STATUS_CHANGE:
@@ -2979,10 +3125,10 @@ stm_send_target_status(MPT_STM_PRIV *priv,
priv->io_state[index] &= ~IO_STATE_AUTO_REPOST;
}
/*
* copy sense buffer so we can send it on the next
* cache sense buffer so we can send it on the next
* REQUEST SENSE command if the IOC can't send the
* status and sense simultaneously (generating
* MPT_IOC_STATUS_TARGET_STS_DATA_NOT_SENT IOCStatus)
* MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT IOCStatus)
*/
sense_size = min(sizeof(rsp->SenseData),
(size_t)SCSI_SENSE_BUFFERSIZE);
@@ -2992,7 +3138,8 @@ stm_send_target_status(MPT_STM_PRIV *priv,
TRACE_BUFFER("priv->pending_sense_buffer",
priv->pending_sense_buffer[init_index],
sense_size);
atomic_set(&priv->pending_sense[init_index], 1);
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_ATTEMPT);
}
if (rsp->Valid & SCSI_RSP_LEN_VALID) {
length += be32_to_cpu(rsp->PktFailuresListLength);
@@ -3710,7 +3857,7 @@ stm_scsi_configuration(MPT_STM_PRIV *priv,
wide ? "WIDE" : " ");
memcpy(priv->hw->config_buf, ScsiDevice1, sizeof(*ScsiDevice1));
stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, 1, i, sleep);
atomic_set(&priv->pending_sense[i], 0);
atomic_set(&priv->pending_sense[i], MPT_STATUS_SENSE_IDLE);
}
TRACE_EXIT();
@@ -5101,12 +5248,14 @@ stmapp_target_error(MPT_STM_PRIV *priv,
CMD *cmd;
int lun = 0;
int tag = 0;
int init_index = 0;
TRACE_ENTRY();
TRACE_ENTRY();
TRACE_DBG("%s target error, index %d, status %x, reason %x",
ioc->name, index, status, reason);
io_state = priv->io_state + index;
init_index = GET_INITIATOR_INDEX(reply_word);
if (*io_state & IO_STATE_DATA_SENT) {
TargetAssistRequest_t *req;
@@ -5219,13 +5368,20 @@ stmapp_target_error(MPT_STM_PRIV *priv,
* result of an attempt to send a check condition with
* attached sense bytes.
* The IOC knows it can't send status and sense over a
* traditional SCSI cable (non-packetized), so we should
* traditional SCSI cable (if non-packetized), so we should
* treat this as a successful completion, manually repost the
* command to the IOC, and free the SCST command.
*/
if (IsScsi(priv) && status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT) {
if (IsScsi(priv) && (status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT)) {
if ((*io_state & IO_STATE_AUTO_REPOST) ||
(*io_state & IO_STATE_STATUS_SENT)) {
/* if we know we were attempting to send status and sense
* simultaneously, indicate that we failed */
if (atomic_read(&priv->pending_sense[init_index]) ==
MPT_STATUS_SENSE_ATTEMPT) {
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_NOT_SENT);
}
stm_tgt_reply(ioc, reply_word);
TRACE_EXIT();
return;
@@ -5234,7 +5390,7 @@ stmapp_target_error(MPT_STM_PRIV *priv,
*io_state &= ~IO_STATE_AUTO_REPOST;
stm_target_cleanup(priv, index);
TRACE_EXIT();
TRACE_EXIT();
}
static void
+6
View File
@@ -327,6 +327,12 @@ typedef struct _MPT_STM_PRIV
#define MPT_SESS_INITING 0 /* The session is being unregistered */
#define MPT_SESS_SHUTDOWN 1 /* The session is being unregistered */
/* pending sense states */
#define MPT_STATUS_SENSE_IDLE 0 /* no cached pending sense */
#define MPT_STATUS_SENSE_ATTEMPT 1 /* attempt to send status and sense */
#define MPT_STATUS_SENSE_NOT_SENT 2 /* sense couldn't be sent with status */
#define MPT_STATUS_SENSE_HANDLE_RQ 3 /* REQUEST SENSE handled with cached sense */
struct mpt_cmd
{
struct mpt_sess *sess;