return rtn;
}
+static void __scsi_report_device_reset(struct scsi_device *sdev, void *data)
+{
+ sdev->was_reset = 1;
+ sdev->expecting_cc_ua = 1;
+}
+
+/**
+ * scsi_try_target_reset - Ask host to perform a target reset
+ * @scmd: SCSI cmd used to send a target reset
+ *
+ * Notes:
+ * There is no timeout for this operation. if this operation is
+ * unreliable for a given host, then the host itself needs to put a
+ * timer on it, and set the host back to a consistent state prior to
+ * returning.
+ */
+static int scsi_try_target_reset(struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ int rtn;
+
+ if (!scmd->device->host->hostt->eh_target_reset_handler)
+ return FAILED;
+
+ rtn = scmd->device->host->hostt->eh_target_reset_handler(scmd);
+ if (rtn == SUCCESS) {
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ __starget_for_each_device(scsi_target(scmd->device), NULL,
+ __scsi_report_device_reset);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+ }
+
+ return rtn;
+}
+
/**
* scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
* @scmd: SCSI cmd used to send BDR
return FAILED;
rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
- if (rtn == SUCCESS) {
- scmd->device->was_reset = 1;
- scmd->device->expecting_cc_ua = 1;
- }
-
+ if (rtn == SUCCESS)
+ __scsi_report_device_reset(scmd->device, NULL);
return rtn;
}
{
if (__scsi_try_to_abort_cmd(scmd) != SUCCESS)
if (scsi_try_bus_device_reset(scmd) != SUCCESS)
- if (scsi_try_bus_reset(scmd) != SUCCESS)
- scsi_try_host_reset(scmd);
+ if (scsi_try_target_reset(scmd) != SUCCESS)
+ if (scsi_try_bus_reset(scmd) != SUCCESS)
+ scsi_try_host_reset(scmd);
}
/**
ses->cmd_len = scmd->cmd_len;
memcpy(ses->cmnd, scmd->cmnd, sizeof(scmd->cmnd));
ses->data_direction = scmd->sc_data_direction;
- ses->bufflen = scmd->request_bufflen;
- ses->buffer = scmd->request_buffer;
- ses->use_sg = scmd->use_sg;
- ses->resid = scmd->resid;
+ ses->sdb = scmd->sdb;
+ ses->next_rq = scmd->request->next_rq;
ses->result = scmd->result;
+ memset(&scmd->sdb, 0, sizeof(scmd->sdb));
+ scmd->request->next_rq = NULL;
+
if (sense_bytes) {
- scmd->request_bufflen = min_t(unsigned,
- sizeof(scmd->sense_buffer), sense_bytes);
+ scmd->sdb.length = min_t(unsigned, SCSI_SENSE_BUFFERSIZE,
+ sense_bytes);
sg_init_one(&ses->sense_sgl, scmd->sense_buffer,
- scmd->request_bufflen);
- scmd->request_buffer = &ses->sense_sgl;
+ scmd->sdb.length);
+ scmd->sdb.table.sgl = &ses->sense_sgl;
scmd->sc_data_direction = DMA_FROM_DEVICE;
- scmd->use_sg = 1;
+ scmd->sdb.table.nents = 1;
memset(scmd->cmnd, 0, sizeof(scmd->cmnd));
scmd->cmnd[0] = REQUEST_SENSE;
- scmd->cmnd[4] = scmd->request_bufflen;
+ scmd->cmnd[4] = scmd->sdb.length;
scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
} else {
- scmd->request_buffer = NULL;
- scmd->request_bufflen = 0;
scmd->sc_data_direction = DMA_NONE;
- scmd->use_sg = 0;
if (cmnd) {
memset(scmd->cmnd, 0, sizeof(scmd->cmnd));
memcpy(scmd->cmnd, cmnd, cmnd_size);
* Zero the sense buffer. The scsi spec mandates that any
* untransferred sense data should be interpreted as being zero.
*/
- memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
+ memset(scmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
}
EXPORT_SYMBOL(scsi_eh_prep_cmnd);
scmd->cmd_len = ses->cmd_len;
memcpy(scmd->cmnd, ses->cmnd, sizeof(scmd->cmnd));
scmd->sc_data_direction = ses->data_direction;
- scmd->request_bufflen = ses->bufflen;
- scmd->request_buffer = ses->buffer;
- scmd->use_sg = ses->use_sg;
- scmd->resid = ses->resid;
+ scmd->sdb = ses->sdb;
+ scmd->request->next_rq = ses->next_rq;
scmd->result = ses->result;
}
EXPORT_SYMBOL(scsi_eh_restore_cmnd);
return list_empty(work_q);
}
+/**
+ * scsi_eh_target_reset - send target reset if needed
+ * @shost: scsi host being recovered.
+ * @work_q: &list_head for pending commands.
+ * @done_q: &list_head for processed commands.
+ *
+ * Notes:
+ * Try a target reset.
+ */
+static int scsi_eh_target_reset(struct Scsi_Host *shost,
+ struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct scsi_cmnd *scmd, *tgtr_scmd, *next;
+ unsigned int id;
+ int rtn;
+
+ for (id = 0; id <= shost->max_id; id++) {
+ tgtr_scmd = NULL;
+ list_for_each_entry(scmd, work_q, eh_entry) {
+ if (id == scmd_id(scmd)) {
+ tgtr_scmd = scmd;
+ break;
+ }
+ }
+ if (!tgtr_scmd)
+ continue;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset "
+ "to target %d\n",
+ current->comm, id));
+ rtn = scsi_try_target_reset(tgtr_scmd);
+ if (rtn == SUCCESS) {
+ list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
+ if (id == scmd_id(scmd))
+ if (!scsi_device_online(scmd->device) ||
+ !scsi_eh_tur(tgtr_scmd))
+ scsi_eh_finish_cmd(scmd,
+ done_q);
+ }
+ } else
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset"
+ " failed target: "
+ "%d\n",
+ current->comm, id));
+ }
+
+ return list_empty(work_q);
+}
+
/**
* scsi_eh_bus_reset - send a bus reset
* @shost: &scsi host being recovered.
{
if (!scsi_eh_stu(shost, work_q, done_q))
if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
- if (!scsi_eh_bus_reset(shost, work_q, done_q))
- if (!scsi_eh_host_reset(work_q, done_q))
- scsi_eh_offline_sdevs(work_q, done_q);
+ if (!scsi_eh_target_reset(shost, work_q, done_q))
+ if (!scsi_eh_bus_reset(shost, work_q, done_q))
+ if (!scsi_eh_host_reset(work_q, done_q))
+ scsi_eh_offline_sdevs(work_q,
+ done_q);
}
EXPORT_SYMBOL_GPL(scsi_eh_ready_devs);
struct scsi_device *sdev;
__shost_for_each_device(sdev, shost) {
- if (channel == sdev_channel(sdev)) {
- sdev->was_reset = 1;
- sdev->expecting_cc_ua = 1;
- }
+ if (channel == sdev_channel(sdev))
+ __scsi_report_device_reset(sdev, NULL);
}
}
EXPORT_SYMBOL(scsi_report_bus_reset);
__shost_for_each_device(sdev, shost) {
if (channel == sdev_channel(sdev) &&
- target == sdev_id(sdev)) {
- sdev->was_reset = 1;
- sdev->expecting_cc_ua = 1;
- }
+ target == sdev_id(sdev))
+ __scsi_report_device_reset(sdev, NULL);
}
}
EXPORT_SYMBOL(scsi_report_device_reset);
unsigned long flags;
int rtn;
+ blk_rq_init(NULL, &req);
scmd->request = &req;
memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout));
memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd));
scmd->scsi_done = scsi_reset_provider_done_command;
- scmd->request_buffer = NULL;
- scmd->request_bufflen = 0;
+ memset(&scmd->sdb, 0, sizeof(scmd->sdb));
scmd->cmd_len = 0;
if (rtn == SUCCESS)
break;
/* FALLTHROUGH */
+ case SCSI_TRY_RESET_TARGET:
+ rtn = scsi_try_target_reset(scmd);
+ if (rtn == SUCCESS)
+ break;
+ /* FALLTHROUGH */
case SCSI_TRY_RESET_BUS:
rtn = scsi_try_bus_reset(scmd);
if (rtn == SUCCESS)
struct scsi_sense_hdr *sshdr)
{
return scsi_normalize_sense(cmd->sense_buffer,
- sizeof(cmd->sense_buffer), sshdr);
+ SCSI_SENSE_BUFFERSIZE, sshdr);
}
EXPORT_SYMBOL(scsi_command_normalize_sense);
}
}
EXPORT_SYMBOL(scsi_get_sense_info_fld);
+
+/**
+ * scsi_build_sense_buffer - build sense data in a buffer
+ * @desc: Sense format (non zero == descriptor format,
+ * 0 == fixed format)
+ * @buf: Where to build sense data
+ * @key: Sense key
+ * @asc: Additional sense code
+ * @ascq: Additional sense code qualifier
+ *
+ **/
+void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq)
+{
+ if (desc) {
+ buf[0] = 0x72; /* descriptor, current */
+ buf[1] = key;
+ buf[2] = asc;
+ buf[3] = ascq;
+ buf[7] = 0;
+ } else {
+ buf[0] = 0x70; /* fixed, current */
+ buf[2] = key;
+ buf[7] = 0xa;
+ buf[12] = asc;
+ buf[13] = ascq;
+ }
+}
+EXPORT_SYMBOL(scsi_build_sense_buffer);