patch-1.3.89 linux/drivers/scsi/scsi.c

Next file: linux/drivers/scsi/scsi.h
Previous file: linux/drivers/scsi/hosts.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.88/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
@@ -84,7 +84,7 @@
 static void scsi_done (Scsi_Cmnd *SCpnt);
 static int update_timeout (Scsi_Cmnd *, int);
 static void print_inquiry(unsigned char *data);
-static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
+static void scsi_times_out (Scsi_Cmnd * SCpnt);
 static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
                  Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
                  struct Scsi_Host *shpnt, char * scsi_result);
@@ -134,6 +134,8 @@
 /* Process ID of SCSI commands */
 unsigned long scsi_pid = 0;
 
+static unsigned long serial_number = 0;
+
 static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
 static void resize_dma_pool(void);
 
@@ -150,13 +152,12 @@
 
 struct proc_dir_entry proc_scsi_scsi = {
     PROC_SCSI_SCSI, 4, "scsi",
-    S_IFREG | S_IRUGO | S_IWUSR, 2, 0, 0, 0, 
+    S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0, 
     NULL,
     NULL, NULL,
     NULL, NULL, NULL
 };
 
-
 /*
  *  As the scsi do command functions are intelligent, and may need to
  *  redo a command, we need to keep track of the last command
@@ -484,16 +485,16 @@
   leave:
 
   {/* Unchain SCpnt from host_queue */
-    Scsi_Cmnd *prev,*next,*hqptr;
-    for(hqptr=shpnt->host_queue; hqptr!=SCpnt; hqptr=hqptr->next) ;
+    Scsi_Cmnd *prev, *next, *hqptr;
+    for(hqptr = shpnt->host_queue; hqptr != SCpnt; hqptr = hqptr->next) ;
     if(hqptr) {
-      prev=hqptr->prev;
-      next=hqptr->next;
+      prev = hqptr->prev;
+      next = hqptr->next;
       if(prev) 
-     	prev->next=next;
+     	prev->next = next;
       else 
-     	shpnt->host_queue=next;
-      if(next) next->prev=prev;
+     	shpnt->host_queue = next;
+      if(next) next->prev = prev;
     }
   }
  
@@ -810,6 +811,7 @@
 #define NORMAL_TIMEOUT 0
 #define IN_ABORT 1
 #define IN_RESET 2
+#define IN_RESET2 4
 
 /*
  * This is our time out function, called when the timer expires for a
@@ -817,10 +819,10 @@
  * command, that failing perform a kernel panic.
  */
 
-static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid)
+static void scsi_times_out (Scsi_Cmnd * SCpnt)
 {
     
-    switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET))
+    switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2))
     {
     case NORMAL_TIMEOUT:
 	{
@@ -829,12 +831,12 @@
 #endif
 	}
 	
-	if (!scsi_abort (SCpnt, DID_TIME_OUT, pid))
+	if (!scsi_abort (SCpnt, DID_TIME_OUT))
 	    return;
     case IN_ABORT:
 	printk("SCSI host %d abort (pid %ld) timed out - resetting\n",
 	       SCpnt->host->host_no, SCpnt->pid);
-	if (!scsi_reset (SCpnt, FALSE))
+	if (!scsi_reset (SCpnt, SCSI_RESET_ASYNCHRONOUS))
 	    return;
     case IN_RESET:
     case (IN_ABORT | IN_RESET):
@@ -842,14 +844,20 @@
 	 * you might conceivably want the machine up and running
 	 * esp if you have an ide disk. 
 	 */
-	printk("Unable to reset scsi host %d - ", SCpnt->host->host_no);
-	printk("probably a SCSI bus hang.\n");
+	printk("SCSI host %d reset (pid %ld) timed out - trying harder\n",
+	       SCpnt->host->host_no, SCpnt->pid);
 	SCpnt->internal_timeout &= ~IN_RESET;
-        scsi_reset (SCpnt, TRUE);
+	SCpnt->internal_timeout |= IN_RESET2;
+        scsi_reset (SCpnt,
+		    SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
 	return;
 	
     default:
-	INTERNAL_ERROR;
+	printk("SCSI host %d reset (pid %ld) timed out again -\n",
+	       SCpnt->host->host_no, SCpnt->pid);
+	printk("probably an unrecoverable SCSI bus or device hang.\n");
+	return;
+
     }
     
 }
@@ -874,8 +882,6 @@
     
     if (req && req->rq_status == RQ_INACTIVE)
 	panic("Inactive in request_queueable");
-    
-    SCpnt =  device->host->host_queue;
 
     /*
      * Look for a free command block.  If we have been instructed not to queue
@@ -883,18 +889,17 @@
      * going for this device first.
      */
       
-    SCpnt = device->host->host_queue;
     if (!device->single_lun) {
+	SCpnt = device->device_queue;
 	while(SCpnt){
-	    if(SCpnt->target == device->id &&
-	       SCpnt->lun == device->lun) {
-		if(SCpnt->request.rq_status == RQ_INACTIVE) break;
-	    }
-	    SCpnt = SCpnt->next;
+	    if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+	    SCpnt = SCpnt->device_next;
 	}
     } else {
+	SCpnt = device->host->host_queue;
 	while(SCpnt){
-	    if(SCpnt->target == device->id) {
+	    if(SCpnt->channel == device->channel 
+                && SCpnt->target == device->id) {
 		if (SCpnt->lun == device->lun) {
 		    if(found == NULL 
 		       && SCpnt->request.rq_status == RQ_INACTIVE) 
@@ -1014,19 +1019,18 @@
     if (intr_count && SCSI_BLOCK(host)) return NULL;
     
     while (1==1){
-	SCpnt = device->host->host_queue;
 	if (!device->single_lun) {
+	    SCpnt = device->device_queue;
 	    while(SCpnt){
-		if(SCpnt->target == device->id &&
-		   SCpnt->lun == device->lun) {
-		   SCwait = SCpnt;
-		    if(SCpnt->request.rq_status == RQ_INACTIVE) break;
-		}
-		SCpnt = SCpnt->next;
+		SCwait = SCpnt;
+		if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+		SCpnt = SCpnt->device_next;
 	    }
 	} else {
+	    SCpnt = device->host->host_queue;
 	    while(SCpnt){
-		if(SCpnt->target == device->id) {
+		if(SCpnt->channel == device->channel 
+                   && SCpnt->target == device->id) {
 		    if (SCpnt->lun == device->lun) {
 			SCwait = SCpnt;
 			if(found == NULL 
@@ -1061,15 +1065,16 @@
 	if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE)	/* Might have changed */
 	{
 #if 1	/* NEW CODE */
-		if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE) {
+		if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE){
  			sleep_on(&device->device_wait);
  			restore_flags(flags);
 	 	} else {
  			restore_flags(flags);
 	 		if (!wait) return NULL;
  			if (!SCwait) {
-	 			printk("Attempt to allocate device target %d, lun %d\n",
- 					device->id ,device->lun);
+	 			printk("Attempt to allocate device channel %d,"
+                                       " target %d, lun %d\n", device->channel,
+                                       device->id, device->lun);
  				panic("No device found in allocate_device\n");
 	 		}
  		}
@@ -1077,8 +1082,9 @@
 		    restore_flags(flags);
 		    if(!wait) return NULL;
 		    if (!SCwait) {
-			printk("Attempt to allocate device channel %d, target %d, "
-			       "lun %d\n", device->channel, device->id, device->lun);
+			printk("Attempt to allocate device channel %d, target"
+                               " %d, lun %d\n", device->channel, device->id, 
+                               device->lun);
 			panic("No device found in allocate_device\n");
 		    }
 		    SCSI_SLEEP(&device->device_wait,
@@ -1166,6 +1172,10 @@
      * we can avoid the drive not being ready.
      */
     save_flags(flags);
+    cli();
+    /* Assign a unique nonzero serial_number. */
+    if (++serial_number == 0) serial_number = 1;
+    SCpnt->serial_number = serial_number;
     sti();
     temp = host->last_reset + MIN_RESET_DELAY;
     while (jiffies < temp);
@@ -1326,6 +1336,8 @@
     SCpnt->target = target;
     SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);
 #endif
+    SCpnt->reset_chain = NULL;
+    SCpnt->serial_number = 0;
     SCpnt->bufflen = bufflen;
     SCpnt->buffer = buffer;
     SCpnt->flags=0;
@@ -1449,6 +1461,7 @@
     int oldto;
     struct Scsi_Host * host = SCpnt->host;
     int result = SCpnt->result;
+    SCpnt->serial_number = 0;
     oldto = update_timeout(SCpnt, 0);
     
 #ifdef DEBUG_TIMEOUT
@@ -1486,7 +1499,9 @@
 	    /* Failed to obtain sense information */
 	{
 	    SCpnt->flags &= ~WAS_SENSE;
+#if 0	/* This cannot possibly be correct. */
 	    SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+#endif
 	    
 	    if (!(SCpnt->flags & WAS_RESET))
 	    {
@@ -1494,7 +1509,7 @@
 		       " failed, performing reset.\n",
 		       SCpnt->host->host_no, SCpnt->channel, SCpnt->target, 
 		       SCpnt->lun);
-		scsi_reset(SCpnt, FALSE);
+		scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
 		return;
 	    }
 	    else
@@ -1515,7 +1530,9 @@
 		    printk ("In scsi_done, GOOD status, COMMAND COMPLETE, parsing sense information.\n");
 #endif
 		    SCpnt->flags &= ~WAS_SENSE;
+#if 0	/* This cannot possibly be correct. */
 		    SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+#endif
 		    
 		    switch (checked = check_sense(SCpnt))
 		    {
@@ -1595,7 +1612,7 @@
 	    case RESERVATION_CONFLICT:
 		printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
 		       " reset.\n", SCpnt->host->host_no, SCpnt->channel);
-		scsi_reset(SCpnt, FALSE);
+		scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
 		return;
 #if 0
 		exit = DRIVER_SOFT | SUGGEST_ABORT;
@@ -1717,7 +1734,7 @@
 	    {
 		printk("scsi%d channel %d : resetting for second half of retries.\n",
 		       SCpnt->host->host_no, SCpnt->channel);
-		scsi_reset(SCpnt, FALSE);
+		scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
 		break;
 	    }
 	    
@@ -1798,7 +1815,7 @@
  */
 
 
-int scsi_abort (Scsi_Cmnd * SCpnt, int why, int pid)
+int scsi_abort (Scsi_Cmnd * SCpnt, int why)
 {
     int oldto;
     unsigned long flags;
@@ -1813,7 +1830,7 @@
 	 * Protect against races here.  If the command is done, or we are
 	 * on a different command forget it.
 	 */
-	if (SCpnt->request.rq_status == RQ_INACTIVE || pid != SCpnt->pid) {
+	if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
 	    restore_flags(flags);
 	    return 0;
 	}
@@ -1849,7 +1866,7 @@
 		   SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel, 
 		   (int) SCpnt->target, (int) SCpnt->lun);
 	    print_command (SCpnt->cmnd);
-	    if (SCpnt->request.rq_status == RQ_INACTIVE || pid != SCpnt->pid)
+	    if (SCpnt->serial_number != SCpnt->serial_number_at_timeout)
 		return 0;
 	    SCpnt->abort_reason = why;
 	    switch(host->hostt->abort(SCpnt)) {
@@ -1926,9 +1943,9 @@
 }
 
 
-int scsi_reset (Scsi_Cmnd * SCpnt, int bus_reset_flag)
+int scsi_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
 {
-    int temp, oldto;
+    int temp;
     unsigned long flags;
     Scsi_Cmnd * SCpnt1;
     struct Scsi_Host * host = SCpnt->host;
@@ -1936,6 +1953,7 @@
     printk("SCSI bus is being reset for host %d.\n",
 	   host->host_no);
  
+#if 0
     /*
      * First of all, we need to make a recommendation to the low-level
      * driver as to whether a BUS_DEVICE_RESET should be performed,
@@ -1952,6 +1970,13 @@
      * result in some race conditions, but no more than
      * you would usually get with timeouts.  We will cross
      * that bridge when we come to it.
+     *
+     * This is actually a pretty bad idea, since a sequence of
+     * commands will often timeout together and this will cause a
+     * Bus Device Reset followed immediately by a SCSI Bus Reset.
+     * If all of the active devices really are jammed up, the
+     * Bus Device Reset will quickly timeout and scsi_times_out
+     * will follow up with a SCSI Bus Reset anyway.
      */
     SCpnt1 = host->host_queue;
     while(SCpnt1) {
@@ -1961,22 +1986,34 @@
         SCpnt1 = SCpnt1->next;
  	}
     if( SCpnt1 == NULL ) {
-        SCpnt->host->suggest_bus_reset = TRUE;
+        reset_flags |= SCSI_RESET_SUGGEST_BUS_RESET;
     }
     
-    
     /*
      * If the code that called us is suggesting a hard reset, then
      * definitely request it.  This usually occurs because a
      * BUS_DEVICE_RESET times out.
+     *
+     * Passing reset_flags along takes care of this automatically.
      */
-    if( bus_reset_flag ) {
+    if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET ) {
         SCpnt->host->suggest_bus_reset = TRUE;
     }
+#endif
     
     while (1) {
 	save_flags(flags);
 	cli();
+
+	/*
+	 * Protect against races here.  If the command is done, or we are
+	 * on a different command forget it.
+	 */
+	if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
+	    restore_flags(flags);
+	    return 0;
+	}
+
 	if (SCpnt->internal_timeout & IN_RESET)
 	{
 	    restore_flags(flags);
@@ -1986,7 +2023,7 @@
 	else
 	{
 	    SCpnt->internal_timeout |= IN_RESET;
-	    oldto = update_timeout(SCpnt, RESET_TIMEOUT);
+	    update_timeout(SCpnt, RESET_TIMEOUT);
 	    
 	    if (host->host_busy)
 	    {
@@ -1997,7 +2034,7 @@
 #if 0
 			if (!(SCpnt1->flags & IS_RESETTING) &&
 			    !(SCpnt1->internal_timeout & IN_ABORT))
-			    scsi_abort(SCpnt1, DID_RESET, SCpnt->pid);
+			    scsi_abort(SCpnt1, DID_RESET);
 #endif
 			SCpnt1->flags |= (WAS_RESET | IS_RESETTING);
 		    }
@@ -2005,7 +2042,7 @@
 		}
 		
 		host->last_reset = jiffies;
-		temp = host->hostt->reset(SCpnt);
+		temp = host->hostt->reset(SCpnt, reset_flags);
 		host->last_reset = jiffies;
 	    }
 	    else
@@ -2014,7 +2051,7 @@
 		restore_flags(flags);
 		host->last_reset = jiffies;
 	        SCpnt->flags |= (WAS_RESET | IS_RESETTING);
-		temp = host->hostt->reset(SCpnt);
+		temp = host->hostt->reset(SCpnt, reset_flags);
 		host->last_reset = jiffies;
 		if (!host->block) host->host_busy--;
 	    }
@@ -2040,13 +2077,13 @@
 		save_flags(flags);
 		cli();
 		SCpnt->internal_timeout &= ~IN_RESET;
-		update_timeout(SCpnt, oldto);
 		restore_flags(flags);
 		return 0;
 	    case SCSI_RESET_PENDING:
 	        if (temp & SCSI_RESET_BUS_RESET)
 		  scsi_mark_host_bus_reset(host);
 		else scsi_mark_device_reset(SCpnt->device);
+	    case SCSI_RESET_NOT_RUNNING:
 		return 0;
 	    case SCSI_RESET_PUNT:
                 SCpnt->internal_timeout &= ~IN_RESET;
@@ -2103,36 +2140,43 @@
      * We must not enter update_timeout with a timeout condition still pending.
      */
     
-    int timed_out, pid;
+    int timed_out;
     unsigned long flags;
     struct Scsi_Host * host;
     Scsi_Cmnd * SCpnt = NULL;
     
-    do {
-	save_flags(flags);
-	cli();
-	
-	update_timeout(NULL, 0);
-	/*
-	 * Find all timers such that they have 0 or negative (shouldn't happen)
-	 * time remaining on them.
-	 */
-	
-	timed_out = 0;
-	for(host = scsi_hostlist; host; host = host->next) {
-	    for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
-		if (SCpnt->timeout == -1)
-		{
-		    SCpnt->timeout = 0;
-		    pid = SCpnt->pid;
+    save_flags(flags);
+    cli();
+
+    update_timeout(NULL, 0);
+
+    /*
+     * Find all timers such that they have 0 or negative (shouldn't happen)
+     * time remaining on them.
+     */
+    timed_out = 0;
+    for (host = scsi_hostlist; host; host = host->next) {
+	for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+	    if (SCpnt->timeout == -1)
+	      {
+		SCpnt->timeout = 0;
+		SCpnt->serial_number_at_timeout = SCpnt->serial_number;
+		++timed_out;
+	      }
+    }
+    if (timed_out > 0) {
+	for (host = scsi_hostlist; host; host = host->next) {
+	    for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+		if (SCpnt->serial_number_at_timeout > 0 &&
+		    SCpnt->serial_number_at_timeout == SCpnt->serial_number)
+		  {
 		    restore_flags(flags);
-		    scsi_times_out(SCpnt, pid);
-		    ++timed_out;
-		    save_flags(flags);
+		    scsi_times_out(SCpnt);
+		    SCpnt->serial_number_at_timeout = 0;
 		    cli();
-		}
-	}
-    } while (timed_out);
+		  }
+	  }
+    }
     restore_flags(flags);
 }
 
@@ -2155,6 +2199,27 @@
     save_flags(flags);
     cli();
 
+    oldto = 0;
+
+    /*
+     * This routine can be a performance bottleneck under high loads, since
+     * it is called twice per SCSI operation: once when internal_cmnd is
+     * called, and again when scsi_done completes the command.  To limit
+     * the load this routine can cause, we shortcut processing if no clock
+     * ticks have occurred since the last time it was called.  This may
+     * cause the computation of least below to be inaccurrate, but it will
+     * be corrected after the next clock tick.
+     */
+
+    if (jiffies == time_start && timer_table[SCSI_TIMER].expires > 0) {
+	if(SCset){
+	    oldto = SCset->timeout;
+	    SCset->timeout = timeout;
+	}
+	restore_flags(flags);
+	return oldto;
+    }
+
     /*
      * Figure out how much time has passed since the last time the timeouts
      * were updated
@@ -2180,7 +2245,7 @@
 	for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
 	    if (SCpnt->timeout > 0) {
 	        if (SCpnt != SCset)
-		  SCpnt->timeout -= used;
+		    SCpnt->timeout -= used;
 		if(SCpnt->timeout <= 0) SCpnt->timeout = -1;
 		if(SCpnt->timeout > 0 && SCpnt->timeout < least)
 		    least = SCpnt->timeout;
@@ -2323,16 +2388,19 @@
 
 void scsi_build_commandblocks(Scsi_Device * SDpnt)
 {
+    struct Scsi_Host *host = SDpnt->host;
     int j;
-    Scsi_Cmnd * SCpnt;
-    struct Scsi_Host * host = NULL;
-    
-    for(j=0;j<SDpnt->host->cmd_per_lun;j++){
-	host = SDpnt->host;
-	SCpnt = (Scsi_Cmnd *)
-		scsi_init_malloc(sizeof(Scsi_Cmnd),
-				 GFP_ATOMIC |
-				 (host->unchecked_isa_dma ? GFP_DMA : 0));
+    Scsi_Cmnd * SCpnt;                 
+
+    if (SDpnt->queue_depth == 0)
+        SDpnt->queue_depth = host->cmd_per_lun;
+    SDpnt->device_queue = NULL;
+ 
+    for(j=0;j<SDpnt->queue_depth;j++){
+      SCpnt = (Scsi_Cmnd *)
+              scsi_init_malloc(sizeof(Scsi_Cmnd),
+                               GFP_ATOMIC |
+                               (host->unchecked_isa_dma ? GFP_DMA : 0));
 	SCpnt->host = host;
 	SCpnt->device = SDpnt;
 	SCpnt->target = SDpnt->id;
@@ -2345,12 +2413,16 @@
 	SCpnt->timeout = 0;
 	SCpnt->underflow = 0;
 	SCpnt->transfersize = 0;
+	SCpnt->serial_number = 0;
+	SCpnt->serial_number_at_timeout = 0;
 	SCpnt->host_scribble = NULL;
 	if(host->host_queue)
 	    host->host_queue->prev = SCpnt;
 	SCpnt->next = host->host_queue;
 	SCpnt->prev = NULL;
 	host->host_queue = SCpnt;
+	SCpnt->device_next = SDpnt->device_queue;
+	SDpnt->device_queue = SCpnt;
     }
     SDpnt->has_cmdblocks = 1;
 }
@@ -2392,8 +2464,11 @@
 
     scsi_devices = (Scsi_Device *) NULL;
 
-    for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+    for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
 	scan_scsis(shpnt,0,0,0,0);           /* scan for scsi devices */
+	if (shpnt->select_queue_depths != NULL)
+	    (shpnt->select_queue_depths)(shpnt, scsi_devices);
+    }
 
     printk("scsi : detected ");
     for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
@@ -2480,11 +2555,13 @@
 int scsi_proc_info(char *buffer, char **start, off_t offset, int length, 
 		    int hostno, int inout)
 {
-    Scsi_Device *scd;
+    Scsi_Cmnd *SCpnt;
+    struct Scsi_Device_Template *SDTpnt;
+    Scsi_Device *scd, *scd_h = NULL;
     struct Scsi_Host *HBA_ptr;
-    int  parameter[4];
     char *p;
-    int	  i,size, len = 0;
+    int   host, channel, id, lun;
+    int	  size, len = 0;
     off_t begin = 0;
     off_t pos = 0;
 
@@ -2529,8 +2606,11 @@
 	return (len);     
     }
 
+    if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
+	return(-EINVAL);
+
     /*
-     * Usage: echo "scsi singledevice 0 1 2 3" >/proc/scsi/scsi
+     * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
      * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
      * Consider this feature BETA.
      *     CAUTION: This is not for hotplugging your peripherals. As
@@ -2540,35 +2620,106 @@
      * already connected device. It is perhaps not 
      * guaranteed this device doesn't corrupt an ongoing data transfer.
      */
-    if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
-	return(-EINVAL);
-
-    if(!strncmp("singledevice", buffer + 5, 12)) {
-	p = buffer + 17;
+    if(!strncmp("add-single-device", buffer + 5, 17)) {
+	p = buffer + 23;
 
-	for(i=0; i<4; i++)	{
-	    p++;
-	    parameter[i] = simple_strtoul(p, &p, 0);
-	}
-	printk("scsi singledevice %d %d %d %d\n", parameter[0], parameter[1],
-			parameter[2], parameter[3]);
-
-	while(scd && (scd->host->host_no != parameter[0] 
-	      || scd->channel != parameter[1] 
-	      || scd->id != parameter[2] 
-	      || scd->lun != parameter[3])) {
+        host    = simple_strtoul(p, &p, 0);
+        channel = simple_strtoul(p+1, &p, 0);
+        id      = simple_strtoul(p+1, &p, 0);
+        lun     = simple_strtoul(p+1, &p, 0);
+
+	printk("scsi singledevice %d %d %d %d\n", host, channel,
+			id, lun);
+
+	while(scd && (scd->host->host_no != host 
+	      || scd->channel != channel 
+	      || scd->id != id 
+	      || scd->lun != lun)) {
 	    scd = scd->next;
 	}
 	if(scd)
 	    return(-ENOSYS);  /* We do not yet support unplugging */
-	while(HBA_ptr && HBA_ptr->host_no != parameter[0])
+	while(HBA_ptr && HBA_ptr->host_no != host)
 	    HBA_ptr = HBA_ptr->next;
 
 	if(!HBA_ptr)
 	    return(-ENXIO);
 
-	scan_scsis (HBA_ptr, 1, parameter[1], parameter[2], parameter[3]);
+	scan_scsis (HBA_ptr, 1, channel, id, lun);
 	return(length);
+        
+    } 
+
+    /*
+     * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
+     * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
+     *
+     * Consider this feature pre-BETA.
+     *
+     *     CAUTION: This is not for hotplugging your peripherals. As
+     *     SCSI was not designed for this you could damage your
+     *     hardware and thoroughly confuse the SCSI subsystem.  
+     *
+     */
+    else if(!strncmp("remove-single-device", buffer + 5, 20)) {
+        p = buffer + 26;
+        
+        host    = simple_strtoul(p, &p, 0);
+        channel = simple_strtoul(p+1, &p, 0);
+        id      = simple_strtoul(p+1, &p, 0);
+        lun     = simple_strtoul(p+1, &p, 0);
+        
+        while(scd != NULL) {
+            if(scd->host->host_no == host  
+               && scd->channel == channel 
+               && scd->id == id 
+               && scd->lun == lun){
+                break;                
+            }
+            scd_h = scd;
+            scd = scd->next;
+        }
+        
+        if(scd == NULL)
+            return(-ENODEV);  /* there is no such device attached */
+        
+        if(scd->access_count)
+            return(-EBUSY);
+        
+        SDTpnt = scsi_devicelist;
+        while(SDTpnt != NULL) {
+            if(SDTpnt->detach) (*SDTpnt->detach)(scd);
+            SDTpnt = SDTpnt->next;
+        }
+        
+        if(scd->attached == 0) {
+            /*
+             * Nobody is using this device any more.
+             * Free all of the command structures.
+             */
+            for(SCpnt=scd->host->host_queue; SCpnt; SCpnt = SCpnt->next){
+                if(SCpnt->device == scd) {
+                    if(SCpnt->prev != NULL)
+                        SCpnt->prev->next = SCpnt->next;
+                    if(SCpnt->next != NULL)
+                        SCpnt->next->prev = SCpnt->prev;
+                    if(SCpnt == scd->host->host_queue)
+                        scd->host->host_queue = SCpnt->next;
+                    scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
+                }
+            }
+            /* Now we can remove the device structure */
+            if(scd_h != NULL) {
+                scd_h->next = scd->next;
+            } else if (scsi_devices == scd) {
+                /* We had a hit on the first entry of the device list */
+                scsi_devices = scd->next;
+            } 
+            scsi_init_free((char *) scd, sizeof(Scsi_Device));
+        } else {
+            return(-EBUSY);
+        }
+        return(0);
     }
     return(-EINVAL);
 }
@@ -2628,17 +2779,17 @@
     
     for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
 	host = SDpnt->host;
-	
+
 	if(SDpnt->type != TYPE_TAPE)
 	    new_dma_sectors += ((host->sg_tablesize *
 	                         sizeof(struct scatterlist) + 511) >> 9) *
-	                             host->cmd_per_lun;
+	                       SDpnt->queue_depth;
 	
 	if(host->unchecked_isa_dma &&
 	   scsi_need_isa_bounce_buffers &&
 	   SDpnt->type != TYPE_TAPE) {
 	    new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
-	        host->cmd_per_lun;
+						  SDpnt->queue_depth;
 	    new_need_isa_buffer++;
 	}
     }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this