*/
extern void scsi_old_done(Scsi_Cmnd * SCpnt);
extern void scsi_old_times_out(Scsi_Cmnd * SCpnt);
+extern int scsi_old_reset(Scsi_Cmnd *SCpnt, unsigned int flag);
+/*
+ * Private interface into the new error handling code.
+ */
+extern int scsi_new_reset(Scsi_Cmnd *SCpnt, unsigned int flag);
/*
* Function: scsi_initialize_queue()
kfree(SDpnt);
}
+/*
+ * Function: scsi_reset_provider_done_command
+ *
+ * Purpose: Dummy done routine.
+ *
+ * Notes: Some low level drivers will call scsi_done and end up here,
+ * others won't bother.
+ * We don't want the bogus command used for the bus/device
+ * reset to find its way into the mid-layer so we intercept
+ * it here.
+ */
+static void
+scsi_reset_provider_done_command(Scsi_Cmnd *SCpnt)
+{
+}
+
+/*
+ * Function: scsi_reset_provider
+ *
+ * Purpose: Send requested reset to a bus or device at any phase.
+ *
+ * Arguments: device - device to send reset to
+ * flag - reset type (see scsi.h)
+ *
+ * Returns: SUCCESS/FAILURE.
+ *
+ * Notes: This is used by the SCSI Generic driver to provide
+ * Bus/Device reset capability.
+ */
+int
+scsi_reset_provider(Scsi_Device *dev, int flag)
+{
+ Scsi_Cmnd SC, *SCpnt = ≻
+ int rtn;
+
+ memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout));
+ SCpnt->host = dev->host;
+ SCpnt->device = dev;
+ SCpnt->target = dev->id;
+ SCpnt->lun = dev->lun;
+ SCpnt->channel = dev->channel;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.waiting = NULL;
+ SCpnt->use_sg = 0;
+ SCpnt->old_use_sg = 0;
+ SCpnt->old_cmd_len = 0;
+ SCpnt->underflow = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->resid = 0;
+ SCpnt->serial_number = 0;
+ SCpnt->serial_number_at_timeout = 0;
+ SCpnt->host_scribble = NULL;
+ SCpnt->next = NULL;
+ SCpnt->state = SCSI_STATE_INITIALIZING;
+ SCpnt->owner = SCSI_OWNER_MIDLEVEL;
+
+ memset(&SCpnt->cmnd, '\0', sizeof(SCpnt->cmnd));
+
+ SCpnt->scsi_done = scsi_reset_provider_done_command;
+ SCpnt->done = NULL;
+ SCpnt->reset_chain = NULL;
+
+ SCpnt->buffer = NULL;
+ SCpnt->bufflen = 0;
+ SCpnt->request_buffer = NULL;
+ SCpnt->request_bufflen = 0;
+
+ SCpnt->internal_timeout = NORMAL_TIMEOUT;
+ SCpnt->abort_reason = DID_ABORT;
+
+ SCpnt->cmd_len = 0;
+
+ SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN;
+ SCpnt->sc_request = NULL;
+ SCpnt->sc_magic = SCSI_CMND_MAGIC;
+
+ /*
+ * Sometimes the command can get back into the timer chain,
+ * so use the pid as an identifier.
+ */
+ SCpnt->pid = 0;
+
+ if (dev->host->hostt->use_new_eh_code) {
+ rtn = scsi_new_reset(SCpnt, flag);
+ } else {
+ unsigned long flags;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ rtn = scsi_old_reset(SCpnt, flag);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ }
+
+ scsi_delete_timer(SCpnt);
+ return rtn;
+}
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
current->state = TASK_RUNNING; \
}; }
+/*
+ * old style reset request from external source
+ * (private to sg.c and scsi_error.c, supplied by scsi_obsolete.c)
+ */
+#define SCSI_TRY_RESET_DEVICE 1
+#define SCSI_TRY_RESET_BUS 2
+#define SCSI_TRY_RESET_HOST 3
+
+extern int scsi_reset_provider(Scsi_Device *, int);
+
#endif
/*
case DID_SOFT_ERROR:
goto maybe_retry;
+ case DID_ERROR:
+ if (msg_byte(SCpnt->result) == COMMAND_COMPLETE &&
+ status_byte(SCpnt->result) == RESERVATION_CONFLICT)
+ /*
+ * execute reservation conflict processing code
+ * lower down
+ */
+ break;
+ /* FALLTHROUGH */
+
case DID_BUS_BUSY:
case DID_PARITY:
- case DID_ERROR:
goto maybe_retry;
case DID_TIME_OUT:
/*
- * When we scan the bus, we get timeout messages for
- * these commands if there is no device available.
- * Other hosts report DID_NO_CONNECT for the same thing.
+ * When we scan the bus, we get timeout messages for
+ * these commands if there is no device available.
+ * Other hosts report DID_NO_CONNECT for the same thing.
*/
if ((SCpnt->cmnd[0] == TEST_UNIT_READY ||
SCpnt->cmnd[0] == INQUIRY)) {
*/
return SUCCESS;
case BUSY:
- case RESERVATION_CONFLICT:
goto maybe_retry;
+
+ case RESERVATION_CONFLICT:
+ printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
+ SCpnt->host->host_no, SCpnt->channel,
+ SCpnt->device->id, SCpnt->device->lun);
+ return SUCCESS; /* causes immediate I/O error */
default:
return FAILED;
}
up(host->eh_notify);
}
+/*
+ * Function: scsi_new_reset
+ *
+ * Purpose: Send requested reset to a bus or device at any phase.
+ *
+ * Arguments: SCpnt - command ptr to send reset with (usually a dummy)
+ * flag - reset type (see scsi.h)
+ *
+ * Returns: SUCCESS/FAILURE.
+ *
+ * Notes: This is used by the SCSI Generic driver to provide
+ * Bus/Device reset capability.
+ */
+int
+scsi_new_reset(Scsi_Cmnd *SCpnt, int flag)
+{
+ int rtn;
+
+ switch(flag) {
+ case SCSI_TRY_RESET_DEVICE:
+ rtn = scsi_try_bus_device_reset(SCpnt, 0);
+ if (rtn == SUCCESS)
+ break;
+ /* FALLTHROUGH */
+ case SCSI_TRY_RESET_BUS:
+ rtn = scsi_try_bus_reset(SCpnt);
+ if (rtn == SUCCESS)
+ break;
+ /* FALLTHROUGH */
+ case SCSI_TRY_RESET_HOST:
+ rtn = scsi_try_host_reset(SCpnt);
+ break;
+ default:
+ rtn = FAILED;
+ }
+
+ return rtn;
+}
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
break;
case RESERVATION_CONFLICT:
- printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
- " reset.\n", SCpnt->host->host_no, SCpnt->channel);
- scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
- status = REDO;
+ /*
+ * Most HAs will return an error for
+ * this, so usually reservation
+ * conflicts will be processed under
+ * DID_ERROR code
+ */
+ printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
+ SCpnt->host->host_no, SCpnt->channel,
+ SCpnt->device->id, SCpnt->device->lun);
+ status = CMD_FINISHED; /* returns I/O error */
break;
+
default:
printk("Internal error %s %d \n"
"status byte = %d \n", __FILE__,
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
case DID_ERROR:
+ if (msg_byte(result) == COMMAND_COMPLETE &&
+ status_byte(result) == RESERVATION_CONFLICT) {
+ printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
+ SCpnt->host->host_no, SCpnt->channel,
+ SCpnt->device->id, SCpnt->device->lun);
+ status = CMD_FINISHED; /* returns I/O error */
+ break;
+ }
status = MAYREDO;
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
}
+/*
+ * This function exports SCSI Bus, Device or Host reset capability
+ * and is for use with the SCSI generic driver.
+ */
+int
+scsi_old_reset(Scsi_Cmnd *SCpnt, unsigned int flag)
+{
+ unsigned int old_flags = SCSI_RESET_SYNCHRONOUS;
+
+ switch(flag) {
+ case SCSI_TRY_RESET_DEVICE:
+ /* no suggestion flags to add, device reset is default */
+ break;
+ case SCSI_TRY_RESET_BUS:
+ old_flags |= SCSI_RESET_SUGGEST_BUS_RESET;
+ break;
+ case SCSI_TRY_RESET_HOST:
+ old_flags |= SCSI_RESET_SUGGEST_HOST_RESET;
+ break;
+ default:
+ return FAILED;
+ }
+
+ if (scsi_reset(SCpnt, old_flags))
+ return FAILED;
+ return SUCCESS;
+}
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
EXPORT_SYMBOL(scsi_register_blocked_host);
EXPORT_SYMBOL(scsi_deregister_blocked_host);
+/*
+ * This symbol is for the highlevel drivers (e.g. sg) only.
+ */
+EXPORT_SYMBOL(scsi_reset_provider);
+
/*
* These are here only while I debug the rest of the scsi stuff.
*/