Handling of double reset UAs made more reliable

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@499 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2008-09-26 16:01:53 +00:00
parent a5ec0e4f51
commit 3785302eaa
4 changed files with 73 additions and 85 deletions

View File

@@ -1005,6 +1005,9 @@ struct scst_cmd {
/* Set if cmd is being processed in atomic context */
unsigned int atomic:1;
/* Set if this command was sent in double UA possible state */
unsigned int double_ua_possible:1;
/* Set if this command contains status */
unsigned int is_send_status:1;
@@ -1351,9 +1354,6 @@ struct scst_device {
/* Set if double reset UA is possible */
unsigned short dev_double_ua_possible:1;
/* Set if reset UA sent (to avoid double reset UAs) */
unsigned short dev_reset_ua_sent:1;
/**************************************************************/
/*************************************************************

View File

@@ -2564,7 +2564,7 @@ EXPORT_SYMBOL(scst_obtain_device_parameters);
/* Called under dev_lock and BH off */
void scst_process_reset(struct scst_device *dev,
struct scst_session *originator, struct scst_cmd *exclude_cmd,
struct scst_mgmt_cmd *mcmd)
struct scst_mgmt_cmd *mcmd, bool setUA)
{
struct scst_tgt_dev *tgt_dev;
struct scst_cmd *cmd, *tcmd;
@@ -2631,13 +2631,15 @@ void scst_process_reset(struct scst_device *dev,
}
}
/* BH already off */
spin_lock(&scst_temp_UA_lock);
scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA),
SCST_LOAD_SENSE(scst_sense_reset_UA));
scst_dev_check_set_local_UA(dev, exclude_cmd, scst_temp_UA,
sizeof(scst_temp_UA));
spin_unlock(&scst_temp_UA_lock);
if (setUA) {
/* BH already off */
spin_lock(&scst_temp_UA_lock);
scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA),
SCST_LOAD_SENSE(scst_sense_reset_UA));
scst_dev_check_set_local_UA(dev, exclude_cmd, scst_temp_UA,
sizeof(scst_temp_UA));
spin_unlock(&scst_temp_UA_lock);
}
TRACE_EXIT();
return;
@@ -2789,7 +2791,7 @@ void __scst_dev_check_set_UA(struct scst_device *dev,
/* Check for reset UA */
if (sense[12] == SCST_SENSE_ASC_UA_RESET)
scst_process_reset(dev, (exclude != NULL) ? exclude->sess : NULL,
exclude, NULL);
exclude, NULL, false);
scst_dev_check_set_local_UA(dev, exclude, sense, sense_len);

View File

@@ -404,7 +404,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
int other_ini, int call_dev_task_mgmt_fn);
void scst_process_reset(struct scst_device *dev,
struct scst_session *originator, struct scst_cmd *exclude_cmd,
struct scst_mgmt_cmd *mcmd);
struct scst_mgmt_cmd *mcmd, bool setUA);
static inline int scst_is_ua_command(struct scst_cmd *cmd)
{

View File

@@ -1544,9 +1544,17 @@ int scst_check_local_events(struct scst_cmd *cmd)
{
int res, rc;
struct scst_tgt_dev *tgt_dev = cmd->tgt_dev;
struct scst_device *dev = cmd->dev;
TRACE_ENTRY();
/*
* There's no race here, because we need to trace commands sent
* *after* dev_double_ua_possible flag was set.
*/
if (unlikely(dev->dev_double_ua_possible))
cmd->double_ua_possible = 1;
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
TRACE_MGMT_DBG("ABORTED set, aborting cmd %p", cmd);
goto out_uncomplete;
@@ -1565,10 +1573,9 @@ int scst_check_local_events(struct scst_cmd *cmd)
}
/* If we had internal bus reset, set the command error unit attention */
if ((cmd->dev->scsi_dev != NULL) &&
unlikely(cmd->dev->scsi_dev->was_reset)) {
if ((dev->scsi_dev != NULL) &&
unlikely(dev->scsi_dev->was_reset)) {
if (scst_is_ua_command(cmd)) {
struct scst_device *dev = cmd->dev;
int done = 0;
/* Prevent more than 1 cmd to be triggered by was_reset */
spin_lock_bh(&dev->dev_lock);
@@ -2044,101 +2051,80 @@ static int scst_check_sense(struct scst_cmd *cmd)
{
int res = 0;
struct scst_device *dev = cmd->dev;
int dbl_ua_possible, ua_sent = 0;
TRACE_ENTRY();
if (unlikely(cmd->ua_ignore))
goto out;
/* If we had internal bus reset behind us, set the command error UA */
if ((dev->scsi_dev != NULL) &&
unlikely(cmd->host_status == DID_RESET) &&
scst_is_ua_command(cmd)) {
TRACE(TRACE_MGMT, "DID_RESET: was_reset=%d host_status=%x",
dev->scsi_dev->was_reset, cmd->host_status);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_reset_UA));
/* just in case */
cmd->ua_ignore = 0;
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_reset_UA));
/* It looks like it is safe to clear was_reset here */
dev->scsi_dev->was_reset = 0;
}
dbl_ua_possible = dev->dev_double_ua_possible;
TRACE_DBG("cmd %p dbl_ua_possible %d", cmd, dbl_ua_possible);
if (unlikely(dbl_ua_possible)) {
spin_lock_bh(&dev->dev_lock);
barrier(); /* to reread dev_double_ua_possible */
dbl_ua_possible = dev->dev_double_ua_possible;
if (dbl_ua_possible)
ua_sent = dev->dev_reset_ua_sent;
else
spin_unlock_bh(&dev->dev_lock);
}
if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) &&
SCST_SENSE_VALID(cmd->sense)) {
PRINT_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense,
SCST_SENSE_BUFFERSIZE);
/* Check Unit Attention Sense Key */
if (scst_is_ua_sense(cmd->sense)) {
if (cmd->sense[12] == SCST_SENSE_ASC_UA_RESET) {
if (dbl_ua_possible) {
if (ua_sent) {
TRACE(TRACE_MGMT_MINOR, "%s",
"Double UA detected");
/* Do retry */
TRACE(TRACE_MGMT_MINOR, "Retrying cmd %p "
"(tag %llu)", cmd,
(long long unsigned)cmd->tag);
cmd->status = 0;
cmd->msg_status = 0;
cmd->host_status = DID_OK;
cmd->driver_status = 0;
mempool_free(cmd->sense, scst_sense_mempool);
cmd->sense = NULL;
sBUG_ON(cmd->dbl_ua_orig_resp_data_len < 0);
sBUG_ON(cmd->sg_buff_modified);
cmd->data_direction =
cmd->dbl_ua_orig_data_direction;
cmd->resp_data_len =
cmd->dbl_ua_orig_resp_data_len;
cmd->retry = 1;
cmd->state = SCST_CMD_STATE_SEND_TO_MIDLEV;
res = 1;
/*
* Dev is still blocked by this cmd, so
* it's OK to clear SCST_DEV_SERIALIZED
* here.
*/
dev->dev_double_ua_possible = 0;
dev->dev_serialized = 0;
dev->dev_reset_ua_sent = 0;
goto out_unlock;
} else
dev->dev_reset_ua_sent = 1;
}
}
if (cmd->ua_ignore == 0) {
if (unlikely(dbl_ua_possible)) {
__scst_dev_check_set_UA(dev, cmd,
cmd->sense,
SCST_SENSE_BUFFERSIZE);
} else {
scst_dev_check_set_UA(dev, cmd,
cmd->sense,
SCST_SENSE_BUFFERSIZE);
if (cmd->double_ua_possible) {
TRACE(TRACE_MGMT_MINOR, "Double UA "
"detected for device %p", dev);
TRACE(TRACE_MGMT_MINOR, "Retrying cmd %p "
"(tag %llu)", cmd,
(long long unsigned)cmd->tag);
cmd->status = 0;
cmd->msg_status = 0;
cmd->host_status = DID_OK;
cmd->driver_status = 0;
mempool_free(cmd->sense, scst_sense_mempool);
cmd->sense = NULL;
scst_check_restore_sg_buff(cmd);
sBUG_ON(cmd->dbl_ua_orig_resp_data_len < 0);
cmd->data_direction =
cmd->dbl_ua_orig_data_direction;
cmd->resp_data_len =
cmd->dbl_ua_orig_resp_data_len;
cmd->retry = 1;
cmd->state = SCST_CMD_STATE_SEND_TO_MIDLEV;
res = 1;
goto out_unlock;
}
}
scst_dev_check_set_UA(dev, cmd, cmd->sense,
SCST_SENSE_BUFFERSIZE);
}
}
if (unlikely(dbl_ua_possible)) {
if (ua_sent && scst_is_ua_command(cmd)) {
TRACE_MGMT_DBG("%s", "Clearing dbl_ua_possible flag");
if (unlikely(cmd->double_ua_possible)) {
if (scst_is_ua_command(cmd)) {
TRACE_MGMT_DBG("Clearing dbl_ua_possible flag (dev %p, "
"cmd %p)", dev, cmd);
/*
* Lock used to protect other flags in the bitfield
* (just in case, actually). Those 2 flags can't be
* changed in parallel, because the device is
* serialized.
*/
spin_lock_bh(&dev->dev_lock);
dev->dev_double_ua_possible = 0;
dev->dev_serialized = 0;
dev->dev_reset_ua_sent = 0;
spin_unlock_bh(&dev->dev_lock);
}
spin_unlock_bh(&dev->dev_lock);
}
out:
@@ -4090,7 +4076,7 @@ static int scst_target_reset(struct scst_mgmt_cmd *mcmd)
spin_lock_bh(&dev->dev_lock);
__scst_block_dev(dev);
scst_process_reset(dev, mcmd->sess, NULL, mcmd);
scst_process_reset(dev, mcmd->sess, NULL, mcmd, true);
spin_unlock_bh(&dev->dev_lock);
cont = 0;
@@ -4178,7 +4164,7 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
spin_lock_bh(&dev->dev_lock);
__scst_block_dev(dev);
scst_process_reset(dev, mcmd->sess, NULL, mcmd);
scst_process_reset(dev, mcmd->sess, NULL, mcmd, true);
spin_unlock_bh(&dev->dev_lock);
rc = scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 1);