Index: linux/2.4/drivers/scsi/scsi.c
diff -c linux/2.4/drivers/scsi/scsi.c:1.1.1.5 linux/2.4/drivers/scsi/scsi.c:1.1.1.5.2.3
*** linux/2.4/drivers/scsi/scsi.c:1.1.1.5	Thu Feb 22 16:36:55 2001
--- linux/2.4/drivers/scsi/scsi.c	Fri Mar  9 14:45:43 2001
***************
*** 146,152 ****
--- 146,157 ----
   */
  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,2662 ****
--- 2662,2758 ----
           */
  	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 SC;
+         Scsi_Cmnd *SCpnt = &SC;
+ 
+         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.sem        	= 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;
  }
  
  /*
Index: linux/2.4/drivers/scsi/scsi.h
diff -c linux/2.4/drivers/scsi/scsi.h:1.1.1.3 linux/2.4/drivers/scsi/scsi.h:1.1.1.3.6.1
*** linux/2.4/drivers/scsi/scsi.h:1.1.1.3	Thu Jan 11 11:39:27 2001
--- linux/2.4/drivers/scsi/scsi.h	Wed Feb 28 17:37:27 2001
***************
*** 842,847 ****
--- 842,855 ----
  	remove_wait_queue(QUEUE, &wait);\
  	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
  
Index: linux/2.4/drivers/scsi/scsi_error.c
diff -c linux/2.4/drivers/scsi/scsi_error.c:1.1.1.3 linux/2.4/drivers/scsi/scsi_error.c:1.1.1.3.2.2
*** linux/2.4/drivers/scsi/scsi_error.c:1.1.1.3	Thu Feb 22 16:36:51 2001
--- linux/2.4/drivers/scsi/scsi_error.c	Fri Mar 16 10:22:36 2001
***************
*** 996,1004 ****
  	case DID_SOFT_ERROR:
  		goto maybe_retry;
  
  	case DID_BUS_BUSY:
  	case DID_PARITY:
- 	case DID_ERROR:
  		goto maybe_retry;
  	case DID_TIME_OUT:
  		/*
--- 996,1012 ----
  	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:
  		goto maybe_retry;
  	case DID_TIME_OUT:
  		/*
***************
*** 1065,1072 ****
  		 */
  		return SUCCESS;
  	case BUSY:
- 	case RESERVATION_CONFLICT:
  		goto maybe_retry;
  	default:
  		return FAILED;
  	}
--- 1073,1085 ----
  		 */
  		return SUCCESS;
  	case BUSY:
  		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,1964 ****
--- 1972,2015 ----
  	 */
  	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/2.4/drivers/scsi/scsi_obsolete.c
diff -c linux/2.4/drivers/scsi/scsi_obsolete.c:1.1.1.3 linux/2.4/drivers/scsi/scsi_obsolete.c:1.1.1.3.2.1
*** linux/2.4/drivers/scsi/scsi_obsolete.c:1.1.1.3	Thu Feb 22 16:37:01 2001
--- linux/2.4/drivers/scsi/scsi_obsolete.c	Wed Feb 28 17:37:27 2001
***************
*** 502,512 ****
  					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;
  				default:
  					printk("Internal error %s %d \n"
  					 "status byte = %d \n", __FILE__,
--- 502,518 ----
  					break;
  
  				case RESERVATION_CONFLICT:
!                                         /* 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,561 ****
--- 562,575 ----
  		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,1102 ****
--- 1111,1141 ----
  	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/2.4/drivers/scsi/scsi_syms.c
diff -c linux/2.4/drivers/scsi/scsi_syms.c:1.1.1.3 linux/2.4/drivers/scsi/scsi_syms.c:1.1.1.3.2.1
*** linux/2.4/drivers/scsi/scsi_syms.c:1.1.1.3	Thu Feb 22 16:37:31 2001
--- linux/2.4/drivers/scsi/scsi_syms.c	Wed Feb 28 17:37:27 2001
***************
*** 84,89 ****
--- 84,93 ----
  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);
