vserver 1.9.5.x5
[linux-2.6.git] / drivers / scsi / scsi_scan.c
index 9d4d3ce..98e92ed 100644 (file)
@@ -39,6 +39,7 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_request.h>
 #include <scsi/scsi_transport.h>
+#include <scsi/scsi_eh.h>
 
 #include "scsi_priv.h"
 #include "scsi_logging.h"
@@ -202,10 +203,12 @@ static void print_inquiry(unsigned char *inq_result)
 static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
                uint channel, uint id, uint lun, void *hostdata)
 {
-       struct scsi_device *sdev, *device;
+       struct scsi_device *sdev;
        unsigned long flags;
+       int display_failure_msg = 1, ret;
 
-       sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC);
+       sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size,
+                      GFP_ATOMIC);
        if (!sdev)
                goto out;
 
@@ -251,81 +254,44 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
        sdev->request_queue->queuedata = sdev;
        scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
 
+       scsi_sysfs_device_initialize(sdev);
+
        if (shost->hostt->slave_alloc) {
-               if (shost->hostt->slave_alloc(sdev))
-                       goto out_free_queue;
+               ret = shost->hostt->slave_alloc(sdev);
+               if (ret) {
+                       /*
+                        * if LLDD reports slave not present, don't clutter
+                        * console with alloc failure messages
+                        */
+                       if (ret == -ENXIO)
+                               display_failure_msg = 0;
+                       goto out_device_destroy;
+               }
        }
 
-       if (shost->transportt->setup) {
-               if (shost->transportt->setup(sdev))
-                       goto out_cleanup_slave;
-       }
+       /* NOTE: this target initialisation code depends critically on
+        * lun scanning being sequential. */
+       if (scsi_sysfs_target_initialize(sdev))
+               goto out_remove_siblings;
 
-       if (get_device(&sdev->host->shost_gendev)) {
-
-               device_initialize(&sdev->sdev_gendev);
-               sdev->sdev_gendev.parent = &sdev->host->shost_gendev;
-               sdev->sdev_gendev.bus = &scsi_bus_type;
-               sdev->sdev_gendev.release = scsi_device_dev_release;
-               sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d",
-                       sdev->host->host_no, sdev->channel, sdev->id,
-                       sdev->lun);
-
-               class_device_initialize(&sdev->sdev_classdev);
-               sdev->sdev_classdev.dev = &sdev->sdev_gendev;
-               sdev->sdev_classdev.class = &sdev_class;
-               snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
-                        "%d:%d:%d:%d", sdev->host->host_no,
-                        sdev->channel, sdev->id, sdev->lun);
-
-               class_device_initialize(&sdev->transport_classdev);
-               sdev->transport_classdev.dev = &sdev->sdev_gendev;
-               sdev->transport_classdev.class = sdev->host->transportt->class;
-               snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE,
-                        "%d:%d:%d:%d", sdev->host->host_no,
-                        sdev->channel, sdev->id, sdev->lun);
-       } else
-               goto out_cleanup_transport;
+       return sdev;
 
-       /*
-        * If there are any same target siblings, add this to the
-        * sibling list
-        */
+out_remove_siblings:
        spin_lock_irqsave(shost->host_lock, flags);
-       list_for_each_entry(device, &shost->__devices, siblings) {
-               if (device->id == sdev->id &&
-                   device->channel == sdev->channel) {
-                       list_add_tail(&sdev->same_target_siblings,
-                                     &device->same_target_siblings);
-                       sdev->scsi_level = device->scsi_level;
-                       break;
-               }
-       }
-
-       /*
-        * If there wasn't another lun already configured at this
-        * target, then default this device to SCSI_2 until we
-        * know better
-        */
-       if (!sdev->scsi_level)
-               sdev->scsi_level = SCSI_2;
-
-       list_add_tail(&sdev->siblings, &shost->__devices);
+       list_del(&sdev->siblings);
+       list_del(&sdev->same_target_siblings);
        spin_unlock_irqrestore(shost->host_lock, flags);
-       return sdev;
 
-out_cleanup_transport:
-       if (shost->transportt->cleanup)
-               shost->transportt->cleanup(sdev);
-out_cleanup_slave:
        if (shost->hostt->slave_destroy)
                shost->hostt->slave_destroy(sdev);
-out_free_queue:
+out_device_destroy:
+       transport_destroy_device(&sdev->sdev_gendev);
        scsi_free_queue(sdev->request_queue);
 out_free_dev:
        kfree(sdev);
 out:
-       printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+       if (display_failure_msg)
+               printk(ALLOC_FAILURE_MSG, __FUNCTION__);
        return NULL;
 }
 
@@ -348,104 +314,117 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
 {
        struct scsi_device *sdev = sreq->sr_device;     /* a bit ugly */
        unsigned char scsi_cmd[MAX_COMMAND_SIZE];
-       int possible_inq_resp_len;
-       int count = 0;
+       int first_inquiry_len, try_inquiry_len, next_inquiry_len;
+       int response_len = 0;
+       int pass, count;
+       struct scsi_sense_hdr sshdr;
 
        *bflags = 0;
- repeat_inquiry:
-       SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d"
-                       " channel %d id %d lun %d\n", sdev->host->host_no,
-                       sdev->channel, sdev->id, sdev->lun));
-
-       memset(scsi_cmd, 0, 6);
-       scsi_cmd[0] = INQUIRY;
-       scsi_cmd[4] = 36;       /* issue conservative alloc_length */
-       sreq->sr_cmd_len = 0;
-       sreq->sr_data_direction = DMA_FROM_DEVICE;
 
-       memset(inq_result, 0, 36);
-       scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36,
-                     HZ/2 + HZ*scsi_inq_timeout, 3);
+       /* Perform up to 3 passes.  The first pass uses a conservative
+        * transfer length of 36 unless sdev->inquiry_len specifies a
+        * different value. */
+       first_inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
+       try_inquiry_len = first_inquiry_len;
+       pass = 1;
+
+ next_pass:
+       SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY pass %d "
+                       "to host %d channel %d id %d lun %d, length %d\n",
+                       pass, sdev->host->host_no, sdev->channel,
+                       sdev->id, sdev->lun, try_inquiry_len));
+
+       /* Each pass gets up to three chances to ignore Unit Attention */
+       for (count = 0; count < 3; ++count) {
+               memset(scsi_cmd, 0, 6);
+               scsi_cmd[0] = INQUIRY;
+               scsi_cmd[4] = (unsigned char) try_inquiry_len;
+               sreq->sr_cmd_len = 0;
+               sreq->sr_data_direction = DMA_FROM_DEVICE;
 
-       SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 1st INQUIRY %s with"
-                       " code 0x%x\n", sreq->sr_result ?
-                       "failed" : "successful", sreq->sr_result));
-       ++count;
+               memset(inq_result, 0, try_inquiry_len);
+               scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
+                               try_inquiry_len,
+                               HZ/2 + HZ*scsi_inq_timeout, 3);
 
-       if (sreq->sr_result) {
-               if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) != 0 &&
-                   (sreq->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION &&
-                   (sreq->sr_sense_buffer[12] == 0x28 ||
-                    sreq->sr_sense_buffer[12] == 0x29) &&
-                   sreq->sr_sense_buffer[13] == 0) {
-                       /* not-ready to ready transition or power-on - good */
-                       /* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */
-                       /* Supposedly, but many buggy devices do so anyway */
-                       if (count < 3)
-                               goto repeat_inquiry;
+               SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
+                               "with code 0x%x\n",
+                               sreq->sr_result ? "failed" : "successful",
+                               sreq->sr_result));
+
+               if (sreq->sr_result) {
+                       /*
+                        * not-ready to ready transition [asc/ascq=0x28/0x0]
+                        * or power-on, reset [asc/ascq=0x29/0x0], continue.
+                        * INQUIRY should not yield UNIT_ATTENTION
+                        * but many buggy devices do so anyway. 
+                        */
+                       if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
+                           scsi_request_normalize_sense(sreq, &sshdr)) {
+                               if ((sshdr.sense_key == UNIT_ATTENTION) &&
+                                   ((sshdr.asc == 0x28) ||
+                                    (sshdr.asc == 0x29)) &&
+                                   (sshdr.ascq == 0))
+                                       continue;
+                       }
                }
-               /*
-                * assume no peripheral if any other sort of error
-                */
-               return;
+               break;
        }
 
-       /*
-        * Get any flags for this device.
-        *
-        * XXX add a bflags to Scsi_Device, and replace the corresponding
-        * bit fields in Scsi_Device, so bflags need not be passed as an
-        * argument.
-        */
-       *bflags |= scsi_get_device_flags(sdev, &inq_result[8], &inq_result[16]);
+       if (sreq->sr_result == 0) {
+               response_len = (unsigned char) inq_result[4] + 5;
+               if (response_len > 255)
+                       response_len = first_inquiry_len;       /* sanity */
 
-       possible_inq_resp_len = (unsigned char) inq_result[4] + 5;
-       if (BLIST_INQUIRY_36 & *bflags)
-               possible_inq_resp_len = 36;
-       else if (BLIST_INQUIRY_58 & *bflags)
-               possible_inq_resp_len = 58;
-       else if (possible_inq_resp_len > 255)
-               possible_inq_resp_len = 36;     /* sanity */
-
-       if (possible_inq_resp_len > 36) {       /* do additional INQUIRY */
-               memset(scsi_cmd, 0, 6);
-               scsi_cmd[0] = INQUIRY;
-               scsi_cmd[4] = (unsigned char) possible_inq_resp_len;
-               sreq->sr_cmd_len = 0;
-               sreq->sr_data_direction = DMA_FROM_DEVICE;
                /*
-                * re-zero inq_result just to be safe.
+                * Get any flags for this device.
+                *
+                * XXX add a bflags to Scsi_Device, and replace the
+                * corresponding bit fields in Scsi_Device, so bflags
+                * need not be passed as an argument.
                 */
-               memset(inq_result, 0, possible_inq_resp_len);
-               scsi_wait_req(sreq, (void *) scsi_cmd,
-                             (void *) inq_result,
-                             possible_inq_resp_len, (1+scsi_inq_timeout)*(HZ/2), 3);
-               SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY"
-                               " %s with code 0x%x\n", sreq->sr_result ?
-                               "failed" : "successful", sreq->sr_result));
-               if (sreq->sr_result) {
-                       /* if the longer inquiry has failed, flag the device
-                        * as only accepting 36 byte inquiries and retry the
-                        * 36 byte inquiry */
-                       printk(KERN_INFO "scsi scan: %d byte inquiry failed"
-                              " with code %d.  Consider BLIST_INQUIRY_36 for"
-                              " this device\n", possible_inq_resp_len,
-                              sreq->sr_result);
-                       *bflags = BLIST_INQUIRY_36;
-                       goto repeat_inquiry;
+               *bflags = scsi_get_device_flags(sdev, &inq_result[8],
+                               &inq_result[16]);
+
+               /* When the first pass succeeds we gain information about
+                * what larger transfer lengths might work. */
+               if (pass == 1) {
+                       if (BLIST_INQUIRY_36 & *bflags)
+                               next_inquiry_len = 36;
+                       else if (BLIST_INQUIRY_58 & *bflags)
+                               next_inquiry_len = 58;
+                       else if (sdev->inquiry_len)
+                               next_inquiry_len = sdev->inquiry_len;
+                       else
+                               next_inquiry_len = response_len;
+
+                       /* If more data is available perform the second pass */
+                       if (next_inquiry_len > try_inquiry_len) {
+                               try_inquiry_len = next_inquiry_len;
+                               pass = 2;
+                               goto next_pass;
+                       }
                }
 
-               /*
-                * The INQUIRY can change, this means the length can change.
-                */
-               possible_inq_resp_len = (unsigned char) inq_result[4] + 5;
-               if (BLIST_INQUIRY_58 & *bflags)
-                       possible_inq_resp_len = 58;
-               else if (possible_inq_resp_len > 255)
-                       possible_inq_resp_len = 36;     /* sanity */
+       } else if (pass == 2) {
+               printk(KERN_INFO "scsi scan: %d byte inquiry failed.  "
+                               "Consider BLIST_INQUIRY_36 for this device\n",
+                               try_inquiry_len);
+
+               /* If this pass failed, the third pass goes back and transfers
+                * the same amount as we successfully got in the first pass. */
+               try_inquiry_len = first_inquiry_len;
+               pass = 3;
+               goto next_pass;
        }
 
-       sdev->inquiry_len = possible_inq_resp_len;
+       /* If the last transfer attempt got an error, assume the
+        * peripheral doesn't exist or is dead. */
+       if (sreq->sr_result)
+               return;
+
+       /* Don't report any more data than the device says is valid */
+       sdev->inquiry_len = min(try_inquiry_len, response_len);
 
        /*
         * XXX Abort if the response length is less than 36? If less than
@@ -500,10 +479,6 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
  **/
 static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
 {
-       struct scsi_device *sdev_sibling;
-       struct scsi_target *starget;
-       unsigned long flags;
-
        /*
         * XXX do not save the inquiry, since it can change underneath us,
         * save just vendor/model/rev.
@@ -533,7 +508,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
                 */
                inq_result[0] = TYPE_ROM;
                inq_result[1] |= 0x80;  /* removable */
-       }
+       } else if (*bflags & BLIST_NO_ULD_ATTACH)
+               sdev->no_uld_attach = 1;
 
        switch (sdev->type = (inq_result[0] & 0x1f)) {
        case TYPE_TAPE:
@@ -605,6 +581,13 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
        if ((*bflags & BLIST_BORKEN) == 0)
                sdev->borken = 0;
 
+       /*
+        * Apparently some really broken devices (contrary to the SCSI
+        * standards) need to be selected without asserting ATN
+        */
+       if (*bflags & BLIST_SELECT_NO_ATN)
+               sdev->select_no_atn = 1;
+
        /*
         * Some devices may not want to have a start command automatically
         * issued when a device is added.
@@ -612,40 +595,9 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
        if (*bflags & BLIST_NOSTARTONADD)
                sdev->no_start_on_add = 1;
 
-       /*
-        * If we need to allow I/O to only one of the luns attached to
-        * this target id at a time set single_lun, and allocate or modify
-        * sdev_target.
-        */
-       if (*bflags & BLIST_SINGLELUN) {
+       if (*bflags & BLIST_SINGLELUN)
                sdev->single_lun = 1;
-               spin_lock_irqsave(sdev->host->host_lock, flags);
-               starget = NULL;
-               /*
-                * Search for an existing target for this sdev.
-                */
-               list_for_each_entry(sdev_sibling, &sdev->same_target_siblings,
-                                   same_target_siblings) {
-                       if (sdev_sibling->sdev_target != NULL) {
-                               starget = sdev_sibling->sdev_target;
-                               break;
-                       }
-               }
-               if (!starget) {
-                       starget = kmalloc(sizeof(*starget), GFP_ATOMIC);
-                       if (!starget) {
-                               printk(ALLOC_FAILURE_MSG, __FUNCTION__);
-                               spin_unlock_irqrestore(sdev->host->host_lock,
-                                                      flags);
-                               return SCSI_SCAN_NO_RESPONSE;
-                       }
-                       starget->starget_refcnt = 0;
-                       starget->starget_sdev_user = NULL;
-               }
-               starget->starget_refcnt++;
-               sdev->sdev_target = starget;
-               spin_unlock_irqrestore(sdev->host->host_lock, flags);
-       }
+
 
        sdev->use_10_for_rw = 1;
 
@@ -668,7 +620,9 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
        if (*bflags & BLIST_NOT_LOCKABLE)
                sdev->lockable = 0;
 
-       if(sdev->host->hostt->slave_configure)
+       transport_configure_device(&sdev->sdev_gendev);
+
+       if (sdev->host->hostt->slave_configure)
                sdev->host->hostt->slave_configure(sdev);
 
        /*
@@ -785,8 +739,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
        } else {
                if (sdev->host->hostt->slave_destroy)
                        sdev->host->hostt->slave_destroy(sdev);
-               if (sdev->host->transportt->cleanup)
-                       sdev->host->transportt->cleanup(sdev);
+               transport_destroy_device(&sdev->sdev_gendev);
                put_device(&sdev->sdev_gendev);
        }
  out:
@@ -939,6 +892,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
        struct scsi_lun *lunp, *lun_data;
        struct scsi_request *sreq;
        u8 *data;
+       struct scsi_sense_hdr sshdr;
 
        /*
         * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
@@ -1016,9 +970,12 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
                                " %s (try %d) result 0x%x\n", sreq->sr_result
                                ?  "failed" : "successful", retries,
                                sreq->sr_result));
-               if (sreq->sr_result == 0 ||
-                   sreq->sr_sense_buffer[2] != UNIT_ATTENTION)
+               if (sreq->sr_result == 0)
                        break;
+               else if (scsi_request_normalize_sense(sreq, &sshdr)) {
+                       if (sshdr.sense_key != UNIT_ATTENTION)
+                               break;
+               }
        }
 
        if (sreq->sr_result) {
@@ -1128,6 +1085,7 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
 
        return sdev;
 }
+EXPORT_SYMBOL(__scsi_add_device);
 
 void scsi_rescan_device(struct device *dev)
 {
@@ -1143,6 +1101,7 @@ void scsi_rescan_device(struct device *dev)
                module_put(drv->owner);
        }
 }
+EXPORT_SYMBOL(scsi_rescan_device);
 
 /**
  * scsi_scan_target - scan a target id, possibly including all LUNs on the
@@ -1271,6 +1230,20 @@ void scsi_scan_host(struct Scsi_Host *shost)
        scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
                                SCAN_WILD_CARD, 0);
 }
+EXPORT_SYMBOL(scsi_scan_host);
+
+/**
+ * scsi_scan_single_target - scan the given SCSI target
+ * @shost:         adapter to scan
+ * @chan:          channel to scan
+ * @id:            target id to scan
+ **/
+void scsi_scan_single_target(struct Scsi_Host *shost, 
+       unsigned int chan, unsigned int id)
+{
+       scsi_scan_host_selected(shost, chan, id, SCAN_WILD_CARD, 1);
+}
+EXPORT_SYMBOL(scsi_scan_single_target);
 
 void scsi_forget_host(struct Scsi_Host *shost)
 {
@@ -1325,6 +1298,7 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
        }
        return sdev;
 }
+EXPORT_SYMBOL(scsi_get_host_dev);
 
 /*
  * Function:    scsi_free_host_dev()
@@ -1345,7 +1319,8 @@ void scsi_free_host_dev(struct scsi_device *sdev)
 
        if (sdev->host->hostt->slave_destroy)
                sdev->host->hostt->slave_destroy(sdev);
-       if (sdev->host->transportt->cleanup)
-               sdev->host->transportt->cleanup(sdev);
+       transport_destroy_device(&sdev->sdev_gendev);
        put_device(&sdev->sdev_gendev);
 }
+EXPORT_SYMBOL(scsi_free_host_dev);
+