Config.help  |   22 +++
 Config.in    |   11 +
 Makefile     |    2 
 hosts.c      |  107 ++++++++------
 hosts.h      |   35 +---
 scsi.c       |  421 +++++++++++++++++++++++++++++------------------------------
 scsi.h       |  152 +++++++++++++++++++--
 scsi_error.c |  361 +++++++++++++++-----------------------------------
 8 files changed, 570 insertions(+), 541 deletions(-)
diff -Nru a/drivers/scsi/Config.help b/drivers/scsi/Config.help
--- a/drivers/scsi/Config.help	Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/Config.help	Fri Oct 25 11:26:47 2002
@@ -152,6 +152,28 @@
   REPORT LUNS scanning is done only for SCSI-3 devices. Most users can safely
   answer N here.
 
+CONFIG_SCSI_MULTI_PATH_IO
+  If your system contains SCSI host adapters connecting into a shared
+  transport, or you have storage devices that have multiple ports, and
+  you are using an adapter that properly supports multiple paths, and your
+  storage devices support unique id's (unique serial numbers) say Y
+  here to enable the multiple paths/ports to be merged into one mid layer
+  SCSI device.
+
+  If you are unsure if your system can properly support this
+  capability or that your devices have this capability say N.
+
+CONFIG_SCSI_PATH_POLICY_LPU 
+  When CONFIG_SCSI_MULTI_PATH_IO is selected this option sets the path
+  selection policy. 
+
+  The last path used policy only uses another path after a path failure.
+
+  The round robin policy round robins across all active paths.
+
+  The last path used policy is the safest path policy to select if you
+  cannot determine the best path policy for your storage device.
+
 CONFIG_SCSI_CONSTANTS
   The error messages regarding your SCSI hardware will be easier to
   understand if you say Y here; it will enlarge your kernel by about
diff -Nru a/drivers/scsi/Config.in b/drivers/scsi/Config.in
--- a/drivers/scsi/Config.in	Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/Config.in	Fri Oct 25 11:26:47 2002
@@ -22,7 +22,16 @@
 
 bool '  Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN
 bool '  Build with SCSI REPORT LUNS support' CONFIG_SCSI_REPORT_LUNS
-  
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	bool '  Enable Mid Layer Multi-Path IO support (EXPERIMENTAL)' CONFIG_SCSI_MULTI_PATH_IO
+	if [ "$CONFIG_SCSI_MULTI_PATH_IO" != "n" ] ; then
+	     choice ' Multipath Routing algorithim'			\
+		 "LastPathUsed		CONFIG_SCSI_PATH_POLICY_LPU	\
+		  RoundRobin		CONFIG_SCSI_PATH_POLICY_RR"	\
+		LastPathUsed
+	fi
+fi
 bool '  Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS
 bool '  SCSI logging facility' CONFIG_SCSI_LOGGING
 
diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile	Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/Makefile	Fri Oct 25 11:26:48 2002
@@ -123,7 +123,7 @@
 
 scsi_mod-objs	:= scsi.o hosts.o scsi_ioctl.o constants.o scsicam.o \
 			scsi_proc.o scsi_error.o scsi_lib.o scsi_merge.o \
-			scsi_scan.o scsi_syms.o
+			scsi_scan.o scsi_syms.o scsi_paths.o
 			
 sd_mod-objs	:= sd.o
 sr_mod-objs	:= sr.o sr_ioctl.o sr_vendor.o
diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c	Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/hosts.c	Fri Oct 25 11:26:47 2002
@@ -117,6 +117,7 @@
 	Scsi_Device *sdev;
 	struct Scsi_Device_Template *sdev_tp;
 	Scsi_Cmnd *scmd;
+	struct scsi_traverse_hndl strav_hndl;
 
 	/*
 	 * Current policy is all shosts go away on unregister.
@@ -129,10 +130,10 @@
 	 * help prevent race conditions where other hosts/processors could
 	 * try and get in and queue a command.
 	 */
-	for (sdev = shost->host_queue; sdev; sdev = sdev->next) 
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no)
 		sdev->online = FALSE;
 
-	for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
 		/*
 		 * Loop over all of the commands associated with the
 		 * device.  If any of them are busy, then set the state
@@ -147,8 +148,8 @@
 				       scmd->request->rq_status,
 				       scmd->target, scmd->pid,
 				       scmd->state, scmd->owner);
-				for (sdev = shost->host_queue; sdev;
-				     sdev = sdev->next) {
+				scsi_for_each_host_sdev(&strav_hndl, sdev,
+							shost->host_no) {
 					for (scmd = sdev->device_queue; scmd;
 					     scmd = scmd->next)
 						if (scmd->request->rq_status ==
@@ -174,7 +175,7 @@
 	 * Next we detach the high level drivers from the Scsi_Device
 	 * structures
 	 */
-	for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
 		for (sdev_tp = scsi_devicelist; sdev_tp;
 		     sdev_tp = sdev_tp->next)
 			if (sdev_tp->detach)
@@ -196,16 +197,8 @@
 
 	/* Next we free up the Scsi_Cmnd structures for this host */
 
-	for (sdev = shost->host_queue; sdev;
-	     sdev = shost->host_queue) {
-		scsi_release_commandblocks(sdev);
-		blk_cleanup_queue(&sdev->request_queue);
-		/* Next free up the Scsi_Device structures for this host */
-		shost->host_queue = sdev->next;
-		if (sdev->inquiry)
-			kfree(sdev->inquiry);
-		kfree(sdev);
-	}
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no)
+		scsi_remove_scsi_device(sdev);
 
 	/* Remove the instance of the individual hosts */
 	pcount = scsi_hosts_registered;
@@ -473,6 +466,7 @@
 	struct Scsi_Device_Template *sdev_tp;
 	struct list_head *lh;
 	struct Scsi_Host *shost;
+	struct scsi_traverse_hndl strav_hndl;
 
 	INIT_LIST_HEAD(&shost_tp->shtp_list);
 
@@ -557,21 +551,27 @@
 		/*
 		 * Next we create the Scsi_Cmnd structures for this host 
 		 */
-		list_for_each(lh, &scsi_host_list) {
-			shost = list_entry(lh, struct Scsi_Host, sh_list);
-			for (sdev = shost->host_queue; sdev; sdev = sdev->next)
-				if (sdev->host->hostt == shost_tp) {
-					for (sdev_tp = scsi_devicelist;
-					     sdev_tp;
-					     sdev_tp = sdev_tp->next)
-						if (sdev_tp->attach)
-							(*sdev_tp->attach) (sdev);
-					if (sdev->attached) {
-						scsi_build_commandblocks(sdev);
-						if (sdev->current_queue_depth == 0)
-							goto out_of_space;
-					}
+		scsi_for_all_sdevs(&strav_hndl, sdev) {
+			shost = scsi_get_host(sdev);
+			/*
+			 * As long as we only support multiple paths that
+			 * all use the same adapter and driver, the attach
+			 * function will only be called once for each
+			 * Scsi_Device. Alternatively, set a flag if the
+			 * device has been attached (to upper levels), or
+			 * check current_queue_depth.
+			 */
+			if (shost && shost->hostt == shost_tp) {
+				for (sdev_tp = scsi_devicelist; sdev_tp;
+				     sdev_tp = sdev_tp->next)
+					if (sdev_tp->attach)
+						(*sdev_tp->attach) (sdev);
+				if (sdev->attached) {
+					scsi_build_commandblocks(sdev);
+					if (sdev->current_queue_depth == 0)
+						goto out_of_space;
 				}
+			}
 		}
 
 		/* This does any final handling that is required. */
@@ -743,29 +743,26 @@
 	}
 }
 
-void scsi_host_busy_inc(struct Scsi_Host *shost, Scsi_Device *sdev)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(shost->host_lock, flags);
-	shost->host_busy++;
-	sdev->device_busy++;
-	spin_unlock_irqrestore(shost->host_lock, flags);
-}
-
+/*
+ * Decrement shost->host_busy, and check if any conditions require action.
+ * Must be called with the queue_lock held. Hack to get the host_lock if
+ * host_lock and queue_lock are different.
+ */
 void scsi_host_busy_dec_and_test(struct Scsi_Host *shost, Scsi_Device *sdev)
 {
-	unsigned long flags;
+	unsigned long flags = 0;
+	request_queue_t *q = &sdev->request_queue;
 
-	spin_lock_irqsave(shost->host_lock, flags);
+	if (q->queue_lock != shost->host_lock)
+		spin_lock_irqsave(shost->host_lock, flags);
 	shost->host_busy--;
-	sdev->device_busy--;
 	if (shost->in_recovery && (shost->host_busy == shost->host_failed)) {
 		up(shost->eh_wait);
 		SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
 					  " thread\n"));
 	}
-	spin_unlock_irqrestore(shost->host_lock, flags);
+	if (q->queue_lock != shost->host_lock)
+		spin_unlock_irqrestore(shost->host_lock, flags);
 }
 
 void scsi_host_failed_inc_and_test(struct Scsi_Host *shost)
@@ -782,6 +779,30 @@
 	}
 	spin_unlock_irqrestore(shost->host_lock, flags);
 }
+
+#if defined(CONFIG_NUMA)
+/*
+ * scsihost_to_node: return the node id that *host is on. This only
+ * knows about pci devices.
+ */
+int scsihost_to_node(struct Scsi_Host *host)
+{
+        if (!host) {
+                printk(KERN_ERR "%s: host NULL\n", __FUNCTION__);
+                return 0;
+        }
+        if(host->pci_dev)
+		/*
+		 * What interface is available today?
+		 * return pcidev_to_node(host->pci_dev);
+                 * return BUS2QUAD(host->pci_dev->bus->number);
+		 */ 
+                return mp_bus_id_to_node[host->pci_dev->bus->number];
+
+        printk(KERN_INFO "%s: Unable to determine bus type\n", __FUNCTION__);
+        return 0;
+}
+#endif
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h	Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/hosts.h	Fri Oct 25 11:26:47 2002
@@ -27,6 +27,7 @@
 #include <linux/config.h>
 #include <linux/proc_fs.h>
 #include <linux/pci.h>
+#include <linux/mm.h>
 
 /* It is senseless to set SG_ALL any higher than this - the performance
  *  does not get any better, and it wastes memory
@@ -373,7 +374,6 @@
      * struct private is a way of marking it in a sort of C++ type of way.
      */
     struct list_head      sh_list;
-    Scsi_Device           * host_queue;
     struct list_head	  all_scsi_hosts;
     struct list_head	  my_devices;
 
@@ -551,9 +551,6 @@
 {
 	shost->pci_dev = pdev;
 	shost->host_driverfs_dev.parent=&pdev->dev;
-
-	/* register parent with driverfs */
-	device_register(&shost->host_driverfs_dev);
 }
 
 
@@ -592,7 +589,14 @@
 };
 
 void  scsi_initialize_queue(Scsi_Device *, struct Scsi_Host *);
-
+#if defined(CONFIG_NUMA)
+int scsihost_to_node(struct Scsi_Host *host);
+#else
+static inline int scsihost_to_node(struct Scsi_Host *host) 
+{ 
+	return 0; 
+}
+#endif
 
 /*
  * Driver registration/unregistration.
@@ -640,27 +644,6 @@
 #define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
 #define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS
 
-
-/**
- * scsi_find_device - find a device given the host
- * @shost:	SCSI host pointer
- * @channel:	SCSI channel (zero if only one channel)
- * @pun:	SCSI target number (physical unit number)
- * @lun:	SCSI Logical Unit Number
- **/
-static inline Scsi_Device *scsi_find_device(struct Scsi_Host *shost,
-                                            int channel, int pun, int lun) {
-        Scsi_Device *sdev;
-
-        for (sdev = shost->host_queue;
-            sdev != NULL;
-            sdev = sdev->next)
-                if (sdev->channel == channel && sdev->id == pun
-                   && sdev->lun ==lun)
-                        break;
-        return sdev;
-}
-    
 #endif
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c	Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi.c	Fri Oct 25 11:26:47 2002
@@ -142,7 +142,7 @@
  * Note - the initial logging level can be set here to log events at boot time.
  * After the system is up, you may enable logging via the /proc interface.
  */
-unsigned int scsi_logging_level;
+unsigned int scsi_logging_level = 0x00000140 /* XXX scan log remove */;
 
 const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
 {
@@ -318,7 +318,6 @@
 	memset(SRpnt, 0, size);
         SRpnt->sr_request = (struct request *)(((char *)SRpnt) + offset);
 	SRpnt->sr_device = device;
-	SRpnt->sr_host = device->host;
 	SRpnt->sr_magic = SCSI_REQ_MAGIC;
 	SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN;
 
@@ -381,17 +380,18 @@
  */
 
 Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, 
-                                int interruptable)
+                                int interruptable, struct scsi_path_id *path_p)
 {
  	struct Scsi_Host *host;
   	Scsi_Cmnd *SCpnt = NULL;
 	Scsi_Device *SDpnt;
 	unsigned long flags;
+	struct scsi_traverse_hndl strav_hndl;
   
   	if (!device)
   		panic("No device passed to scsi_allocate_device().\n");
   
-  	host = device->host;
+  	host = path_p->spi_shpnt;
   
 	spin_lock_irqsave(&device_request_lock, flags);
  
@@ -413,16 +413,11 @@
 				 * allow us to more easily figure out whether we should
 				 * do anything here or not.
 				 */
-				for (SDpnt = host->host_queue;
-				     SDpnt;
-				     SDpnt = SDpnt->next) {
-					/*
-					 * Only look for other devices on the same bus
-					 * with the same target ID.
-					 */
-					if (SDpnt->channel != device->channel
-					    || SDpnt->id != device->id
-					    || SDpnt == device) {
+				scsi_for_each_sdev_lun(&strav_hndl, SDpnt,
+						host->host_no,
+						path_p->spi_channel, 
+						path_p->spi_id) {
+					if (SDpnt == device) {
  						continue;
 					}
                                         if( atomic_read(&SDpnt->device_active) != 0)
@@ -513,6 +508,8 @@
 		}
 	}
 
+	scsi_path_set_scmnd_ids(SCpnt, path_p);
+
 	SCpnt->request = NULL;
 	atomic_inc(&SCpnt->host->host_active);
 	atomic_inc(&SCpnt->device->device_active);
@@ -551,6 +548,7 @@
 {
 	unsigned long flags;
         Scsi_Device * SDpnt;
+	struct Scsi_Host *shpnt;
 	int alloc_cmd = 0;
 
 	spin_lock_irqsave(&device_request_lock, flags);
@@ -601,19 +599,16 @@
 	 * device_request_lock since that's nice and quick, but allocation
 	 * can take more time so do it outside that scope instead.
 	 */
-	if(alloc_cmd) {
+	shpnt = scsi_get_host(SDpnt);
+	if(shpnt && alloc_cmd) {
 		Scsi_Cmnd *newSCpnt;
 
 		newSCpnt = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC |
-				(SDpnt->host->unchecked_isa_dma ?
+				(shpnt->unchecked_isa_dma ?
 				 GFP_DMA : 0));
 		if(newSCpnt) {
 			memset(newSCpnt, 0, sizeof(Scsi_Cmnd));
-			newSCpnt->host = SDpnt->host;
 			newSCpnt->device = SDpnt;
-			newSCpnt->target = SDpnt->id;
-			newSCpnt->lun = SDpnt->lun;
-			newSCpnt->channel = SDpnt->channel;
 			newSCpnt->request = NULL;
 			newSCpnt->use_sg = 0;
 			newSCpnt->old_use_sg = 0;
@@ -687,8 +682,14 @@
 	 */
 	if (reason == SCSI_MLQUEUE_HOST_BUSY) {
 		host->host_blocked = host->max_host_blocked;
-	} else {
+		cmd->retries = 0;
+	} else if (reason == SCSI_MLQUEUE_DEVICE_BUSY) {
                 cmd->device->device_blocked = cmd->device->max_device_blocked;
+		cmd->retries = 0;
+	} else if (reason != SCSI_MLQUEUE_RETRY) {
+		printk("%s: unexpected value of reason %d\n", __FUNCTION__,
+		       reason);
+		return 0;
 	}
 
 	/*
@@ -702,16 +703,29 @@
 	 * Decrement the counters, since these commands are no longer
 	 * active on the host/device.
 	 */
-	spin_lock_irqsave(cmd->host->host_lock, flags);
-	cmd->host->host_busy--;
-	cmd->device->device_busy--;
-	spin_unlock_irqrestore(cmd->host->host_lock, flags);
+	spin_lock_irqsave(cmd->device->request_queue.queue_lock, flags);
+	cmd->device->device_busy--; /* XXX race */
+	scsi_host_busy_dec_and_test(cmd->host, cmd->device);
+	spin_unlock_irqrestore(cmd->device->request_queue.queue_lock, flags);
 
 	/*
 	 * Insert this command at the head of the queue for it's device.
 	 * It will go before all other commands that are already in the queue.
 	 */
-	scsi_insert_special_cmd(cmd, 1);
+	if (cmd->request->flags & REQ_SPECIAL) {
+		/*
+		 * Calling this for REQ_CMD requests is fatal.
+		 */
+		scsi_insert_special_cmd(cmd, 1);
+	} else {
+		/*
+		 * Clean up the IO, and requeue it without setting cmd to
+		 * REQ_SPECIAL.
+		 */
+		scsi_cleanup_io(cmd);
+		scsi_queue_next_request(&cmd->device->request_queue, cmd,
+					cmd->host);
+	}
 	return 0;
 }
 
@@ -746,8 +760,10 @@
 {
         request_queue_t *q;
         Scsi_Device * SDpnt;
+	struct Scsi_Host * SHpnt;
 
         SDpnt = SCpnt->device;
+	SHpnt = SCpnt->host;
 
         __scsi_release_command(SCpnt);
 
@@ -758,7 +774,7 @@
          * will go on.  
          */
         q = &SDpnt->request_queue;
-        scsi_queue_next_request(q, NULL);                
+        scsi_queue_next_request(q, NULL, SHpnt); 
 }
 
 /*
@@ -833,10 +849,12 @@
 	 * We will use a queued command if possible, otherwise we will emulate the
 	 * queuing and calling of completion function ourselves.
 	 */
-	SCSI_LOG_MLQUEUE(3, printk("scsi_dispatch_cmnd (host = %d, channel = %d, target = %d, "
-	       "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n",
-	SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd,
-			    SCpnt->buffer, SCpnt->bufflen, SCpnt->done));
+	SCSI_LOG_MLQUEUE(3, 
+		printk("scsi_dispatch_cmnd (host = %d, channel = %d, target"
+		       " = %d, lun = %d,\n command = %p, buffer = %p,"
+		       " bufflen = %d, done = %p)\n", SCpnt->host->host_no,
+		       SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->cmnd,
+		       SCpnt->buffer, SCpnt->bufflen, SCpnt->done));
 
 	SCpnt->state = SCSI_STATE_QUEUED;
 	SCpnt->owner = SCSI_OWNER_LOWLEVEL;
@@ -952,30 +970,17 @@
 	      void *buffer, unsigned bufflen, void (*done) (Scsi_Cmnd *),
 		 int timeout, int retries)
 {
-	Scsi_Device * SDpnt = SRpnt->sr_device;
-	struct Scsi_Host *host = SDpnt->host;
-
-	ASSERT_LOCK(host->host_lock, 0);
-
 	SCSI_LOG_MLQUEUE(4,
 			 {
 			 int i;
-			 int target = SDpnt->id;
 			 int size = COMMAND_SIZE(((const unsigned char *)cmnd)[0]);
-			 printk("scsi_do_req (host = %d, channel = %d target = %d, "
-		    "buffer =%p, bufflen = %d, done = %p, timeout = %d, "
-				"retries = %d)\n"
-				"command : ", host->host_no, SDpnt->channel, target, buffer,
-				bufflen, done, timeout, retries);
+			 printk("scsi_do_req (buffer =%p, bufflen = %d, done = "
+				" %p, timeout = %d, retries = %d)\n command : ",
+				buffer, bufflen, done, timeout, retries);
 			 for (i	 = 0; i < size; ++i)
 			 	printk("%02x  ", ((unsigned char *) cmnd)[i]);
 			 	printk("\n");
 			 });
-
-	if (!host) {
-		panic("Invalid or not present host.\n");
-	}
-
 	/*
 	 * If the upper level driver is reusing these things, then
 	 * we should release the low-level block now.  Another one will
@@ -1343,6 +1348,15 @@
 
 				scsi_retry_command(SCpnt);
 				break;
+			case REQUEUE:
+				/*
+				 * Resend the IO, likely using a different
+				 * path.
+				 */
+				SCSI_LOG_MLCOMPLETE(3, printk("Requeuing IO %d %d 0x%x\n", SCpnt->host->host_busy,
+				SCpnt->host->host_failed, SCpnt->result));
+				scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_RETRY);
+				break;
 			case ADD_TO_MLQUEUE:
 				/* 
 				 * This typically happens for a QUEUE_FULL
@@ -1431,6 +1445,7 @@
 	struct Scsi_Host *host;
 	Scsi_Device *device;
 	Scsi_Request * SRpnt;
+	unsigned long flags;
 
 	host = SCpnt->host;
 	device = SCpnt->device;
@@ -1444,7 +1459,11 @@
          * one execution context, but the device and host structures are
          * shared.
          */
+	spin_lock_irqsave(device->request_queue.queue_lock, flags);
+	device->device_busy--;
 	scsi_host_busy_dec_and_test(host, device);
+	spin_unlock_irqrestore(device->request_queue.queue_lock, flags);
+
 
         /*
          * Clear the flags which say that the device/host is no longer
@@ -1463,7 +1482,7 @@
 		SCpnt->result |= (DRIVER_SENSE << 24);
 	}
 	SCSI_LOG_MLCOMPLETE(3, printk("Notifying upper driver of completion for device %d %x\n",
-				      SCpnt->device->id, SCpnt->result));
+				      SCpnt->target, SCpnt->result));
 
 	SCpnt->owner = SCSI_OWNER_HIGHLEVEL;
 	SCpnt->state = SCSI_STATE_FINISHED;
@@ -1533,30 +1552,34 @@
 void scsi_build_commandblocks(Scsi_Device * SDpnt)
 {
 	unsigned long flags;
+	struct Scsi_Host *host;
 	Scsi_Cmnd *SCpnt;
 
 	if (SDpnt->current_queue_depth != 0)
 		return;
 		
+	host = scsi_get_host(SDpnt);
+	/*
+	 * We are called during scan, and must always have at least one
+	 * path.
+	 */
+	BUG_ON(host == NULL);
+
 	SCpnt = (Scsi_Cmnd *) kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC |
-			(SDpnt->host->unchecked_isa_dma ? GFP_DMA : 0));
+			(host->unchecked_isa_dma ? GFP_DMA : 0));
 	if (NULL == SCpnt) {
 		/*
 		 * Since we don't currently have *any* command blocks on this
 		 * device, go ahead and try an atomic allocation...
 		 */
 		SCpnt = (Scsi_Cmnd *) kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC |
-			(SDpnt->host->unchecked_isa_dma ? GFP_DMA : 0));
+			(host->unchecked_isa_dma ? GFP_DMA : 0));
 		if (NULL == SCpnt)
 			return;	/* Oops, we aren't going anywhere for now */
 	}
 
 	memset(SCpnt, 0, sizeof(Scsi_Cmnd));
-	SCpnt->host = SDpnt->host;
 	SCpnt->device = SDpnt;
-	SCpnt->target = SDpnt->id;
-	SCpnt->lun = SDpnt->lun;
-	SCpnt->channel = SDpnt->channel;
 	SCpnt->request = NULL;
 	SCpnt->use_sg = 0;
 	SCpnt->old_use_sg = 0;
@@ -1649,10 +1672,10 @@
 			SDpnt->simple_tags = 1;
 			break;
 		default:
-			printk(KERN_WARNING "(scsi%d:%d:%d:%d) "
-				"scsi_adjust_queue_depth, bad queue type, "
-				"disabled\n", SDpnt->host->host_no,
-				SDpnt->channel, SDpnt->id, SDpnt->lun); 
+			printk(KERN_WARNING); 
+			scsi_paths_printk(SDpnt, NULL, "<%d, %d, %d, %d>");
+			printk("scsi_adjust_queue_depth, bad queue type, "
+				"disabled\n");
 		case 0:
 			SDpnt->ordered_tags = SDpnt->simple_tags = 0;
 			SDpnt->new_queue_depth = tags;
@@ -1669,46 +1692,37 @@
 static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
 {
 	Scsi_Device *scd;
-	struct Scsi_Host *HBA_ptr;
+	struct scsi_traverse_hndl strav_hndl;
 	int size, len = 0;
 	off_t begin = 0;
 	off_t pos = 0;
+	int header_output = 0;
 
-	/*
-	 * First, see if there are any attached devices or not.
-	 */
-	for (HBA_ptr = scsi_host_get_next(NULL); HBA_ptr;
-	     HBA_ptr = scsi_host_get_next(HBA_ptr)) {
-		if (HBA_ptr->host_queue != NULL) {
-			break;
+	scsi_for_all_sdevs(&strav_hndl, scd) {
+		if (!header_output) {
+			size = sprintf(buffer + len, "Attached devices:\n");
+			len += size;
+			pos = begin + len;
+			header_output = 1;
 		}
-	}
-	size = sprintf(buffer + len, "Attached devices: %s\n", (HBA_ptr) ? "" : "none");
-	len += size;
-	pos = begin + len;
-	for (HBA_ptr = scsi_host_get_next(NULL); HBA_ptr;
-	     HBA_ptr = scsi_host_get_next(HBA_ptr)) {
-#if 0
-		size += sprintf(buffer + len, "scsi%2d: %s\n", (int) HBA_ptr->host_no,
-				HBA_ptr->hostt->procname);
+		proc_print_scsidevice(scd, buffer, &size, len);
 		len += size;
 		pos = begin + len;
-#endif
-		for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
-			proc_print_scsidevice(scd, buffer, &size, len);
-			len += size;
-			pos = begin + len;
 
-			if (pos < offset) {
-				len = 0;
-				begin = pos;
-			}
-			if (pos > offset + length)
-				goto stop_output;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
 		}
+		if (pos > offset + length)
+			goto stop_output;
 	}
 
 stop_output:
+	if (!header_output) {
+		size = sprintf(buffer + len, "Attached devices: none\n");
+		len += size;
+		pos = begin + len;
+	}
 	*start = buffer + (offset - begin);	/* Start of wanted data */
 	len -= (offset - begin);	/* Start slop */
 	if (len > length)
@@ -1864,13 +1878,7 @@
 		if (!HBA_ptr)
 			goto out;
 
-		for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
-			if ((scd->channel == channel
-			     && scd->id == id
-			     && scd->lun == lun)) {
-				break;
-			}
-		}
+		scd = scsi_locate_sdev(host, channel, id, lun);
 
 		err = -ENOSYS;
 		if (scd)
@@ -1910,13 +1918,7 @@
 		if (!HBA_ptr)
 			goto out;
 
-		for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
-			if ((scd->channel == channel
-			     && scd->id == id
-			     && scd->lun == lun)) {
-				break;
-			}
-		}
+		scd = scsi_locate_sdev(host, channel, id, lun);
 
 		if (scd == NULL)
 			goto out;	/* there is no such device attached */
@@ -1933,6 +1935,10 @@
 		}
 
 		if (scd->attached == 0) {
+			scsi_remove_path(scd, SCSI_FIND_ALL_HOST_NO,
+					 SCSI_FIND_ALL_CHANNEL,
+					 SCSI_FIND_ALL_ID,
+					 SCSI_FIND_ALL_LUN);
 			/*
 			 * Nobody is using this device any more.
 			 * Free all of the command structures.
@@ -1942,22 +1948,8 @@
 			if (HBA_ptr->hostt->slave_detach)
 				(*HBA_ptr->hostt->slave_detach) (scd);
 			devfs_unregister (scd->de);
-			scsi_release_commandblocks(scd);
-
 			/* Now we can remove the device structure */
-			if (scd->next != NULL)
-				scd->next->prev = scd->prev;
-
-			if (scd->prev != NULL)
-				scd->prev->next = scd->next;
-
-			if (HBA_ptr->host_queue == scd) {
-				HBA_ptr->host_queue = scd->next;
-			}
-			blk_cleanup_queue(&scd->request_queue);
-			if (scd->inquiry)
-				kfree(scd->inquiry);
-			kfree((char *) scd);
+			scsi_remove_scsi_device(scd);
 		} else {
 			goto out;
 		}
@@ -1977,8 +1969,8 @@
 int scsi_register_device(struct Scsi_Device_Template *tpnt)
 {
 	Scsi_Device *SDpnt;
-	struct Scsi_Host *shpnt;
 	int out_of_space = 0;
+	struct scsi_traverse_hndl strav_hndl;
 
 #ifdef CONFIG_KMOD
 	if (scsi_host_get_next(NULL) == NULL)
@@ -1994,15 +1986,9 @@
 	/*
 	 * First scan the devices that we know about, and see if we notice them.
 	 */
-
-	for (shpnt = scsi_host_get_next(NULL); shpnt;
-	     shpnt = scsi_host_get_next(shpnt)) {
-		for (SDpnt = shpnt->host_queue; SDpnt;
-		     SDpnt = SDpnt->next) {
-			if (tpnt->detect)
-				SDpnt->attached += (*tpnt->detect) (SDpnt);
-		}
-	}
+	scsi_for_all_sdevs(&strav_hndl, SDpnt)
+		if (tpnt->detect)
+			SDpnt->attached += (*tpnt->detect) (SDpnt);
 
 	/*
 	 * If any of the devices would match this driver, then perform the
@@ -2015,22 +2001,24 @@
 	/*
 	 * Now actually connect the devices to the new driver.
 	 */
-	for (shpnt = scsi_host_get_next(NULL); shpnt;
-	     shpnt = scsi_host_get_next(shpnt)) {
-		for (SDpnt = shpnt->host_queue; SDpnt;
-		     SDpnt = SDpnt->next) {
-			if (tpnt->attach)
-				(*tpnt->attach) (SDpnt);
-			/*
-			 * If this driver attached to the device, and don't have any
-			 * command blocks for this device, allocate some.
-			 */
-			if (SDpnt->attached && SDpnt->current_queue_depth == 0) {
-				SDpnt->online = TRUE;
-				scsi_build_commandblocks(SDpnt);
-				if (SDpnt->current_queue_depth == 0)
-					out_of_space = 1;
-			}
+	scsi_for_all_sdevs(&strav_hndl, SDpnt) {
+		/*
+		 * We know this device cannot already be attached,
+		 * since this is the first chance we have to call
+		 * the tpnt->attach, unlike registration of the adapter
+		 * (in scsi_register_host).
+		 */
+		if (tpnt->attach)
+			(*tpnt->attach) (SDpnt);
+		/*
+		 * If this driver attached to the device, and don't have any
+		 * command blocks for this device, allocate some.
+		 */
+		if (SDpnt->attached && SDpnt->current_queue_depth == 0) {
+			SDpnt->online = TRUE;
+			scsi_build_commandblocks(SDpnt);
+			if (SDpnt->current_queue_depth == 0)
+				out_of_space = 1;
 		}
 	}
 
@@ -2051,9 +2039,10 @@
 int scsi_unregister_device(struct Scsi_Device_Template *tpnt)
 {
 	Scsi_Device *SDpnt;
-	struct Scsi_Host *shpnt;
 	struct Scsi_Device_Template *spnt;
 	struct Scsi_Device_Template *prev_spnt;
+	struct scsi_traverse_hndl strav_hndl;
+	struct Scsi_Host *shpnt;
 	
 	lock_kernel();
 	/*
@@ -2066,25 +2055,29 @@
 	 * Next, detach the devices from the driver.
 	 */
 
-	for (shpnt = scsi_host_get_next(NULL); shpnt;
-	     shpnt = scsi_host_get_next(shpnt)) {
-		for (SDpnt = shpnt->host_queue; SDpnt;
-		     SDpnt = SDpnt->next) {
-			if (tpnt->detach)
-				(*tpnt->detach) (SDpnt);
-			if (SDpnt->attached == 0) {
-				SDpnt->online = FALSE;
+	scsi_for_all_sdevs(&strav_hndl, SDpnt) {
+		if (tpnt->detach)
+			(*tpnt->detach) (SDpnt);
+		if (SDpnt->attached == 0) {
+			SDpnt->online = FALSE;
 
-				/*
-				 * Nobody is using this device any more.  Free all of the
-				 * command structures.
-				 */
-				if (shpnt->hostt->slave_detach)
-					(*shpnt->hostt->slave_detach) (SDpnt);
-				scsi_release_commandblocks(SDpnt);
-			}
+			/*
+			 * Nobody is using this device any more.  Free all of the
+			 * command structures.
+			 */
+			shpnt = scsi_get_host(SDpnt);
+			/*
+			 * XXX fix if no paths left, we still want one
+			 * detach call.  Maybe call slave_detach() when
+			 * the last path goes away, and slave_attach()
+			 * when the first path is added.
+			 */
+			if (shpnt && shpnt->hostt->slave_detach)
+				(*shpnt->hostt->slave_detach) (SDpnt);
+			scsi_release_commandblocks(SDpnt);
 		}
 	}
+
 	/*
 	 * Extract the template from the linked list.
 	 */
@@ -2137,6 +2130,8 @@
 	struct Scsi_Host *shpnt;
 	Scsi_Cmnd *SCpnt;
 	Scsi_Device *SDpnt;
+	struct scsi_traverse_hndl strav_hndl;
+
 	printk(KERN_INFO "Dump of scsi host parameters:\n");
 	i = 0;
 	for (shpnt = scsi_host_get_next(NULL); shpnt;
@@ -2151,39 +2146,36 @@
 
 	printk(KERN_INFO "\n\n");
 	printk(KERN_INFO "Dump of scsi command parameters:\n");
-	for (shpnt = scsi_host_get_next(NULL); shpnt;
-	     shpnt = scsi_host_get_next(shpnt)) {
-		printk(KERN_INFO "h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result\n");
-		for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) {
-			for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
-				/*  (0) h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result %d %x      */
-				printk(KERN_INFO "(%3d) %2d:%1d:%2d:%2d (%6s %4llu %4ld %4ld %4x %1d) (%1d %1d 0x%2x) (%4d %4d %4d) 0x%2.2x 0x%2.2x 0x%8.8x\n",
-				       i++,
-
-				       SCpnt->host->host_no,
-				       SCpnt->channel,
-                                       SCpnt->target,
-                                       SCpnt->lun,
-
-                                       kdevname(SCpnt->request->rq_dev),
-                                       (unsigned long long)SCpnt->request->sector,
-				       SCpnt->request->nr_sectors,
-				       (long)SCpnt->request->current_nr_sectors,
-				       SCpnt->request->rq_status,
-				       SCpnt->use_sg,
-
-				       SCpnt->retries,
-				       SCpnt->allowed,
-				       SCpnt->flags,
-
-				       SCpnt->timeout_per_command,
-				       SCpnt->timeout,
-				       SCpnt->internal_timeout,
-
-				       SCpnt->cmnd[0],
-				       SCpnt->sense_buffer[2],
-				       SCpnt->result);
-			}
+	printk(KERN_INFO "h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result\n");
+	scsi_for_all_sdevs(&strav_hndl, SDpnt) {
+		for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+			/*  (0) h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result %d %x      */
+			printk(KERN_INFO "(%3d) %2d:%1d:%2d:%2d (%6s %4llu %4ld %4ld %4x %1d) (%1d %1d 0x%2x) (%4d %4d %4d) 0x%2.2x 0x%2.2x 0x%8.8x\n",
+			       i++,
+
+			       SCpnt->host->host_no,
+			       SCpnt->channel,
+			       SCpnt->target,
+			       SCpnt->lun,
+
+			       kdevname(SCpnt->request->rq_dev),
+			       (unsigned long long)SCpnt->request->sector,
+			       SCpnt->request->nr_sectors,
+			       (long)SCpnt->request->current_nr_sectors,
+			       SCpnt->request->rq_status,
+			       SCpnt->use_sg,
+
+			       SCpnt->retries,
+			       SCpnt->allowed,
+			       SCpnt->flags,
+
+			       SCpnt->timeout_per_command,
+			       SCpnt->timeout,
+			       SCpnt->internal_timeout,
+
+			       SCpnt->cmnd[0],
+			       SCpnt->sense_buffer[2],
+			       SCpnt->result);
 		}
 	}
 #endif	/* CONFIG_SCSI_LOGGING */ /* } */
@@ -2326,6 +2318,7 @@
 		return -ENOMEM;
 	}
 	generic->write_proc = proc_scsi_gen_write;
+	scsi_paths_proc_init();
 #endif
 
         scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL);
@@ -2349,6 +2342,7 @@
 	scsi_host_hn_release();
 
 #ifdef CONFIG_PROC_FS
+	scsi_paths_proc_cleanup();
 	/* No, we're not here anymore. Don't show the /proc/scsi files. */
 	remove_proc_entry ("scsi/scsi", 0);
 	remove_proc_entry ("scsi", 0);
@@ -2399,19 +2393,25 @@
         if(SDpnt == NULL)
         	return NULL;
         	
+	scsi_add_scsi_device(SDpnt, SHpnt);
         memset(SDpnt, 0, sizeof(Scsi_Device));
 	SDpnt->vendor = scsi_null_device_strs;
 	SDpnt->model = scsi_null_device_strs;
 	SDpnt->rev = scsi_null_device_strs;
 
-        SDpnt->host = SHpnt;
-        SDpnt->id = SHpnt->this_id;
         SDpnt->type = -1;
+	/*
+	 * add a path: host is SHpnt, channel 0, id is SHpnt->this_id, lun 0
+	 */
+	if (scsi_add_path(SDpnt, SHpnt, 0, SHpnt->this_id, 0)) {
+		scsi_remove_scsi_device(SDpnt);
+		return NULL;
+	}
 	SDpnt->new_queue_depth = 1;
         
 	scsi_build_commandblocks(SDpnt);
 	if(SDpnt->current_queue_depth == 0) {
-		kfree(SDpnt);
+		scsi_remove_scsi_device(SDpnt);
 		return NULL;
 	}
 
@@ -2441,21 +2441,14 @@
  */
 void scsi_free_host_dev(Scsi_Device * SDpnt)
 {
-        if( (unsigned char) SDpnt->id != (unsigned char) SDpnt->host->this_id )
-        {
+	struct scsi_path_id scsi_path;
+
+	scsi_get_path(SDpnt, &scsi_path);
+	if ((unsigned char) scsi_path.spi_id != (unsigned char)
+	    scsi_path.spi_shpnt->this_id) {
                 panic("Attempt to delete wrong device\n");
         }
-
-        blk_cleanup_queue(&SDpnt->request_queue);
-
-        /*
-         * We only have a single SCpnt attached to this device.  Free
-         * it now.
-         */
-	scsi_release_commandblocks(SDpnt);
-	if (SDpnt->inquiry)
-		kfree(SDpnt->inquiry);
-        kfree(SDpnt);
+	scsi_remove_scsi_device(SDpnt);
 }
 
 /*
@@ -2493,14 +2486,20 @@
 	Scsi_Cmnd SC, *SCpnt = &SC;
         struct request req;
 	int rtn;
+	struct scsi_path_id scsi_path;
 
         SCpnt->request = &req;
 	memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout));
-	SCpnt->host                    	= dev->host;
+	scsi_get_path(dev, &scsi_path);
+	/*
+	 * XXX this is broken if we have multiple paths and we want to
+	 * reset the bus or the adapter on all paths to dev.
+	 */
+	SCpnt->host                    	= scsi_path.spi_shpnt;
 	SCpnt->device                  	= dev;
-	SCpnt->target                  	= dev->id;
-	SCpnt->lun                     	= dev->lun;
-	SCpnt->channel                 	= dev->channel;
+	SCpnt->target                  	= scsi_path.spi_id;
+	SCpnt->lun                     	= scsi_path.spi_lun;
+	SCpnt->channel                 	= scsi_path.spi_channel;
 	SCpnt->request->rq_status      	= RQ_SCSI_BUSY;
 	SCpnt->request->waiting        	= NULL;
 	SCpnt->use_sg                  	= 0;
diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
--- a/drivers/scsi/scsi.h	Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi.h	Fri Oct 25 11:26:47 2002
@@ -133,12 +133,14 @@
 #define sense_error(sense)  ((sense) & 0xf)
 #define sense_valid(sense)  ((sense) & 0x80);
 
+#define	UNKNOWN_ERROR	0x0000
 #define NEEDS_RETRY     0x2001
 #define SUCCESS         0x2002
 #define FAILED          0x2003
 #define QUEUED          0x2004
 #define SOFT_ERROR      0x2005
 #define ADD_TO_MLQUEUE  0x2006
+#define REQUEUE  	0x2007
 
 /*
  * These are the values that scsi_cmd->state can take.
@@ -387,6 +389,13 @@
 #define SYNC_RESET      0x40
 
 /*
+ * Prefix values for the SCSI id's. These are stored in the first byte of the
+ * driverfs name field, see scsi_scan.c.
+ */
+#define SCSI_UID_SER_NUM 'S'
+#define SCSI_UID_UNKNOWN 'Z'
+
+/*
  * This is the crap from the old error handling code.  We have it in a special
  * place so that we can more easily delete it later on.
  */
@@ -398,6 +407,7 @@
 typedef struct scsi_device Scsi_Device;
 typedef struct scsi_cmnd Scsi_Cmnd;
 typedef struct scsi_request Scsi_Request;
+struct scsi_path_id;
 
 #define SCSI_CMND_MAGIC 0xE25C23A5
 #define SCSI_REQ_MAGIC  0x75F6D354
@@ -428,7 +438,7 @@
 			   void (*complete) (Scsi_Cmnd *));
 extern int scsi_delete_timer(Scsi_Cmnd * SCset);
 extern void scsi_error_handler(void *host);
-extern int scsi_decide_disposition(Scsi_Cmnd * SCpnt);
+extern int scsi_check_sense(Scsi_Cmnd * SCpnt);
 extern int scsi_block_when_processing_errors(Scsi_Device *);
 extern void scsi_sleep(int);
 
@@ -458,10 +468,12 @@
  */
 extern void scsi_initialize_merge_fn(Scsi_Device *SDpnt);
 extern int scsi_init_io(Scsi_Cmnd *SCpnt);
+extern void scsi_cleanup_io(Scsi_Cmnd *SCpnt);
 
 /*
  * Prototypes for functions in scsi_lib.c
  */
+struct Scsi_Host;
 extern int scsi_maybe_unblock_host(Scsi_Device * SDpnt);
 extern Scsi_Cmnd *scsi_end_request(Scsi_Cmnd * SCpnt, int uptodate,
 				   int sectors);
@@ -471,9 +483,12 @@
 extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int);
 extern void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
 			       int block_sectors);
-extern void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt);
+extern void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt,
+				    struct Scsi_Host * SHpnt);
 extern void scsi_request_fn(request_queue_t * q);
 extern int scsi_starvation_completion(Scsi_Device * SDpnt);
+extern void scsi_add_scsi_device(Scsi_Device *SDpnt, struct Scsi_Host *SHpnt);
+extern void scsi_remove_scsi_device(Scsi_Device *SDpnt);
 
 /*
  * Prototypes for functions in scsi.c
@@ -486,7 +501,8 @@
 extern void scsi_done(Scsi_Cmnd * SCpnt);
 extern void scsi_finish_command(Scsi_Cmnd *);
 extern int scsi_retry_command(Scsi_Cmnd *);
-extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int);
+extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int,
+				       struct scsi_path_id *);
 extern void __scsi_release_command(Scsi_Cmnd *);
 extern void scsi_release_command(Scsi_Cmnd *);
 extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd,
@@ -495,6 +511,9 @@
 			int timeout, int retries);
 extern int scsi_dev_init(void);
 
+extern int scsi_paths_proc_print_paths(Scsi_Device *SDpnt, char *buffer,
+				       char *format);
+
 /*
  * Newer request-based interfaces.
  */
@@ -539,6 +558,17 @@
 extern const char *scsi_extd_sense_format(unsigned char, unsigned char);
 
 /*
+ * The scsi_path_id struct contains the basic description of a path. IO to any
+ * logical unit (Scsi_Device) must use all four of these elements.
+ */
+struct scsi_path_id {
+	struct Scsi_Host *spi_shpnt;	/* Host adapter to use */
+	unsigned int spi_channel;	/* channel on the host */
+	unsigned int spi_id;		/* target id on the channel */
+	unsigned int spi_lun;		/* LUN on the target */
+};
+
+/*
  *  The scsi_device struct contains what we know about each given scsi
  *  device.
  *
@@ -555,13 +585,18 @@
 	/*
 	 * This information is private to the scsi mid-layer.
 	 */
-	struct scsi_device *next;	/* Used for linked list */
-	struct scsi_device *prev;	/* Used for linked list */
+	/*
+	 * Only the SCSI mid-layer routines should access sdev_next and 
+	 * sdev_prev. If you need to traverse through scsi_device's, use
+	 * one of the scsi traverse functions - adapter drivers probably want
+	 * to use scsi_for_each_host_sdev.
+	 */
+	struct scsi_device *sdev_next;	/* Used for linked list */
+	struct scsi_device *sdev_prev;	/* Used for linked list */
 	struct list_head    siblings;   /* list of all devices on this host */
 	struct list_head    same_target_siblings; /* just the devices sharing same target id */
 	wait_queue_head_t   scpnt_wait;	/* Used to wait if
 					   device is busy */
-	struct Scsi_Host *host;
 	request_queue_t request_queue;
         atomic_t                device_active; /* commands checked out for device */
 	volatile unsigned short device_busy;	/* commands actually active on low-level */
@@ -569,11 +604,9 @@
 	struct list_head busy_cmnds;    /* list of Scsi_Cmnd structs in use */
 	Scsi_Cmnd *device_queue;	/* queue of SCSI Command structures */
         Scsi_Cmnd *current_cmnd;	/* currently active command */
+ 	void	*sdev_paths;
 	unsigned short current_queue_depth;/* How deep of a queue we have */
 	unsigned short new_queue_depth; /* How deep of a queue we want */
-
-	unsigned int id, lun, channel;
-
 	unsigned int manufacturer;	/* Manufacturer of device, for using 
 					 * vendor-specific cmd's */
 	unsigned sector_size;	/* size in bytes */
@@ -626,6 +659,7 @@
 	unsigned remap:1;	/* support remapping  */
 	unsigned starved:1;	/* unable to process commands because
 				   host busy */
+	unsigned scanning:1;	/* set while scanning */
 //	unsigned sync:1;	/* Sync transfer state, managed by host */
 //	unsigned wide:1;	/* WIDE transfer state, managed by host */
 
@@ -635,7 +669,6 @@
   	/* default value if the device doesn't override */
 	#define SCSI_DEFAULT_DEVICE_BLOCKED	3
 
-
 	// Flag to allow revalidate to succeed in sd_open
 	int allow_revalidate;
 	struct device sdev_driverfs_dev;
@@ -678,7 +711,6 @@
 						 * received on original command 
 						 * (auto-sense) */
 
-	struct Scsi_Host *sr_host;
 	Scsi_Device *sr_device;
 	Scsi_Cmnd *sr_command;
 	struct request *sr_request;	/* A copy of the command we are
@@ -753,6 +785,10 @@
 	struct scsi_cmnd *bh_next;	/* To enumerate the commands waiting 
 					   to be processed. */
 
+	/*
+	 * XXX a scsi_path_id should eventually replace the following,
+	 * including the *host.
+	 */
 	unsigned int target;
 	unsigned int lun;
 	unsigned int channel;
@@ -847,6 +883,7 @@
  */
 #define SCSI_MLQUEUE_HOST_BUSY   0x1055
 #define SCSI_MLQUEUE_DEVICE_BUSY 0x1056
+#define SCSI_MLQUEUE_RETRY       0x1057
 
 #define SCSI_SLEEP(QUEUE, CONDITION) {		    \
     if (CONDITION) {			            \
@@ -867,6 +904,72 @@
 	current->state = TASK_RUNNING;	\
     }; }
 
+struct scsi_traverse_hndl {
+	Scsi_Device *next_sdev;
+	struct Scsi_Host *cur_shost;
+};
+
+extern Scsi_Device *scsi_traverse_sdevs(struct scsi_traverse_hndl *handle,
+					uint host_no, uint channel, uint id,
+					uint lun);
+/*
+ * Values used for scsi_find_lun_start()
+ */
+#define SCSI_FIND_ALL_HOST_NO	0xffffffff
+#define SCSI_FIND_ALL_CHANNEL	0xffffffff
+#define SCSI_FIND_ALL_ID	0xffffffff
+#define SCSI_FIND_ALL_LUN	0xffffffff
+
+#define __INIT_TRAVERSE_HNDL(name) \
+	(name)->next_sdev = scsi_sdev_list
+
+#define SCSI_TRAVERSE_ALL_SDEVS(hndl) \
+	scsi_traverse_sdevs(hndl, SCSI_FIND_ALL_HOST_NO, \
+		SCSI_FIND_ALL_CHANNEL, SCSI_FIND_ALL_ID, \
+		SCSI_FIND_ALL_LUN)
+
+#define scsi_for_all_sdevs(hndl, sdev) \
+	for (__INIT_TRAVERSE_HNDL(hndl), sdev = SCSI_TRAVERSE_ALL_SDEVS(hndl); \
+	     sdev != NULL; sdev = SCSI_TRAVERSE_ALL_SDEVS(hndl))
+
+#define SCSI_TRAVERSE_HOST_SDEVS(hndl, host) \
+	scsi_traverse_sdevs(hndl, host, SCSI_FIND_ALL_CHANNEL, \
+			    SCSI_FIND_ALL_ID, SCSI_FIND_ALL_LUN)
+
+#define scsi_for_each_host_sdev(hndl, sdev, host) \
+	for (__INIT_TRAVERSE_HNDL(hndl), \
+	     sdev = SCSI_TRAVERSE_HOST_SDEVS(hndl, host); sdev != NULL; \
+	     sdev = SCSI_TRAVERSE_HOST_SDEVS(hndl, host))
+
+#define SCSI_TRAVERSE_HOST_CHAN_SDEVS(hndl, host, chan) \
+	scsi_traverse_sdevs(hndl, host, chan, SCSI_FIND_ALL_ID, \
+			    SCSI_FIND_ALL_LUN)
+
+#define scsi_for_each_host_chan_sdev(hndl, sdev, host, chan) \
+	for (__INIT_TRAVERSE_HNDL(hndl), \
+	     sdev = SCSI_TRAVERSE_HOST_CHAN_SDEVS(hndl, host, chan); \
+	     sdev != NULL; \
+	     sdev = SCSI_TRAVERSE_HOST_CHAN_SDEVS(hndl, host, chan))
+
+#define SCSI_TRAVERSE_SDEV_LUNS(hndl, host, chan, id) \
+	scsi_traverse_sdevs(hndl, host, chan, id, SCSI_FIND_ALL_LUN)
+
+#define scsi_for_each_sdev_lun(hndl, sdev, host, chan, id) \
+	for (__INIT_TRAVERSE_HNDL(hndl), \
+	     sdev = SCSI_TRAVERSE_SDEV_LUNS(hndl, host, chan, id); \
+	     sdev != NULL; \
+	     sdev = SCSI_TRAVERSE_SDEV_LUNS(hndl, host, chan, id))
+
+
+#define scsi_for_each_sdev(hndl, sdev, host, chan, id, lun) \
+	for (__INIT_TRAVERSE_HNDL(hndl), \
+	     sdev = scsi_traverse_sdevs(hndl, host, chan, id, lun); \
+	     sdev != NULL; \
+	     sdev = scsi_traverse_sdevs(hndl, host, chan, id, lun))
+
+#define scsi_locate_sdev(host, chan, id, lun) \
+	scsi_traverse_sdevs((struct scsi_traverse_hndl *) NULL, host, chan, \
+			    id, lun)
 /*
  * old style reset request from external source
  * (private to sg.c and scsi_error.c, supplied by scsi_obsolete.c)
@@ -960,6 +1063,33 @@
 
         return (Scsi_Cmnd *)req->special;
 }
+
+extern struct scsi_device *scsi_sdev_list;
+
+extern int scsi_paths_proc_init( void );
+extern void scsi_paths_proc_cleanup( void );
+extern struct Scsi_Host *scsi_get_host(Scsi_Device *SDpnt);
+extern int scsi_get_path(Scsi_Device *SDpnt, struct scsi_path_id *pathp);
+extern int scsi_get_best_path(Scsi_Device *SDpnt, struct scsi_path_id *pathp,
+			      struct request *);
+extern int scsi_decide_disposition(struct scsi_cmnd *);
+extern int scsi_add_path(Scsi_Device *SDpnt, struct Scsi_Host *shpnt,
+			 unsigned int channel, unsigned int dev,
+			 unsigned int lun);
+extern void  scsi_remove_path(Scsi_Device *SDpnt, unsigned int host_no,
+			      unsigned int channel, unsigned int dev,
+			      unsigned int lun);
+extern Scsi_Device *scsi_lookup_id(char *id, Scsi_Device *sdev);
+extern void scsi_replace_path(Scsi_Device *sdev, struct Scsi_Host *shost,
+	unsigned int channel, unsigned int id, unsigned int lun);
+
+extern void scsi_paths_printk(Scsi_Device *SDpnt, char *prefix, char *format);
+
+#define scsi_path_set_scmnd_ids(scp, pathp) do {		 	\
+	scp->host = pathp->spi_shpnt;			\
+	scp->target = pathp->spi_id;			\
+	scp->lun = pathp->spi_lun; 				\
+	scp->channel = pathp->spi_channel; } while(0)
 
 #define scsi_eh_eflags_chk(scp, flags) (scp->eh_eflags & flags)
 
diff -Nru a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c	Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi_error.c	Fri Oct 25 11:26:47 2002
@@ -133,37 +133,6 @@
 }
 
 /**
- * scsi_times_out - Timeout function for normal scsi commands.
- * @scmd:	Cmd that is timing out.
- *
- * Notes:
- *     We do not need to lock this.  There is the potential for a race
- *     only in that the normal completion handling might run, but if the
- *     normal completion function determines that the timer has already
- *     fired, then it mustn't do anything.
- **/
-void scsi_times_out(Scsi_Cmnd *scmd)
-{
-	/* Set the serial_number_at_timeout to the current serial_number */
-	scmd->serial_number_at_timeout = scmd->serial_number;
-
-	scsi_eh_eflags_set(scmd, SCSI_EH_CMD_TIMEOUT | SCSI_EH_CMD_ERR);
-
-	if( scmd->host->eh_wait == NULL ) {
-		panic("Error handler thread not present at %p %p %s %d",
-		      scmd, scmd->host, __FILE__, __LINE__);
-	}
-
-	scsi_host_failed_inc_and_test(scmd->host);
-
-	SCSI_LOG_TIMEOUT(3, printk("Command timed out active=%d busy=%d "
-				   " failed=%d\n",
-				   atomic_read(&scmd->host->host_active),
-				   scmd->host->host_busy,
-				   scmd->host->host_failed));
-}
-
-/**
  * scsi_block_when_processing_errors - Prevent cmds from being queued.
  * @sdev:	Device on which we are performing recovery.
  *
@@ -177,12 +146,15 @@
  **/
 int scsi_block_when_processing_errors(Scsi_Device *sdev)
 {
-
-	SCSI_SLEEP(&sdev->host->host_wait, sdev->host->in_recovery);
-
 	SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __FUNCTION__,
 					  sdev->online));
-
+	/*
+	 * If a device is in_recovery, IO will be not be processed when
+	 * scsi_request_fn is called. We used to sleep here, doing so
+	 * raced with the setting of in_recovery, but prevented lots of IO
+	 * from queueing up after the open returns, but did not prevent IO
+	 * from queueing up for already open devices.
+	 */
 	return sdev->online;
 }
 
@@ -200,9 +172,9 @@
 	int cmd_failed = 0;
 	int cmd_timed_out = 0;
 	int devices_failed = 0;
+	struct scsi_traverse_hndl strav_hndl;
 
-
-	for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
 		for (scmd = sc_list; scmd; scmd = scmd->bh_next) {
 			if (scmd->device == sdev) {
 				++total_failures;
@@ -215,12 +187,13 @@
 		}
 
 		if (cmd_timed_out || cmd_failed) {
-			SCSI_LOG_ERROR_RECOVERY(3,
-				printk("%s: %d:%d:%d:%d cmds failed: %d,"
-				       " timedout: %d\n",
-				       __FUNCTION__, shost->host_no,
-				       sdev->channel, sdev->id, sdev->lun,
-				       cmd_failed, cmd_timed_out));
+			SCSI_LOG_ERROR_RECOVERY(3, {
+				printk("scsi_eh: device at ");
+				scsi_paths_printk(sdev, " ",
+						  "<%d, %d, %d, %d>");
+				printk(" cmds failed: %d, timed out: %d\n",
+				       cmd_failed, cmd_timed_out); 
+			});
 			cmd_timed_out = 0;
 			cmd_failed = 0;
 			++devices_failed;
@@ -246,8 +219,10 @@
 	int found;
 	Scsi_Device *sdev;
 	Scsi_Cmnd *scmd;
+	struct scsi_traverse_hndl strav_hndl;
 
-	for (found = 0, sdev = shost->host_queue; sdev; sdev = sdev->next) {
+	found = 0;
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
 		for (scmd = sdev->device_queue; scmd; scmd = scmd->next) {
 			if (scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) {
 				scmd->bh_next = *sc_list;
@@ -297,7 +272,7 @@
  * Return value:
  * 	SUCCESS or FAILED or NEEDS_RETRY
  **/
-static int scsi_check_sense(Scsi_Cmnd *scmd)
+int scsi_check_sense(Scsi_Cmnd *scmd)
 {
 	if (!SCSI_SENSE_VALID(scmd)) {
 		return FAILED;
@@ -823,6 +798,73 @@
 }
 
 /**
+ * scsi_times_out - Timeout function for normal scsi commands.
+ * @scmd:	Cmd that is timing out.
+ *
+ * Notes:
+ *     We do not need to lock this.  There is the potential for a race
+ *     only in that the normal completion handling might run, but if the
+ *     normal completion function determines that the timer has already
+ *     fired, then it mustn't do anything.
+ **/
+void scsi_times_out(Scsi_Cmnd *scmd)
+{
+
+	/*
+	 * We can get here with DID_ERROR set, meaning someone has touched
+	 * the result. For qlogicfc, it looks like it might change the
+	 * result, even though it does not let us know the IO has
+	 * completed (on loop down)! So, don't just or in DID_TIME_OUT.
+	 *
+	 * XXX maybe only set DRIVER_TIMEOUT? We then need changes in
+	 * scsi_decide_disposition.
+	 */
+	scmd->result = (scmd->result & 0xff00ffff) | (DID_TIME_OUT << 16) |
+		(DRIVER_TIMEOUT << 24);
+	if (scsi_decide_disposition(scmd) == REQUEUE) {
+#ifdef WILL_NOT_WORK
+		/*
+		 * XXX Ideally, abort the command, and then requeue it.
+		 * The current adapter abort command (eh_abort_handler) is
+		 * optional, and is allowed to block, so it will not work
+		 * here. Since we need to effectively reuse scmd, and we
+		 * can't tell if it is still used by the adapter, the only
+		 * solution for now is to allow the standard error handler
+		 * to kick in and retry the command - with the current
+		 * error handling code, this means the command will likely
+		 * not be retried or even failed for quite a bit of time,
+		 * maybe minutes.
+		 */
+		(void)scsi_try_to_abort_cmd(scmd);
+		SCSI_LOG_TIMEOUT(3, 
+			printk("Requeuing timed out IO host %d channel %d id % d lun %d; result 0x%x\n",
+			       scmd->host->host_no, scmd->channel,
+			       scmd->target, scmd->lun, scmd->result));
+		scsi_mlqueue_insert(scmd, SCSI_MLQUEUE_RETRY);
+		return;
+#endif
+	}
+
+	/* Set the serial_number_at_timeout to the current serial_number */
+	scmd->serial_number_at_timeout = scmd->serial_number;
+
+	scsi_eh_eflags_set(scmd, SCSI_EH_CMD_TIMEOUT | SCSI_EH_CMD_ERR);
+
+	if( scmd->host->eh_wait == NULL ) {
+		panic("Error handler thread not present at %p %p %s %d",
+		      scmd, scmd->host, __FILE__, __LINE__);
+	}
+
+	scsi_host_failed_inc_and_test(scmd->host);
+
+	SCSI_LOG_TIMEOUT(3, printk("Command timed out active=%d busy=%d "
+				   "failed=%d\n",
+				   atomic_read(&scmd->host->host_active),
+				   scmd->host->host_busy,
+				   scmd->host->host_failed));
+}
+
+/**
  * scsi_eh_tur - Send TUR to device.
  * @scmd:	Scsi cmd to send TUR
  *
@@ -959,10 +1001,11 @@
 	int rtn;
 	Scsi_Cmnd *scmd;
 	Scsi_Device *sdev;
+	struct scsi_traverse_hndl strav_hndl;
 
 	SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Trying BDR\n", __FUNCTION__));
 
-	for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
 		for (scmd = sc_todo; scmd; scmd = scmd->bh_next)
 			if ((scmd->device == sdev) &&
 			    scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
@@ -998,6 +1041,7 @@
 	unsigned long flags;
 	int rtn;
 	Scsi_Device *sdev;
+	struct scsi_traverse_hndl strav_hndl;
 
 	SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
 					  __FUNCTION__));
@@ -1016,11 +1060,11 @@
 		/*
 		 * Mark all affected devices to expect a unit attention.
 		 */
-		for (sdev = scmd->host->host_queue; sdev; sdev = sdev->next)
-			if (scmd->channel == sdev->channel) {
-				sdev->was_reset = 1;
-				sdev->expecting_cc_ua = 1;
-			}
+		scsi_for_each_host_chan_sdev(&strav_hndl, sdev,
+		     scmd->host->host_no, scmd->channel) {
+			sdev->was_reset = 1;
+			sdev->expecting_cc_ua = 1;
+		}
 	}
 	return rtn;
 }
@@ -1033,7 +1077,8 @@
 {
 	unsigned long flags;
 	int rtn;
-	 Scsi_Device *sdev;
+	Scsi_Device *sdev;
+	struct scsi_traverse_hndl strav_hndl;
 
 	SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
 					  __FUNCTION__));
@@ -1052,11 +1097,11 @@
 		/*
 		 * Mark all affected devices to expect a unit attention.
 		 */
-		for (sdev = scmd->host->host_queue; sdev; sdev = sdev->next)
-			if (scmd->channel == sdev->channel) {
-				sdev->was_reset = 1;
-				sdev->expecting_cc_ua = 1;
-			}
+		scsi_for_each_host_chan_sdev(&strav_hndl, sdev,
+		     scmd->host->host_no, scmd->channel) {
+			sdev->was_reset = 1;
+			sdev->expecting_cc_ua = 1;
+		}
 	}
 	return rtn;
 }
@@ -1109,7 +1154,7 @@
 		 * we now know that we are able to perform a reset for the
 		 * channel that scmd points to.
 		 */
-		rtn = scsi_try_bus_reset(scmd);
+		rtn = scsi_try_bus_reset(scmd); 
 		if (rtn != SUCCESS)
 			rtn = scsi_try_host_reset(scmd);
 
@@ -1145,14 +1190,11 @@
 		if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
 			continue;
 
-		printk(KERN_INFO "%s: Device offlined - not"
-				" ready or command retry failed"
-				" after error recovery: host"
-				" %d channel %d id %d lun %d\n",
-				__FUNCTION__, shost->host_no,
-				scmd->device->channel,
-				scmd->device->id,
-				scmd->device->lun);
+		printk(KERN_INFO "%s: device at ", __FUNCTION__);
+		scsi_paths_printk(scmd->device,
+				  " ", "<%d, %d, %d, %d>");
+		printk(" set offline - not ready or command retry"
+		       " failed after bus and host reset\n");
 		scmd->device->online = FALSE;
 		scsi_eh_finish_cmd(scmd, shost);
 	}
@@ -1197,175 +1239,6 @@
 }
 
 /**
- * scsi_decide_disposition - Disposition a cmd on return from LLD.
- * @scmd:	SCSI cmd to examine.
- *
- * Notes:
- *    This is *only* called when we are examining the status after sending
- *    out the actual data command.  any commands that are queued for error
- *    recovery (i.e. test_unit_ready) do *not* come through here.
- *
- *    When this routine returns failed, it means the error handler thread
- *    is woken.  in cases where the error code indicates an error that
- *    doesn't require the error handler read (i.e. we don't need to
- *    abort/reset), then this function should return SUCCESS.
- **/
-int scsi_decide_disposition(Scsi_Cmnd *scmd)
-{
-	int rtn;
-
-	/*
-	 * if the device is offline, then we clearly just pass the result back
-	 * up to the top level.
-	 */
-	if (scmd->device->online == FALSE) {
-		SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report"
-						  " as SUCCESS\n",
-						  __FUNCTION__));
-		return SUCCESS;
-	}
-	/*
-	 * first check the host byte, to see if there is anything in there
-	 * that would indicate what we need to do.
-	 */
-
-	switch (host_byte(scmd->result)) {
-	case DID_PASSTHROUGH:
-		/*
-		 * no matter what, pass this through to the upper layer.
-		 * nuke this special code so that it looks like we are saying
-		 * did_ok.
-		 */
-		scmd->result &= 0xff00ffff;
-		return SUCCESS;
-	case DID_OK:
-		/*
-		 * looks good.  drop through, and check the next byte.
-		 */
-		break;
-	case DID_NO_CONNECT:
-	case DID_BAD_TARGET:
-	case DID_ABORT:
-		/*
-		 * note - this means that we just report the status back
-		 * to the top level driver, not that we actually think
-		 * that it indicates SUCCESS.
-		 */
-		return SUCCESS;
-		/*
-		 * when the low level driver returns did_soft_error,
-		 * it is responsible for keeping an internal retry counter 
-		 * in order to avoid endless loops (db)
-		 *
-		 * actually this is a bug in this function here.  we should
-		 * be mindful of the maximum number of retries specified
-		 * and not get stuck in a loop.
-		 */
-	case DID_SOFT_ERROR:
-		goto maybe_retry;
-
-	case DID_ERROR:
-		if (msg_byte(scmd->result) == COMMAND_COMPLETE &&
-		    status_byte(scmd->result) == RESERVATION_CONFLICT)
-			/*
-			 * execute reservation conflict processing code
-			 * lower down
-			 */
-			break;
-		/* fallthrough */
-
-	case DID_BUS_BUSY:
-	case DID_PARITY:
-		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.
-		 */
-		if ((scmd->cmnd[0] == TEST_UNIT_READY ||
-		     scmd->cmnd[0] == INQUIRY)) {
-			return SUCCESS;
-		} else {
-			return FAILED;
-		}
-	case DID_RESET:
-		/*
-		 * in the normal case where we haven't initiated a reset,
-		 * this is a failure.
-		 */
-		if (scmd->flags & IS_RESETTING) {
-			scmd->flags &= ~IS_RESETTING;
-			goto maybe_retry;
-		}
-		return SUCCESS;
-	default:
-		return FAILED;
-	}
-
-	/*
-	 * next, check the message byte.
-	 */
-	if (msg_byte(scmd->result) != COMMAND_COMPLETE) {
-		return FAILED;
-	}
-	/*
-	 * now, check the status byte to see if this indicates anything special.
-	 */
-	switch (status_byte(scmd->result)) {
-	case QUEUE_FULL:
-		/*
-		 * the case of trying to send too many commands to a
-		 * tagged queueing device.
-		 */
-	case BUSY:
-		/*
-		 * device can't talk to us at the moment.  Should only
-		 * occur (SAM-3) when the task queue is empty, so will cause
-		 * the empty queue handling to trigger a stall in the
-		 * device.
-		 */
-		return ADD_TO_MLQUEUE;
-	case GOOD:
-	case COMMAND_TERMINATED:
-		return SUCCESS;
-	case CHECK_CONDITION:
-		rtn = scsi_check_sense(scmd);
-		if (rtn == NEEDS_RETRY) {
-			goto maybe_retry;
-		}
-		return rtn;
-	case CONDITION_GOOD:
-	case INTERMEDIATE_GOOD:
-	case INTERMEDIATE_C_GOOD:
-		/*
-		 * who knows?  FIXME(eric)
-		 */
-		return SUCCESS;
-
-	case RESERVATION_CONFLICT:
-		printk("scsi%d (%d,%d,%d) : reservation conflict\n",
-		       scmd->host->host_no, scmd->channel,
-		       scmd->device->id, scmd->device->lun);
-		return SUCCESS; /* causes immediate i/o error */
-	default:
-		return FAILED;
-	}
-	return FAILED;
-
-      maybe_retry:
-
-	if ((++scmd->retries) < scmd->allowed) {
-		return NEEDS_RETRY;
-	} else {
-		/*
-		 * no more retries - report this one back to upper level.
-		 */
-		return SUCCESS;
-	}
-}
-
-/**
  * scsi_eh_lock_done - done function for eh door lock request
  * @scmd:	SCSI command block for the door lock request
  *
@@ -1445,7 +1318,7 @@
 static void scsi_restart_operations(struct Scsi_Host *shost)
 {
 	Scsi_Device *sdev;
-	unsigned long flags;
+	struct scsi_traverse_hndl strav_hndl;
 
 	ASSERT_LOCK(shost->host_lock, 0);
 
@@ -1454,7 +1327,7 @@
 	 * onto the head of the SCSI request queue for the device.  There
 	 * is no point trying to lock the door of an off-line device.
 	 */
-	for (sdev = shost->host_queue; sdev; sdev = sdev->next)
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no)
 		if (sdev->online && sdev->locked)
 			scsi_eh_lock_door(sdev);
 
@@ -1474,20 +1347,12 @@
 	 * now that error recovery is done, we will need to ensure that these
 	 * requests are started.
 	 */
-	spin_lock_irqsave(shost->host_lock, flags);
-	for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+	scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
 		request_queue_t *q = &sdev->request_queue;
-
-		if ((shost->can_queue > 0 &&
-		     (shost->host_busy >= shost->can_queue))
-		    || (shost->host_blocked)
-		    || (shost->host_self_blocked)) {
-			break;
-		}
-
+		spin_lock_irq(q->queue_lock);
 		q->request_fn(q);
+		spin_unlock_irq(q->queue_lock);
 	}
-	spin_unlock_irqrestore(shost->host_lock, flags);
 }
 
 /**
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/