+ * scsi_try_host_reset - ask host adapter to reset itself
+ * @scmd: SCSI cmd to send hsot reset.
+ */
+static int scsi_try_host_reset(struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ int rtn;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
+ __func__));
+
+ if (!scmd->device->host->hostt->eh_host_reset_handler)
+ return FAILED;
+
+ rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd);
+
+ if (rtn == SUCCESS) {
+ if (!scmd->device->host->hostt->skip_settle_delay)
+ ssleep(HOST_RESET_SETTLE_TIME);
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ scsi_report_bus_reset(scmd->device->host,
+ scmd_channel(scmd));
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+ }
+
+ return rtn;
+}
+
+/**
+ * scsi_try_bus_reset - ask host to perform a bus reset
+ * @scmd: SCSI cmd to send bus reset.
+ */
+static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ int rtn;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
+ __func__));
+
+ if (!scmd->device->host->hostt->eh_bus_reset_handler)
+ return FAILED;
+
+ rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd);
+
+ if (rtn == SUCCESS) {
+ if (!scmd->device->host->hostt->skip_settle_delay)
+ ssleep(BUS_RESET_SETTLE_TIME);
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ scsi_report_bus_reset(scmd->device->host,
+ scmd_channel(scmd));
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+ }
+
+ 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
+ *
+ * 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_bus_device_reset(struct scsi_cmnd *scmd)
+{
+ int rtn;
+
+ if (!scmd->device->host->hostt->eh_device_reset_handler)
+ return FAILED;
+
+ rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
+ if (rtn == SUCCESS)
+ __scsi_report_device_reset(scmd->device, NULL);
+ return rtn;
+}
+
+static int __scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
+{
+ if (!scmd->device->host->hostt->eh_abort_handler)
+ return FAILED;
+
+ return scmd->device->host->hostt->eh_abort_handler(scmd);
+}
+
+/**
+ * scsi_try_to_abort_cmd - Ask host to abort a running command.
+ * @scmd: SCSI cmd to abort from Lower Level.
+ *
+ * Notes:
+ * This function will not return until the user's completion function
+ * has been called. there is no timeout on this operation. if the
+ * author of the low-level driver wishes this operation to be timed,
+ * they can provide this facility themselves. helper functions in
+ * scsi_error.c can be supplied to make this easier to do.
+ */
+static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
+{
+ /*
+ * scsi_done was called just after the command timed out and before
+ * we had a chance to process it. (db)
+ */
+ if (scmd->serial_number == 0)
+ return SUCCESS;
+ return __scsi_try_to_abort_cmd(scmd);
+}
+
+static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd)
+{
+ if (__scsi_try_to_abort_cmd(scmd) != SUCCESS)
+ if (scsi_try_bus_device_reset(scmd) != SUCCESS)
+ if (scsi_try_target_reset(scmd) != SUCCESS)
+ if (scsi_try_bus_reset(scmd) != SUCCESS)
+ scsi_try_host_reset(scmd);
+}
+
+/**
+ * scsi_eh_prep_cmnd - Save a scsi command info as part of error recory
+ * @scmd: SCSI command structure to hijack
+ * @ses: structure to save restore information
+ * @cmnd: CDB to send. Can be NULL if no new cmnd is needed
+ * @cmnd_size: size in bytes of @cmnd (must be <= BLK_MAX_CDB)
+ * @sense_bytes: size of sense data to copy. or 0 (if != 0 @cmnd is ignored)
+ *
+ * This function is used to save a scsi command information before re-execution
+ * as part of the error recovery process. If @sense_bytes is 0 the command
+ * sent must be one that does not transfer any data. If @sense_bytes != 0
+ * @cmnd is ignored and this functions sets up a REQUEST_SENSE command
+ * and cmnd buffers to read @sense_bytes into @scmd->sense_buffer.
+ */
+void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
+ unsigned char *cmnd, int cmnd_size, unsigned sense_bytes)
+{
+ struct scsi_device *sdev = scmd->device;
+
+ /*
+ * We need saved copies of a number of fields - this is because
+ * error handling may need to overwrite these with different values
+ * to run different commands, and once error handling is complete,
+ * we will need to restore these values prior to running the actual
+ * command.
+ */
+ ses->cmd_len = scmd->cmd_len;
+ ses->cmnd = scmd->cmnd;
+ ses->data_direction = scmd->sc_data_direction;
+ ses->sdb = scmd->sdb;
+ ses->next_rq = scmd->request->next_rq;
+ ses->result = scmd->result;
+ ses->underflow = scmd->underflow;
+ ses->prot_op = scmd->prot_op;
+
+ scmd->prot_op = SCSI_PROT_NORMAL;
+ scmd->cmnd = ses->eh_cmnd;
+ memset(scmd->cmnd, 0, BLK_MAX_CDB);
+ memset(&scmd->sdb, 0, sizeof(scmd->sdb));
+ scmd->request->next_rq = NULL;
+
+ if (sense_bytes) {
+ scmd->sdb.length = min_t(unsigned, SCSI_SENSE_BUFFERSIZE,
+ sense_bytes);
+ sg_init_one(&ses->sense_sgl, scmd->sense_buffer,
+ scmd->sdb.length);
+ scmd->sdb.table.sgl = &ses->sense_sgl;
+ scmd->sc_data_direction = DMA_FROM_DEVICE;
+ scmd->sdb.table.nents = 1;
+ scmd->cmnd[0] = REQUEST_SENSE;
+ scmd->cmnd[4] = scmd->sdb.length;
+ scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+ } else {
+ scmd->sc_data_direction = DMA_NONE;
+ if (cmnd) {
+ BUG_ON(cmnd_size > BLK_MAX_CDB);
+ memcpy(scmd->cmnd, cmnd, cmnd_size);
+ scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+ }
+ }
+
+ scmd->underflow = 0;
+
+ if (sdev->scsi_level <= SCSI_2 && sdev->scsi_level != SCSI_UNKNOWN)
+ scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) |
+ (sdev->lun << 5 & 0xe0);
+
+ /*
+ * Zero the sense buffer. The scsi spec mandates that any
+ * untransferred sense data should be interpreted as being zero.
+ */
+ memset(scmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+}
+EXPORT_SYMBOL(scsi_eh_prep_cmnd);
+
+/**
+ * scsi_eh_restore_cmnd - Restore a scsi command info as part of error recory
+ * @scmd: SCSI command structure to restore
+ * @ses: saved information from a coresponding call to scsi_eh_prep_cmnd
+ *
+ * Undo any damage done by above scsi_eh_prep_cmnd().
+ */
+void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
+{
+ /*
+ * Restore original data
+ */
+ scmd->cmd_len = ses->cmd_len;
+ scmd->cmnd = ses->cmnd;
+ scmd->sc_data_direction = ses->data_direction;
+ scmd->sdb = ses->sdb;
+ scmd->request->next_rq = ses->next_rq;
+ scmd->result = ses->result;
+ scmd->underflow = ses->underflow;
+ scmd->prot_op = ses->prot_op;
+}
+EXPORT_SYMBOL(scsi_eh_restore_cmnd);
+
+/**
+ * scsi_send_eh_cmnd - submit a scsi command as part of error recory
+ * @scmd: SCSI command structure to hijack
+ * @cmnd: CDB to send
+ * @cmnd_size: size in bytes of @cmnd
+ * @timeout: timeout for this request
+ * @sense_bytes: size of sense data to copy or 0
+ *
+ * This function is used to send a scsi command down to a target device
+ * as part of the error recovery process. See also scsi_eh_prep_cmnd() above.