Index: linux/drivers/scsi/scsi.c =================================================================== RCS file: /home/cvs/CVSROOT/linux/2.4/drivers/scsi/scsi.c,v retrieving revision 1.1.1.4 retrieving revision 1.1.1.4.6.4 diff -u -r1.1.1.4 -r1.1.1.4.6.4 --- linux/drivers/scsi/scsi.c 2000/11/30 17:19:15 1.1.1.4 +++ linux/drivers/scsi/scsi.c 2001/02/12 04:58:05 1.1.1.4.6.4 @@ -146,7 +146,12 @@ */ 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() @@ -2657,6 +2662,67 @@ */ scsi_release_commandblocks(SDpnt); kfree(SDpnt); +} + +/* Dummy done routine. 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) { + /* Empty function. Some low level drivers will call scsi_done + * (and end up here), others won't bother */ +} + + +/* + * 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) +{ + int rtn; + Scsi_Cmnd *SCpnt; + + /* get a dummy command to issue the reset to */ + SCpnt = scsi_allocate_device(dev, 1, 1); + /* NULL indicates interrupt */ + if(SCpnt == NULL) + return -EINTR; + + /* initialise the command */ + memset(&SCpnt->cmnd, '\0', sizeof(SCpnt->cmnd)); + + SCpnt->channel = dev->channel; + SCpnt->lun = dev->lun; + SCpnt->target = dev->id; + SCpnt->owner = SCSI_OWNER_MIDLEVEL; + SCpnt->scsi_done = scsi_reset_provider_done_command; + SCpnt->done = NULL; + SCpnt->reset_chain = NULL; + + SCpnt->internal_timeout = NORMAL_TIMEOUT; + SCpnt->abort_reason = DID_ABORT; + + if(dev->host->hostt->use_new_eh_code) { + rtn = scsi_new_reset(SCpnt, flag); + } else { + rtn = scsi_old_reset(SCpnt, flag); + } + if(timer_pending(&SCpnt->eh_timeout)) + scsi_delete_timer(SCpnt); + scsi_release_command(SCpnt); + + return rtn; } /* Index: linux/drivers/scsi/scsi.h =================================================================== RCS file: /home/cvs/CVSROOT/linux/2.4/drivers/scsi/scsi.h,v retrieving revision 1.1.1.3 diff -u -r1.1.1.3 scsi.h --- linux/drivers/scsi/scsi.h 2001/01/11 16:39:27 1.1.1.3 +++ linux/drivers/scsi/scsi.h 2001/02/12 17:58:41 @@ -842,6 +842,14 @@ remove_wait_queue(QUEUE, &wait);\ current->state = TASK_RUNNING; \ }; } +/* reset request from external source (private to sg.c, scsi.c + * scsi_error.c and 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 Index: linux/drivers/scsi/scsi_error.c =================================================================== RCS file: /home/cvs/CVSROOT/linux/2.4/drivers/scsi/scsi_error.c,v retrieving revision 1.1.1.2 retrieving revision 1.1.1.2.6.1 diff -u -r1.1.1.2 -r1.1.1.2.6.1 --- linux/drivers/scsi/scsi_error.c 2000/09/30 17:38:23 1.1.1.2 +++ linux/drivers/scsi/scsi_error.c 2001/02/11 17:33:42 1.1.1.2.6.1 @@ -996,9 +996,17 @@ 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; + /* fall through */ + + case DID_BUS_BUSY: case DID_PARITY: - case DID_ERROR: goto maybe_retry; case DID_TIME_OUT: /* @@ -1065,8 +1073,12 @@ */ 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; } @@ -1959,6 +1971,44 @@ */ if (host->eh_notify != NULL) 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; + /* fall through */ + case SCSI_TRY_RESET_BUS: + rtn = scsi_try_bus_reset(SCpnt); + if(rtn == SUCCESS) + break; + /* fall through */ + case SCSI_TRY_RESET_HOST: + rtn = scsi_try_host_reset(SCpnt); + break; + default: + rtn = FAILED; + } + return rtn; } /* Index: linux/drivers/scsi/scsi_obsolete.c =================================================================== RCS file: /home/cvs/CVSROOT/linux/2.4/drivers/scsi/scsi_obsolete.c,v retrieving revision 1.1.1.2 retrieving revision 1.1.1.2.6.1 diff -u -r1.1.1.2 -r1.1.1.2.6.1 --- linux/drivers/scsi/scsi_obsolete.c 2000/11/30 17:19:23 1.1.1.2 +++ linux/drivers/scsi/scsi_obsolete.c 2001/02/11 17:33:42 1.1.1.2.6.1 @@ -502,11 +502,17 @@ 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; - break; + /* 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__, @@ -556,6 +562,14 @@ 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; @@ -1097,6 +1111,31 @@ return rtn; } + +/* 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) +{ + int rtn; + 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; + } + return (scsi_reset(SCpnt, old_flags) == 0) ? SUCCESS : FAILED; +} /* * Overrides for Emacs so that we follow Linus's tabbing style. Index: linux/drivers/scsi/scsi_syms.c =================================================================== RCS file: /home/cvs/CVSROOT/linux/2.4/drivers/scsi/scsi_syms.c,v retrieving revision 1.1.1.2 retrieving revision 1.1.1.2.6.1 diff -u -r1.1.1.2 -r1.1.1.2.6.1 --- linux/drivers/scsi/scsi_syms.c 2000/11/30 17:19:11 1.1.1.2 +++ linux/drivers/scsi/scsi_syms.c 2001/02/11 17:33:42 1.1.1.2.6.1 @@ -84,6 +84,10 @@ EXPORT_SYMBOL(scsi_deregister_blocked_host); /* + * This symbol is for the sg device only + */ +EXPORT_SYMBOL(scsi_reset_provider); +/* * These are here only while I debug the rest of the scsi stuff. */ EXPORT_SYMBOL(scsi_hostlist);