[RFC] [PATCH] 1/7 2.5.35 SCSI multi-path

From: Patrick Mansfield (patmans@us.ibm.com)
Date: Tue Sep 17 2002 - 17:50:18 EST


Patch 1 of 3 to add mid-layer scsi changes to simplify and enable the
addition of scsi multi-path IO support.

The bulk (number of lines changed) of these changes are:

        add a Scsi_Device iterator

        add and use functions for removal and addition of a Scsi_Device

        add hooks for calls to multi-path functions - with this patch, the
        hooks are either NULL code, or code that matches the current scsi
        behaviour

 hosts.c | 25 +--
 hosts.h | 30 +++-
 scsi.c | 429 +++++++++++++++++++++++++++++------------------------------
 scsi_error.c | 189 +++++++++++++++----------
 4 files changed, 364 insertions(+), 309 deletions(-)

diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/hosts.c Mon Sep 16 15:29:45 2002
@@ -259,30 +259,27 @@
     return retval;
 }
 
-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 (%d)\n",
                                           atomic_read(&shost->eh_wait->count)));
         }
- 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)
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/hosts.h Mon Sep 16 15:29:45 2002
@@ -576,7 +576,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
  * @channel: SCSI channel (zero if only one channel)
@@ -589,13 +588,40 @@
 
         for(SDpnt = host->host_queue;
             SDpnt != NULL;
- SDpnt = SDpnt->next)
+ SDpnt = SDpnt->sdev_next)
                 if(SDpnt->channel == channel && SDpnt->id == pun
                    && SDpnt->lun ==lun)
                         break;
         return SDpnt;
 }
     
+/*
+ * XXX For now, check for the existence of the NUMA topology patch via the
+ * define of MAX_NR_MEMBLKS.
+ */
+#if defined(CONFIG_MULTIQUAD) && defined(MAX_NR_MEMBLKS)
+static inline int scsihost_to_node(struct Scsi_Host *host)
+{
+ if (!host) {
+ printk("%s: host == NULL \n", __FUNCTION__);
+ return 0;
+ }
+ if(host->pci_dev)
+ return pcidev_to_node(host->pci_dev);
+
+ /*
+ * XXX umm what about usb and ide over scsi?
+ */
+ printk("%s: Unable to determine bus type\n", __FUNCTION__);
+ return 0;
+}
+#else
+/*
+ * In the absence of NUMA, assign to node 0
+ */
+#define scsihost_to_node(host) 0
+#endif /* CONFIG_MULTIQUAD */
+
 #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 Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi.c Mon Sep 16 15:29:45 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;
+ scsi_traverse_hndl_t 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);
@@ -608,8 +605,10 @@
 {
         request_queue_t *q;
         Scsi_Device * SDpnt;
+ struct Scsi_Host * SHpnt;
 
         SDpnt = SCpnt->device;
+ SHpnt = SCpnt->host;
 
         __scsi_release_command(SCpnt);
 
@@ -620,7 +619,7 @@
          * will go on.
          */
         q = &SDpnt->request_queue;
- scsi_queue_next_request(q, NULL);
+ scsi_queue_next_request(q, NULL, SHpnt);
 }
 
 /*
@@ -689,10 +688,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;
@@ -808,30 +809,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
@@ -1199,6 +1187,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
@@ -1291,6 +1288,7 @@
         struct Scsi_Host *host;
         Scsi_Device *device;
         Scsi_Request * SRpnt;
+ unsigned long flags;
 
         host = SCpnt->host;
         device = SCpnt->device;
@@ -1304,7 +1302,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
@@ -1323,7 +1325,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;
@@ -1392,12 +1394,18 @@
 void scsi_build_commandblocks(Scsi_Device * SDpnt)
 {
         unsigned long flags;
- struct Scsi_Host *host = SDpnt->host;
+ struct Scsi_Host *host;
         int j;
         Scsi_Cmnd *SCpnt;
 
         spin_lock_irqsave(&device_request_lock, flags);
 
+ host = scsi_get_host(SDpnt);
+ /*
+ * We are called during scan, and must always have at least one
+ * path.
+ */
+ BUG_ON(host == NULL);
         if (SDpnt->queue_depth == 0)
         {
                 SDpnt->queue_depth = host->cmd_per_lun;
@@ -1415,10 +1423,10 @@
                         break; /* If not, the next line will oops ... */
                 memset(SCpnt, 0, sizeof(Scsi_Cmnd));
                 SCpnt->host = host;
- SCpnt->device = SDpnt;
                 SCpnt->target = SDpnt->id;
                 SCpnt->lun = SDpnt->lun;
                 SCpnt->channel = SDpnt->channel;
+ SCpnt->device = SDpnt;
                 SCpnt->request = NULL;
                 SCpnt->use_sg = 0;
                 SCpnt->old_use_sg = 0;
@@ -1477,44 +1485,37 @@
 static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
 {
         Scsi_Device *scd;
- struct Scsi_Host *HBA_ptr;
+ scsi_traverse_hndl_t 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_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
- 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_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
-#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)
@@ -1669,13 +1670,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)
@@ -1721,13 +1716,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 */
@@ -1744,6 +1733,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.
@@ -1752,21 +1745,11 @@
                                 HBA_ptr->hostt->revoke(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);
+ /* Now we can remove the device structure */
+ scsi_remove_scsi_device(scd);
                 } else {
                         goto out;
                 }
@@ -1791,6 +1774,7 @@
         struct Scsi_Device_Template *sdtpnt;
         const char *name;
         int out_of_space = 0;
+ scsi_traverse_hndl_t STrav_hndl;
 
         if (tpnt->next || !tpnt->detect)
                 return 1; /* Must be already loaded, or
@@ -1901,18 +1885,19 @@
                 /*
                  * Next we create the Scsi_Cmnd structures for this host
                  */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next)
- if (SDpnt->host->hostt == tpnt) {
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if (sdtpnt->attach)
- (*sdtpnt->attach) (SDpnt);
- if (SDpnt->attached) {
- scsi_build_commandblocks(SDpnt);
- if (0 == SDpnt->has_cmdblocks)
- out_of_space = 1;
- }
+ scsi_for_all_sdevs(&STrav_hndl, SDpnt) {
+ shpnt = scsi_get_host(SDpnt);
+ if (shpnt && shpnt->hostt == tpnt) {
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if (sdtpnt->attach)
+ (*sdtpnt->attach) (SDpnt);
+ if (SDpnt->attached && (0 ==
+ SDpnt->has_cmdblocks)) {
+ scsi_build_commandblocks(SDpnt);
+ if (0 == SDpnt->has_cmdblocks)
+ out_of_space = 1;
                                 }
+ }
                 }
 
                 /* This does any final handling that is required. */
@@ -1943,8 +1928,9 @@
         Scsi_Device *SDpnt1;
         struct Scsi_Device_Template *sdtpnt;
         struct Scsi_Host *sh1;
- struct Scsi_Host *shpnt;
+ struct Scsi_Host *shpnt, *shpnt2;
         char name[10]; /* host_no>=10^9? I don't think so. */
+ scsi_traverse_hndl_t STrav_hndl;
 
         /* get the big kernel lock, so we don't race with open() */
         lock_kernel();
@@ -1954,12 +1940,16 @@
          * commands
          */
         for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (SDpnt->host->hostt == tpnt
- && SDpnt->host->hostt->module
- && GET_USE_COUNT(SDpnt->host->hostt->module))
- goto err_out;
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
+ /*
+ * If any shpnt2 is still in use, it should be
+ * impossible to call this function.
+ */
+ shpnt2 = scsi_get_host(SDpnt);
+ if (shpnt2 && shpnt2->hostt == tpnt
+ && shpnt2->hostt->module
+ && GET_USE_COUNT(shpnt2->hostt->module))
+ BUG();
                         /*
                          * FIXME(eric) - We need to find a way to notify the
                          * low level driver that we are shutting down - via the
@@ -1976,9 +1966,9 @@
          * get in and queue a command.
          */
         for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (SDpnt->host->hostt == tpnt)
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
+ shpnt2 = scsi_get_host(SDpnt);
+ if (shpnt2 && shpnt2->hostt == tpnt)
                                 SDpnt->online = FALSE;
 
                 }
@@ -1988,8 +1978,7 @@
                 if (shpnt->hostt != tpnt) {
                         continue;
                 }
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
                         /*
                          * Loop over all of the commands associated with the device. If any of
                          * them are busy, then set the state back to inactive and bail.
@@ -1998,12 +1987,12 @@
                              SCpnt = SCpnt->next) {
                                 online_status = SDpnt->online;
                                 SDpnt->online = FALSE;
- if (SCpnt->request && SCpnt->request->rq_status != RQ_INACTIVE) {
+ if ((SCpnt->request && SCpnt->request->rq_status != RQ_INACTIVE)
+ && SCpnt->request->rq_status != RQ_SCSI_DISCONNECTING) {
                                         printk(KERN_ERR "SCSI device not inactive - rq_status=%d, target=%d, pid=%ld, state=%d, owner=%d.\n",
                                                SCpnt->request->rq_status, SCpnt->target, SCpnt->pid,
                                              SCpnt->state, SCpnt->owner);
- for (SDpnt1 = shpnt->host_queue; SDpnt1;
- SDpnt1 = SDpnt1->next) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt1, shpnt->host_no) {
                                                 for (SCpnt = SDpnt1->device_queue; SCpnt;
                                                      SCpnt = SCpnt->next)
                                                         if (SCpnt->request->rq_status == RQ_SCSI_DISCONNECTING)
@@ -2029,18 +2018,20 @@
                 if (shpnt->hostt != tpnt) {
                         continue;
                 }
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if (sdtpnt->detach)
- (*sdtpnt->detach) (SDpnt);
-
- /* If something still attached, punt */
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
                         if (SDpnt->attached) {
- printk(KERN_ERR "Attached usage count = %d\n", SDpnt->attached);
- goto err_out;
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+ if (sdtpnt->detach)
+ (*sdtpnt->detach) (SDpnt);
+ }
+
+ /* If something still attached, punt */
+ if (SDpnt->attached) {
+ printk(KERN_ERR "Attached usage count = %d\n", SDpnt->attached);
+ goto err_out;
+ }
+ devfs_unregister (SDpnt->de);
                         }
- devfs_unregister (SDpnt->de);
                         put_device(&SDpnt->sdev_driverfs_dev);
                 }
         }
@@ -2066,17 +2057,15 @@
                 if (shpnt->hostt != tpnt) {
                         continue;
                 }
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = shpnt->host_queue) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
+ /* Next free up the Scsi_Device structures for this host */
+ scsi_remove_path(SDpnt, shpnt->host_no,
+ SCSI_FIND_ALL_CHANNEL,
+ SCSI_FIND_ALL_ID,
+ SCSI_FIND_ALL_LUN);
                         scsi_release_commandblocks(SDpnt);
-
                         blk_cleanup_queue(&SDpnt->request_queue);
- /* Next free up the Scsi_Device structures for this host */
- shpnt->host_queue = SDpnt->next;
- if (SDpnt->inquiry)
- kfree(SDpnt->inquiry);
- kfree((char *) SDpnt);
-
+ scsi_remove_scsi_device(SDpnt);
                 }
         }
 
@@ -2150,8 +2139,8 @@
 int scsi_register_device(struct Scsi_Device_Template *tpnt)
 {
         Scsi_Device *SDpnt;
- struct Scsi_Host *shpnt;
         int out_of_space = 0;
+ scsi_traverse_hndl_t STrav_hndl;
 
 #ifdef CONFIG_KMOD
         if (scsi_hosts == NULL)
@@ -2168,12 +2157,9 @@
          * First scan the devices that we know about, and see if we notice them.
          */
 
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- 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);
         }
 
         /*
@@ -2187,21 +2173,24 @@
         /*
          * Now actually connect the devices to the new driver.
          */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- 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->has_cmdblocks == 0) {
- SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
- if (0 == SDpnt->has_cmdblocks)
- 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->has_cmdblocks == 0) {
+ SDpnt->online = TRUE;
+ scsi_build_commandblocks(SDpnt);
+ if (0 == SDpnt->has_cmdblocks)
+ out_of_space = 1;
                 }
         }
 
@@ -2222,9 +2211,9 @@
 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;
+ scsi_traverse_hndl_t STrav_hndl;
         
         lock_kernel();
         /*
@@ -2237,22 +2226,20 @@
          * Next, detach the devices from the driver.
          */
 
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- 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.
- */
- scsi_release_commandblocks(SDpnt);
- }
+ /*
+ * Nobody is using this device any more. Free all of the
+ * command structures.
+ */
+ scsi_release_commandblocks(SDpnt);
                 }
         }
+
         /*
          * Extract the template from the linked list.
          */
@@ -2305,6 +2292,8 @@
         struct Scsi_Host *shpnt;
         Scsi_Cmnd *SCpnt;
         Scsi_Device *SDpnt;
+ scsi_traverse_hndl_t STrav_hndl;
+
         printk(KERN_INFO "Dump of scsi host parameters:\n");
         i = 0;
         for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
@@ -2318,38 +2307,36 @@
 
         printk(KERN_INFO "\n\n");
         printk(KERN_INFO "Dump of scsi command parameters:\n");
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- 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 %4ld %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),
- 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 %4ld %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),
+ 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 */ /* } */
@@ -2592,15 +2579,21 @@
         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;
         SDpnt->queue_depth = 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;
+ }
         
         scsi_build_commandblocks(SDpnt);
 
@@ -2630,11 +2623,17 @@
  */
 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");
         }
-
+ scsi_remove_path(SDpnt, scsi_path.spi_shpnt->host_no,
+ scsi_path.spi_channel,
+ scsi_path.spi_id,
+ scsi_path.spi_lun);
         blk_cleanup_queue(&SDpnt->request_queue);
 
         /*
@@ -2642,9 +2641,7 @@
          * it now.
          */
         scsi_release_commandblocks(SDpnt);
- if (SDpnt->inquiry)
- kfree(SDpnt->inquiry);
- kfree(SDpnt);
+ scsi_remove_scsi_device(SDpnt);
 }
 
 /*
@@ -2682,14 +2679,16 @@
         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);
+ 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_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_error.c Mon Sep 16 15:29:45 2002
@@ -126,37 +126,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.
  *
@@ -172,7 +141,6 @@
 {
 
         SCSI_SLEEP(&sdev->host->host_wait, sdev->host->in_recovery);
-
         SCSI_LOG_ERROR_RECOVERY(5, printk("Open returning %d\n",
                                           sdev->online));
 
@@ -193,9 +161,9 @@
         int cmd_failed = 0;
         int cmd_timed_out = 0;
         int devices_failed = 0;
+ scsi_traverse_hndl_t 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;
@@ -208,12 +176,13 @@
                 }
 
                 if (cmd_timed_out || cmd_failed) {
- SCSI_LOG_ERROR_RECOVERY(3,
- printk("scsi_eh: %d:%d:%d:%d cmds failed: %d,"
- "timedout: %d\n",
- 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, timedout: %d\n",
+ cmd_failed, cmd_timed_out);
+ });
                         cmd_timed_out = 0;
                         cmd_failed = 0;
                         ++devices_failed;
@@ -239,8 +208,10 @@
         int found;
         Scsi_Device *sdev;
         Scsi_Cmnd *scmd;
+ scsi_traverse_hndl_t 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;
@@ -285,7 +256,7 @@
  * scsi_check_sense - Examine scsi cmd sense
  * @scmd: Cmd to have sense checked.
  **/
-static int scsi_check_sense(Scsi_Cmnd *scmd)
+int scsi_check_sense(Scsi_Cmnd *scmd)
 {
         if (!SCSI_SENSE_VALID(scmd)) {
                 return FAILED;
@@ -836,6 +807,72 @@
 }
 
 /**
+ * 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_path_decide_disposition.
+ */
+ scmd->result = (scmd->result & 0xff00ffff) | (DID_TIME_OUT << 16) |
+ (DRIVER_TIMEOUT << 24);
+ if (scsi_path_decide_disposition(scmd) == REQUEUE) {
+ /*
+ * Abort the command, and then requeue it.
+ *
+ * XXX If the abort is not possible, what do we do?
+ * Probably should not retry. But, for example, qlogicfc.c
+ * times out commands after a loop down, and the command
+ * is no longer live; so the abort command always fails
+ * after a loop down causes the command to timeout. This
+ * is also a problem for the current error handling, since
+ * it does not know if the command was not able to be
+ * aborted, or if the command failed to abort because the
+ * adapter no longer has it.
+ */
+ (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;
+ }
+
+ /* 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
  *
@@ -982,10 +1019,11 @@
         int rtn;
         Scsi_Cmnd *scmd;
         Scsi_Device *sdev;
+ scsi_traverse_hndl_t 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))
@@ -1021,6 +1059,7 @@
         unsigned long flags;
         int rtn;
         Scsi_Device *sdev;
+ scsi_traverse_hndl_t strav_hndl;
 
         SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
                                           __FUNCTION__));
@@ -1039,11 +1078,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;
 }
@@ -1056,7 +1095,8 @@
 {
         unsigned long flags;
         int rtn;
- Scsi_Device *sdev;
+ Scsi_Device *sdev;
+ scsi_traverse_hndl_t strav_hndl;
 
         SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
                                           __FUNCTION__));
@@ -1075,11 +1115,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;
 }
@@ -1132,8 +1172,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);
- if (rtn != SUCCESS)
+ rtn = scsi_try_bus_reset(scmd); if (rtn != SUCCESS)
                         rtn = scsi_try_host_reset(scmd);
 
                 if (rtn == SUCCESS) {
@@ -1168,14 +1207,11 @@
                 if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
                         continue;
 
- printk(KERN_INFO "%s: Device set offline - 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);
         }
@@ -1247,6 +1283,12 @@
                                                   __FUNCTION__));
                 return SUCCESS;
         }
+
+ rtn = scsi_path_decide_disposition(scmd);
+ if (rtn != UNKNOWN_ERROR) {
+ return(rtn);
+ }
+
         /*
          * first check the host byte, to see if there is anything in there
          * that would indicate what we need to do.
@@ -1364,7 +1406,7 @@
         case RESERVATION_CONFLICT:
                 printk("scsi%d (%d,%d,%d) : reservation conflict\n",
                        scmd->host->host_no, scmd->channel,
- scmd->device->id, scmd->device->lun);
+ scmd->target, scmd->lun);
                 return SUCCESS; /* causes immediate i/o error */
         default:
                 return FAILED;
@@ -1394,7 +1436,7 @@
 static void scsi_restart_operations(struct Scsi_Host *shost)
 {
         Scsi_Device *sdev;
- unsigned long flags;
+ scsi_traverse_hndl_t strav_hndl;
 
         ASSERT_LOCK(shost->host_lock, 0);
 
@@ -1414,21 +1456,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)
- || (sdev->device_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/



This archive was generated by hypermail 2b29 : Mon Sep 23 2002 - 22:00:20 EST