diff --git a/scst/include/scst.h b/scst/include/scst.h index 017f94fcf..ce6abf9d9 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -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; - /**************************************************************/ /************************************************************* diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index f1d6107db..908fa0611 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index e3a47267f..c4c086764 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -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) { diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 386fb6d52..72f34e1bc 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -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);