Commit 6e7c838c authored by James Bottomley's avatar James Bottomley

scsi-reset-2.4.18.diff

SCSI reservation/reset handling

- Make both the old and the new error handlers respond correctly
  to reservation conflicts (i.e. return an I/O error).

- Add a scsi_reset_provider() function for use by the sg driver
  SCSI reset facility.
parent 381ab1f5
...@@ -156,7 +156,12 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt); ...@@ -156,7 +156,12 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt);
*/ */
extern void scsi_old_done(Scsi_Cmnd * SCpnt); extern void scsi_old_done(Scsi_Cmnd * SCpnt);
extern void scsi_old_times_out(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() * Function: scsi_initialize_queue()
...@@ -2722,6 +2727,102 @@ void scsi_free_host_dev(Scsi_Device * SDpnt) ...@@ -2722,6 +2727,102 @@ void scsi_free_host_dev(Scsi_Device * SDpnt)
kfree(SDpnt); 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. * Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically * Emacs will notice this stuff at the end of the file and automatically
......
...@@ -849,6 +849,16 @@ struct scsi_cmnd { ...@@ -849,6 +849,16 @@ struct scsi_cmnd {
current->state = TASK_RUNNING; \ 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 #endif
/* /*
......
...@@ -984,15 +984,24 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt) ...@@ -984,15 +984,24 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt)
case DID_SOFT_ERROR: case DID_SOFT_ERROR:
goto maybe_retry; 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_BUS_BUSY:
case DID_PARITY: case DID_PARITY:
case DID_ERROR:
goto maybe_retry; goto maybe_retry;
case DID_TIME_OUT: case DID_TIME_OUT:
/* /*
* When we scan the bus, we get timeout messages for * When we scan the bus, we get timeout messages for
* these commands if there is no device available. * these commands if there is no device available.
* Other hosts report DID_NO_CONNECT for the same thing. * Other hosts report DID_NO_CONNECT for the same thing.
*/ */
if ((SCpnt->cmnd[0] == TEST_UNIT_READY || if ((SCpnt->cmnd[0] == TEST_UNIT_READY ||
SCpnt->cmnd[0] == INQUIRY)) { SCpnt->cmnd[0] == INQUIRY)) {
...@@ -1053,8 +1062,13 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt) ...@@ -1053,8 +1062,13 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt)
*/ */
return SUCCESS; return SUCCESS;
case BUSY: case BUSY:
case RESERVATION_CONFLICT:
goto maybe_retry; 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: default:
return FAILED; return FAILED;
} }
...@@ -1959,6 +1973,45 @@ void scsi_error_handler(void *data) ...@@ -1959,6 +1973,45 @@ void scsi_error_handler(void *data)
up(host->eh_notify); 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. * Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically * Emacs will notice this stuff at the end of the file and automatically
......
...@@ -503,11 +503,18 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) ...@@ -503,11 +503,18 @@ void scsi_old_done(Scsi_Cmnd * SCpnt)
break; break;
case RESERVATION_CONFLICT: case RESERVATION_CONFLICT:
printk("scsi%d, channel %d : RESERVATION CONFLICT performing" /*
" reset.\n", SCpnt->host->host_no, SCpnt->channel); * Most HAs will return an error for
scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); * this, so usually reservation
status = REDO; * 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; break;
default: default:
printk("Internal error %s %d \n" printk("Internal error %s %d \n"
"status byte = %d \n", __FILE__, "status byte = %d \n", __FILE__,
...@@ -557,6 +564,14 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) ...@@ -557,6 +564,14 @@ void scsi_old_done(Scsi_Cmnd * SCpnt)
exit = (DRIVER_HARD | SUGGEST_ABORT); exit = (DRIVER_HARD | SUGGEST_ABORT);
break; break;
case DID_ERROR: 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; status = MAYREDO;
exit = (DRIVER_HARD | SUGGEST_ABORT); exit = (DRIVER_HARD | SUGGEST_ABORT);
break; break;
...@@ -1099,6 +1114,34 @@ int update_timeout(Scsi_Cmnd * SCset, int timeout) ...@@ -1099,6 +1114,34 @@ int update_timeout(Scsi_Cmnd * SCset, int timeout)
} }
/*
* 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. * Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically * Emacs will notice this stuff at the end of the file and automatically
......
...@@ -83,6 +83,11 @@ EXPORT_SYMBOL(scsi_end_request); ...@@ -83,6 +83,11 @@ EXPORT_SYMBOL(scsi_end_request);
EXPORT_SYMBOL(scsi_register_blocked_host); EXPORT_SYMBOL(scsi_register_blocked_host);
EXPORT_SYMBOL(scsi_deregister_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. * These are here only while I debug the rest of the scsi stuff.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment