Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / scsi / scsi_scan.c
index 166cb9a..1a5474b 100644 (file)
@@ -9,7 +9,7 @@
  * global variable (boot or module load time) settings.
  *
  * A specific LUN is scanned via an INQUIRY command; if the LUN has a
- * device attached, a Scsi_Device is allocated and setup for it.
+ * device attached, a scsi_device is allocated and setup for it.
  *
  * For every id of every channel on the given host:
  *
@@ -17,7 +17,7 @@
  *     device or storage attached to LUN 0):
  *
  *             If LUN 0 has a device attached, allocate and setup a
- *             Scsi_Device for it.
+ *             scsi_device for it.
  *
  *             If target is SCSI-3 or up, issue a REPORT LUN, and scan
  *             all of the LUNs returned by the REPORT LUN; else,
@@ -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"
@@ -73,7 +74,7 @@
 #define SCSI_SCAN_TARGET_PRESENT       1
 #define SCSI_SCAN_LUN_PRESENT          2
 
-static char *scsi_null_device_strs = "nullnullnullnull";
+static const char *scsi_null_device_strs = "nullnullnullnull";
 
 #define MAX_SCSI_LUNS  512
 
@@ -110,15 +111,14 @@ MODULE_PARM_DESC(inq_timeout,
 
 /**
  * scsi_unlock_floptical - unlock device via a special MODE SENSE command
- * @sreq:      used to send the command
+ * @sdev:      scsi device to send command to
  * @result:    area to store the result of the MODE SENSE
  *
  * Description:
- *     Send a vendor specific MODE SENSE (not a MODE SELECT) command using
- *     @sreq to unlock a device, storing the (unused) results into result.
+ *     Send a vendor specific MODE SENSE (not a MODE SELECT) command.
  *     Called for BLIST_KEY devices.
  **/
-static void scsi_unlock_floptical(struct scsi_request *sreq,
+static void scsi_unlock_floptical(struct scsi_device *sdev,
                                  unsigned char *result)
 {
        unsigned char scsi_cmd[MAX_COMMAND_SIZE];
@@ -128,11 +128,10 @@ static void scsi_unlock_floptical(struct scsi_request *sreq,
        scsi_cmd[1] = 0;
        scsi_cmd[2] = 0x2e;
        scsi_cmd[3] = 0;
-       scsi_cmd[4] = 0x2a;     /* size */
+       scsi_cmd[4] = 0x2a;     /* size */
        scsi_cmd[5] = 0;
-       sreq->sr_cmd_len = 0;
-       sreq->sr_data_direction = DMA_FROM_DEVICE;
-       scsi_wait_req(sreq, scsi_cmd, result, 0x2a /* size */, SCSI_TIMEOUT, 3);
+       scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL,
+                        SCSI_TIMEOUT, 3);
 }
 
 /**
@@ -199,24 +198,25 @@ static void print_inquiry(unsigned char *inq_result)
  * Return value:
  *     scsi_Device pointer, or NULL on failure.
  **/
-static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
-               uint channel, uint id, uint lun)
+static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
+                                          unsigned int lun, void *hostdata)
 {
-       struct scsi_device *sdev, *device;
-       unsigned long flags;
+       struct scsi_device *sdev;
+       int display_failure_msg = 1, ret;
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
 
-       sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC);
+       sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
+                      GFP_ATOMIC);
        if (!sdev)
                goto out;
 
-       memset(sdev, 0, sizeof(*sdev));
        sdev->vendor = scsi_null_device_strs;
        sdev->model = scsi_null_device_strs;
        sdev->rev = scsi_null_device_strs;
        sdev->host = shost;
-       sdev->id = id;
+       sdev->id = starget->id;
        sdev->lun = lun;
-       sdev->channel = channel;
+       sdev->channel = starget->channel;
        sdev->sdev_state = SDEV_CREATED;
        INIT_LIST_HEAD(&sdev->siblings);
        INIT_LIST_HEAD(&sdev->same_target_siblings);
@@ -224,6 +224,11 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
        INIT_LIST_HEAD(&sdev->starved_entry);
        spin_lock_init(&sdev->list_lock);
 
+       sdev->sdev_gendev.parent = get_device(&starget->dev);
+       sdev->sdev_target = starget;
+
+       /* usually NULL and set by ->slave_alloc instead */
+       sdev->hostdata = hostdata;
 
        /* if the device needs this changing, it may do so in the
         * slave_configure function */
@@ -241,209 +246,336 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
         */
        sdev->borken = 1;
 
-       spin_lock_init(&sdev->sdev_lock);
        sdev->request_queue = scsi_alloc_queue(sdev);
-       if (!sdev->request_queue)
-               goto out_free_dev;
+       if (!sdev->request_queue) {
+               /* release fn is set up in scsi_sysfs_device_initialise, so
+                * have to free and put manually here */
+               put_device(&starget->dev);
+               kfree(sdev);
+               goto out;
+       }
 
        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;
-       }
+       return sdev;
 
-       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;
+out_device_destroy:
+       transport_destroy_device(&sdev->sdev_gendev);
+       put_device(&sdev->sdev_gendev);
+out:
+       if (display_failure_msg)
+               printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+       return NULL;
+}
 
+static void scsi_target_dev_release(struct device *dev)
+{
+       struct device *parent = dev->parent;
+       struct scsi_target *starget = to_scsi_target(dev);
+
+       kfree(starget);
+       put_device(parent);
+}
+
+int scsi_is_target_device(const struct device *dev)
+{
+       return dev->release == scsi_target_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_target_device);
+
+static struct scsi_target *__scsi_find_target(struct device *parent,
+                                             int channel, uint id)
+{
+       struct scsi_target *starget, *found_starget = NULL;
+       struct Scsi_Host *shost = dev_to_shost(parent);
        /*
-        * If there are any same target siblings, add this to the
-        * sibling list
+        * Search for an existing target for this sdev.
         */
-       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;
+       list_for_each_entry(starget, &shost->__targets, siblings) {
+               if (starget->id == id &&
+                   starget->channel == channel) {
+                       found_starget = starget;
                        break;
                }
        }
+       if (found_starget)
+               get_device(&found_starget->dev);
 
-       /*
-        * 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;
+       return found_starget;
+}
+
+static struct scsi_target *scsi_alloc_target(struct device *parent,
+                                            int channel, uint id)
+{
+       struct Scsi_Host *shost = dev_to_shost(parent);
+       struct device *dev = NULL;
+       unsigned long flags;
+       const int size = sizeof(struct scsi_target)
+               + shost->transportt->target_size;
+       struct scsi_target *starget;
+       struct scsi_target *found_target;
+       int error;
 
-       list_add_tail(&sdev->siblings, &shost->__devices);
+       starget = kzalloc(size, GFP_KERNEL);
+       if (!starget) {
+               printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
+               return NULL;
+       }
+       dev = &starget->dev;
+       device_initialize(dev);
+       starget->reap_ref = 1;
+       dev->parent = get_device(parent);
+       dev->release = scsi_target_dev_release;
+       sprintf(dev->bus_id, "target%d:%d:%d",
+               shost->host_no, channel, id);
+       starget->id = id;
+       starget->channel = channel;
+       INIT_LIST_HEAD(&starget->siblings);
+       INIT_LIST_HEAD(&starget->devices);
+       starget->state = STARGET_RUNNING;
+ retry:
+       spin_lock_irqsave(shost->host_lock, flags);
+
+       found_target = __scsi_find_target(parent, channel, id);
+       if (found_target)
+               goto found;
+
+       list_add_tail(&starget->siblings, &shost->__targets);
        spin_unlock_irqrestore(shost->host_lock, flags);
-       return sdev;
+       /* allocate and add */
+       transport_setup_device(dev);
+       error = device_add(dev);
+       if (error) {
+               dev_err(dev, "target device_add failed, error %d\n", error);
+               spin_lock_irqsave(shost->host_lock, flags);
+               list_del_init(&starget->siblings);
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               transport_destroy_device(dev);
+               put_device(parent);
+               kfree(starget);
+               return NULL;
+       }
+       transport_add_device(dev);
+       if (shost->hostt->target_alloc) {
+               error = shost->hostt->target_alloc(starget);
+
+               if(error) {
+                       dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error);
+                       /* don't want scsi_target_reap to do the final
+                        * put because it will be under the host lock */
+                       get_device(dev);
+                       scsi_target_reap(starget);
+                       put_device(dev);
+                       return NULL;
+               }
+       }
 
-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:
-       scsi_free_queue(sdev->request_queue);
-out_free_dev:
-       kfree(sdev);
-out:
-       printk(ALLOC_FAILURE_MSG, __FUNCTION__);
-       return NULL;
+       return starget;
+
+ found:
+       found_target->reap_ref++;
+       spin_unlock_irqrestore(shost->host_lock, flags);
+       put_device(parent);
+       if (found_target->state != STARGET_DEL) {
+               kfree(starget);
+               return found_target;
+       }
+       /* Unfortunately, we found a dying target; need to
+        * wait until it's dead before we can get a new one */
+       put_device(&found_target->dev);
+       flush_scheduled_work();
+       goto retry;
+}
+
+static void scsi_target_reap_usercontext(void *data)
+{
+       struct scsi_target *starget = data;
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       unsigned long flags;
+
+       transport_remove_device(&starget->dev);
+       device_del(&starget->dev);
+       transport_destroy_device(&starget->dev);
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (shost->hostt->target_destroy)
+               shost->hostt->target_destroy(starget);
+       list_del_init(&starget->siblings);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+       put_device(&starget->dev);
+}
+
+/**
+ * scsi_target_reap - check to see if target is in use and destroy if not
+ *
+ * @starget: target to be checked
+ *
+ * This is used after removing a LUN or doing a last put of the target
+ * it checks atomically that nothing is using the target and removes
+ * it if so.
+ */
+void scsi_target_reap(struct scsi_target *starget)
+{
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+
+       if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
+               BUG_ON(starget->state == STARGET_DEL);
+               starget->state = STARGET_DEL;
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               execute_in_process_context(scsi_target_reap_usercontext,
+                                          starget, &starget->ew);
+               return;
+
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       return;
 }
 
 /**
  * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
- * @sreq:      used to send the INQUIRY
+ * @sdev:      scsi_device to probe
  * @inq_result:        area to store the INQUIRY result
+ * @result_len: len of inq_result
  * @bflags:    store any bflags found here
  *
  * Description:
- *     Probe the lun associated with @sreq using a standard SCSI INQUIRY;
+ *     Probe the lun associated with @req using a standard SCSI INQUIRY;
  *
- *     If the INQUIRY is successful, sreq->sr_result is zero and: the
+ *     If the INQUIRY is successful, zero is returned and the
  *     INQUIRY data is in @inq_result; the scsi_level and INQUIRY length
- *     are copied to the Scsi_Device at @sreq->sr_device (sdev);
- *     any flags value is stored in *@bflags.
+ *     are copied to the scsi_device any flags value is stored in *@bflags.
  **/
-static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
-                          int *bflags)
+static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result,
+                         int result_len, int *bflags)
 {
-       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, result;
+       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);
-
-       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;
-
-       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;
-               }
-               /*
-                * assume no peripheral if any other sort of error
-                */
-               return;
-       }
 
-       /*
-        * 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]);
+       /* 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;
 
-       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 */
+ next_pass:
+       SCSI_LOG_SCAN_BUS(3, sdev_printk(KERN_INFO, sdev,
+                               "scsi scan: INQUIRY pass %d length %d\n",
+                               pass, try_inquiry_len));
 
-       if (possible_inq_resp_len > 36) {       /* do additional INQUIRY */
+       /* 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) possible_inq_resp_len;
-               sreq->sr_cmd_len = 0;
-               sreq->sr_data_direction = DMA_FROM_DEVICE;
-               /*
-                * re-zero inq_result just to be safe.
-                */
-               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;
+               scsi_cmd[4] = (unsigned char) try_inquiry_len;
+
+               memset(inq_result, 0, try_inquiry_len);
+
+               result = scsi_execute_req(sdev,  scsi_cmd, DMA_FROM_DEVICE,
+                                         inq_result, try_inquiry_len, &sshdr,
+                                         HZ / 2 + HZ * scsi_inq_timeout, 3);
+
+               SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
+                               "with code 0x%x\n",
+                               result ? "failed" : "successful", result));
+
+               if (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(result) & DRIVER_SENSE) &&
+                           scsi_sense_valid(&sshdr)) {
+                               if ((sshdr.sense_key == UNIT_ATTENTION) &&
+                                   ((sshdr.asc == 0x28) ||
+                                    (sshdr.asc == 0x29)) &&
+                                   (sshdr.ascq == 0))
+                                       continue;
+                       }
                }
+               break;
+       }
+
+       if (result == 0) {
+               response_len = (unsigned char) inq_result[4] + 5;
+               if (response_len > 255)
+                       response_len = first_inquiry_len;       /* sanity */
 
                /*
-                * The INQUIRY can change, this means the length can change.
+                * 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.
                 */
-               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 */
+               *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;
+                       }
+               }
+
+       } 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 (result)
+               return -EIO;
+
+       /* 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
@@ -475,33 +607,30 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
        if (sdev->scsi_level >= 2 ||
            (sdev->scsi_level == 1 && (inq_result[3] & 0x0f) == 1))
                sdev->scsi_level++;
+       sdev->sdev_target->scsi_level = sdev->scsi_level;
 
-       return;
+       return 0;
 }
 
 /**
- * scsi_add_lun - allocate and fully initialze a Scsi_Device
- * @sdevscan:  holds information to be stored in the new Scsi_Device
- * @sdevnew:   store the address of the newly allocated Scsi_Device
+ * scsi_add_lun - allocate and fully initialze a scsi_device
+ * @sdevscan:  holds information to be stored in the new scsi_device
+ * @sdevnew:   store the address of the newly allocated scsi_device
  * @inq_result:        holds the result of a previous INQUIRY to the LUN
  * @bflags:    black/white list flag
  *
  * Description:
- *     Allocate and initialize a Scsi_Device matching sdevscan. Optionally
+ *     Allocate and initialize a scsi_device matching sdevscan. Optionally
  *     set fields based on values in *@bflags. If @sdevnew is not
- *     NULL, store the address of the new Scsi_Device in *@sdevnew (needed
+ *     NULL, store the address of the new scsi_device in *@sdevnew (needed
  *     when scanning a particular LUN).
  *
  * Return:
- *     SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device
- *     SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
+ *     SCSI_SCAN_NO_RESPONSE: could not allocate or setup a scsi_device
+ *     SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
  **/
 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.
@@ -531,7 +660,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:
@@ -543,6 +673,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
        case TYPE_MEDIUM_CHANGER:
        case TYPE_ENCLOSURE:
        case TYPE_COMM:
+       case TYPE_RAID:
+       case TYPE_RBC:
                sdev->writeable = 1;
                break;
        case TYPE_WORM:
@@ -564,7 +696,7 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
         *
         * The above is vague, as it implies that we could treat 001 and
         * 011 the same. Stay compatible with previous code, and create a
-        * Scsi_Device for a PQ of 1
+        * scsi_device for a PQ of 1
         *
         * Don't set the device offline here; rather let the upper
         * level drivers eval the PQ to decide whether they should
@@ -584,12 +716,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
        if (inq_result[7] & 0x10)
                sdev->sdtr = 1;
 
-       sprintf(sdev->devfs_name, "scsi/host%d/bus%d/target%d/lun%d",
-                               sdev->host->host_no, sdev->channel,
-                               sdev->id, sdev->lun);
-
        /*
-        * End driverfs/devfs code.
+        * End sysfs code.
         */
 
        if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) &&
@@ -603,6 +731,20 @@ 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;
+
+       /*
+        * Maximum 512 sector transfer length
+        * broken RA4x00 Compaq Disk Array
+        */
+       if (*bflags & BLIST_MAX_512)
+               blk_queue_max_sectors(sdev->request_queue, 512);
+
        /*
         * Some devices may not want to have a start command automatically
         * issued when a device is added.
@@ -610,40 +752,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;
 
@@ -666,23 +777,52 @@ 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)
-               sdev->host->hostt->slave_configure(sdev);
+       if (*bflags & BLIST_RETRY_HWERROR)
+               sdev->retry_hwerror = 1;
+
+       transport_configure_device(&sdev->sdev_gendev);
+
+       if (sdev->host->hostt->slave_configure) {
+               int ret = sdev->host->hostt->slave_configure(sdev);
+               if (ret) {
+                       /*
+                        * if LLDD reports slave not present, don't clutter
+                        * console with alloc failure messages
+                        */
+                       if (ret != -ENXIO) {
+                               sdev_printk(KERN_ERR, sdev,
+                                       "failed to configure device\n");
+                       }
+                       return SCSI_SCAN_NO_RESPONSE;
+               }
+       }
 
        /*
         * Ok, the device is now all set up, we can
         * register it and tell the rest of the kernel
         * about it.
         */
-       scsi_sysfs_add_sdev(sdev);
+       if (scsi_sysfs_add_sdev(sdev) != 0)
+               return SCSI_SCAN_NO_RESPONSE;
 
        return SCSI_SCAN_LUN_PRESENT;
 }
 
+static inline void scsi_destroy_sdev(struct scsi_device *sdev)
+{
+       if (sdev->host->hostt->slave_destroy)
+               sdev->host->hostt->slave_destroy(sdev);
+       transport_destroy_device(&sdev->sdev_gendev);
+       put_device(&sdev->sdev_gendev);
+}
+
+
 /**
  * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it
- * @sdevscan:  probe the LUN corresponding to this Scsi_Device
- * @sdevnew:   store the value of any new Scsi_Device allocated
+ * @starget:   pointer to target device structure
+ * @lun:       LUN of target device
+ * @sdevscan:  probe the LUN corresponding to this scsi_device
+ * @sdevnew:   store the value of any new scsi_device allocated
  * @bflagsp:   store bflags here if not NULL
  *
  * Description:
@@ -690,55 +830,54 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
  *     allocate and set it up by calling scsi_add_lun.
  *
  * Return:
- *     SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device
+ *     SCSI_SCAN_NO_RESPONSE: could not allocate or setup a scsi_device
  *     SCSI_SCAN_TARGET_PRESENT: target responded, but no device is
  *         attached at the LUN
- *     SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
+ *     SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
  **/
-static int scsi_probe_and_add_lun(struct Scsi_Host *host,
-               uint channel, uint id, uint lun, int *bflagsp,
-               struct scsi_device **sdevp, int rescan)
+static int scsi_probe_and_add_lun(struct scsi_target *starget,
+                                 uint lun, int *bflagsp,
+                                 struct scsi_device **sdevp, int rescan,
+                                 void *hostdata)
 {
        struct scsi_device *sdev;
-       struct scsi_request *sreq;
        unsigned char *result;
-       int bflags, res = SCSI_SCAN_NO_RESPONSE;
+       int bflags, res = SCSI_SCAN_NO_RESPONSE, result_len = 256;
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
 
        /*
         * The rescan flag is used as an optimization, the first scan of a
         * host adapter calls into here with rescan == 0.
         */
-       if (rescan) {
-               sdev = scsi_device_lookup(host, channel, id, lun);
-               if (sdev) {
+       sdev = scsi_device_lookup_by_target(starget, lun);
+       if (sdev) {
+               if (rescan || sdev->sdev_state != SDEV_CREATED) {
                        SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
-                               "scsi scan: device exists on <%d:%d:%d:%d>\n",
-                               host->host_no, channel, id, lun));
+                               "scsi scan: device exists on %s\n",
+                               sdev->sdev_gendev.bus_id));
                        if (sdevp)
                                *sdevp = sdev;
+                       else
+                               scsi_device_put(sdev);
+
                        if (bflagsp)
                                *bflagsp = scsi_get_device_flags(sdev,
                                                                 sdev->vendor,
                                                                 sdev->model);
-                       /* XXX: bandaid until callers do refcounting */
-                       scsi_device_put(sdev);
                        return SCSI_SCAN_LUN_PRESENT;
                }
-       }
-
-       sdev = scsi_alloc_sdev(host, channel, id, lun);
+               scsi_device_put(sdev);
+       } else
+               sdev = scsi_alloc_sdev(starget, lun, hostdata);
        if (!sdev)
                goto out;
-       sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
-       if (!sreq)
-               goto out_free_sdev;
-       result = kmalloc(256, GFP_ATOMIC |
-                       (host->unchecked_isa_dma) ? __GFP_DMA : 0);
+
+       result = kmalloc(result_len, GFP_ATOMIC |
+                       ((shost->unchecked_isa_dma) ? __GFP_DMA : 0));
        if (!result)
-               goto out_free_sreq;
+               goto out_free_sdev;
 
-       scsi_probe_lun(sreq, result, &bflags);
-       if (sreq->sr_result)
+       if (scsi_probe_lun(sdev, result, result_len, &bflags))
                goto out_free_result;
 
        /*
@@ -762,11 +901,24 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
                goto out_free_result;
        }
 
+       /*
+        * Non-standard SCSI targets may set the PDT to 0x1f (unknown or
+        * no device type) instead of using the Peripheral Qualifier to
+        * indicate that no LUN is present.  For example, USB UFI does this.
+        */
+       if (starget->pdt_1f_for_no_lun && (result[0] & 0x1f) == 0x1f) {
+               SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
+                                       "scsi scan: peripheral device type"
+                                       " of 31, no device added\n"));
+               res = SCSI_SCAN_TARGET_PRESENT;
+               goto out_free_result;
+       }
+
        res = scsi_add_lun(sdev, result, &bflags);
        if (res == SCSI_SCAN_LUN_PRESENT) {
                if (bflags & BLIST_KEY) {
                        sdev->lockable = 0;
-                       scsi_unlock_floptical(sreq, result);
+                       scsi_unlock_floptical(sdev, result);
                }
                if (bflagsp)
                        *bflagsp = bflags;
@@ -774,26 +926,25 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
 
  out_free_result:
        kfree(result);
- out_free_sreq:
-       scsi_release_request(sreq);
  out_free_sdev:
        if (res == SCSI_SCAN_LUN_PRESENT) {
-               if (sdevp)
-                       *sdevp = sdev;
-       } else {
-               if (sdev->host->hostt->slave_destroy)
-                       sdev->host->hostt->slave_destroy(sdev);
-               if (sdev->host->transportt->cleanup)
-                       sdev->host->transportt->cleanup(sdev);
-               put_device(&sdev->sdev_gendev);
-       }
+               if (sdevp) {
+                       if (scsi_device_get(sdev) == 0) {
+                               *sdevp = sdev;
+                       } else {
+                               __scsi_remove_device(sdev);
+                               res = SCSI_SCAN_NO_RESPONSE;
+                       }
+               }
+       } else
+               scsi_destroy_sdev(sdev);
  out:
        return res;
 }
 
 /**
  * scsi_sequential_lun_scan - sequentially scan a SCSI target
- * @sdevscan:  scan the host, channel, and id of this Scsi_Device
+ * @starget:   pointer to target structure to scan
  * @bflags:    black/white list flag for LUN 0
  * @lun0_res:  result of scanning LUN 0
  *
@@ -804,14 +955,15 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
  *
  *     Modifies sdevscan->lun.
  **/
-static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel,
-               uint id, int bflags, int lun0_res, int scsi_level, int rescan)
+static void scsi_sequential_lun_scan(struct scsi_target *starget,
+                                    int bflags, int lun0_res, int scsi_level,
+                                    int rescan)
 {
        unsigned int sparse_lun, lun, max_dev_lun;
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
 
        SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of"
-                       " host %d channel %d id %d\n", shost->host_no,
-                       channel, id));
+                                   "%s\n", starget->dev.bus_id));
 
        max_dev_lun = min(max_scsi_luns, shost->max_lun);
        /*
@@ -873,8 +1025,8 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel,
         * sparse_lun.
         */
        for (lun = 1; lun < max_dev_lun; ++lun)
-               if ((scsi_probe_and_add_lun(shost, channel, id, lun,
-                     NULL, NULL, rescan) != SCSI_SCAN_LUN_PRESENT) &&
+               if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan,
+                                           NULL) != SCSI_SCAN_LUN_PRESENT) &&
                    !sparse_lun)
                        return;
 }
@@ -910,9 +1062,41 @@ static int scsilun_to_int(struct scsi_lun *scsilun)
        return lun;
 }
 
+/**
+ * int_to_scsilun: reverts an int into a scsi_lun
+ * @int:        integer to be reverted
+ * @scsilun:   struct scsi_lun to be set.
+ *
+ * Description:
+ *     Reverts the functionality of the scsilun_to_int, which packed
+ *     an 8-byte lun value into an int. This routine unpacks the int
+ *     back into the lun value.
+ *     Note: the scsilun_to_int() routine does not truly handle all
+ *     8bytes of the lun value. This functions restores only as much
+ *     as was set by the routine.
+ *
+ * Notes:
+ *     Given an integer : 0x0b030a04,  this function returns a
+ *     scsi_lun of : struct scsi_lun of: 0a 04 0b 03 00 00 00 00
+ *
+ **/
+void int_to_scsilun(unsigned int lun, struct scsi_lun *scsilun)
+{
+       int i;
+
+       memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun));
+
+       for (i = 0; i < sizeof(lun); i += 2) {
+               scsilun->scsi_lun[i] = (lun >> 8) & 0xFF;
+               scsilun->scsi_lun[i+1] = lun & 0xFF;
+               lun = lun >> 16;
+       }
+}
+EXPORT_SYMBOL(int_to_scsilun);
+
 /**
  * scsi_report_lun_scan - Scan using SCSI REPORT LUN results
- * @sdevscan:  scan the host, channel, and id of this Scsi_Device
+ * @sdevscan:  scan the host, channel, and id of this scsi_device
  *
  * Description:
  *     If @sdevscan is for a SCSI-3 or up device, send a REPORT LUN
@@ -925,7 +1109,7 @@ static int scsilun_to_int(struct scsi_lun *scsilun)
  *     0: scan completed (or no memory, so further scanning is futile)
  *     1: no report lun scan, or not configured
  **/
-static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
+static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
                                int rescan)
 {
        char devname[64];
@@ -934,29 +1118,40 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
        unsigned int lun;
        unsigned int num_luns;
        unsigned int retries;
+       int result;
        struct scsi_lun *lunp, *lun_data;
-       struct scsi_request *sreq;
        u8 *data;
+       struct scsi_sense_hdr sshdr;
+       struct scsi_device *sdev;
+       struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+       int ret = 0;
 
        /*
         * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
         * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does
         * support more than 8 LUNs.
         */
-       if ((bflags & BLIST_NOREPORTLUN) || 
-            sdev->scsi_level < SCSI_2 ||
-           (sdev->scsi_level < SCSI_3 && 
-            (!(bflags & BLIST_REPORTLUN2) || sdev->host->max_lun <= 8)) )
+       if (bflags & BLIST_NOREPORTLUN)
+               return 1;
+       if (starget->scsi_level < SCSI_2 &&
+           starget->scsi_level != SCSI_UNKNOWN)
+               return 1;
+       if (starget->scsi_level < SCSI_3 &&
+           (!(bflags & BLIST_REPORTLUN2) || shost->max_lun <= 8))
                return 1;
        if (bflags & BLIST_NOLUN)
                return 0;
 
-       sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
-       if (!sreq)
-               goto out;
+       if (!(sdev = scsi_device_lookup_by_target(starget, 0))) {
+               sdev = scsi_alloc_sdev(starget, 0, NULL);
+               if (!sdev)
+                       return 0;
+               if (scsi_device_get(sdev))
+                       return 0;
+       }
 
        sprintf(devname, "host %d channel %d id %d",
-               sdev->host->host_no, sdev->channel, sdev->id);
+               shost->host_no, sdev->channel, sdev->id);
 
        /*
         * Allocate enough to hold the header (the same size as one scsi_lun)
@@ -971,8 +1166,10 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
        length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun);
        lun_data = kmalloc(length, GFP_ATOMIC |
                           (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0));
-       if (!lun_data)
-               goto out_release_request;
+       if (!lun_data) {
+               printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+               goto out;
+       }
 
        scsi_cmd[0] = REPORT_LUNS;
 
@@ -991,8 +1188,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
 
        scsi_cmd[10] = 0;       /* reserved */
        scsi_cmd[11] = 0;       /* control */
-       sreq->sr_cmd_len = 0;
-       sreq->sr_data_direction = DMA_FROM_DEVICE;
 
        /*
         * We can get a UNIT ATTENTION, for example a power on/reset, so
@@ -1008,26 +1203,29 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
                SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending"
                                " REPORT LUNS to %s (try %d)\n", devname,
                                retries));
-               scsi_wait_req(sreq, scsi_cmd, lun_data, length,
-                               SCSI_TIMEOUT + 4*HZ, 3);
+
+               result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE,
+                                         lun_data, length, &sshdr,
+                                         SCSI_TIMEOUT + 4 * HZ, 3);
+
                SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS"
-                               " %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)
+                               " %s (try %d) result 0x%x\n", result
+                               ?  "failed" : "successful", retries, result));
+               if (result == 0)
                        break;
+               else if (scsi_sense_valid(&sshdr)) {
+                       if (sshdr.sense_key != UNIT_ATTENTION)
+                               break;
+               }
        }
 
-       if (sreq->sr_result) {
+       if (result) {
                /*
                 * The device probably does not support a REPORT LUN command
                 */
-               kfree(lun_data);
-               scsi_release_request(sreq);
-               return 1;
+               ret = 1;
+               goto out_err;
        }
-       scsi_release_request(sreq);
 
        /*
         * Get the length from the first four bytes of lun_data.
@@ -1045,9 +1243,8 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
                num_luns = max_scsi_report_luns;
        }
 
-       SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of"
-                       " host %d channel %d id %d\n", sdev->host->host_no,
-                       sdev->channel, sdev->id));
+       SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev,
+               "scsi scan: REPORT LUN scan\n"));
 
        /*
         * Scan the luns in lun_data. The entry at offset 0 is really
@@ -1073,10 +1270,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
                        for (i = 0; i < sizeof(struct scsi_lun); i++)
                                printk("%02x", data[i]);
                        printk(" has a LUN larger than currently supported.\n");
-               } else if (lun == 0) {
-                       /*
-                        * LUN 0 has already been scanned.
-                        */
                } else if (lun > sdev->host->max_lun) {
                        printk(KERN_WARNING "scsi: %s lun%d has a LUN larger"
                               " than allowed by the host adapter\n",
@@ -1084,47 +1277,68 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
                } else {
                        int res;
 
-                       res = scsi_probe_and_add_lun(sdev->host, sdev->channel,
-                               sdev->id, lun, NULL, NULL, rescan);
+                       res = scsi_probe_and_add_lun(starget,
+                               lun, NULL, NULL, rescan, NULL);
                        if (res == SCSI_SCAN_NO_RESPONSE) {
                                /*
                                 * Got some results, but now none, abort.
                                 */
-                               printk(KERN_ERR "scsi: Unexpected response"
-                                      " from %s lun %d while scanning, scan"
-                                      " aborted\n", devname, lun);
+                               sdev_printk(KERN_ERR, sdev,
+                                       "Unexpected response"
+                                       " from lun %d while scanning, scan"
+                                       " aborted\n", lun);
                                break;
                        }
                }
        }
 
+ out_err:
        kfree(lun_data);
-       return 0;
-
- out_release_request:
-       scsi_release_request(sreq);
  out:
-       /*
-        * We are out of memory, don't try scanning any further.
-        */
-       printk(ALLOC_FAILURE_MSG, __FUNCTION__);
-       return 0;
+       scsi_device_put(sdev);
+       if (sdev->sdev_state == SDEV_CREATED)
+               /*
+                * the sdev we used didn't appear in the report luns scan
+                */
+               scsi_destroy_sdev(sdev);
+       return ret;
 }
 
-struct scsi_device *scsi_add_device(struct Scsi_Host *shost,
-                                   uint channel, uint id, uint lun)
+struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
+                                     uint id, uint lun, void *hostdata)
 {
-       struct scsi_device *sdev;
-       int res;
+       struct scsi_device *sdev = ERR_PTR(-ENODEV);
+       struct device *parent = &shost->shost_gendev;
+       struct scsi_target *starget;
 
-       down(&shost->scan_mutex);
-       res = scsi_probe_and_add_lun(shost, channel, id, lun, NULL, &sdev, 1);
-       if (res != SCSI_SCAN_LUN_PRESENT)
-               sdev = ERR_PTR(-ENODEV);
-       up(&shost->scan_mutex);
+       starget = scsi_alloc_target(parent, channel, id);
+       if (!starget)
+               return ERR_PTR(-ENOMEM);
+
+       get_device(&starget->dev);
+       mutex_lock(&shost->scan_mutex);
+       if (scsi_host_scan_allowed(shost))
+               scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
+       mutex_unlock(&shost->scan_mutex);
+       scsi_target_reap(starget);
+       put_device(&starget->dev);
 
        return sdev;
 }
+EXPORT_SYMBOL(__scsi_add_device);
+
+int scsi_add_device(struct Scsi_Host *host, uint channel,
+                   uint target, uint lun)
+{
+       struct scsi_device *sdev = 
+               __scsi_add_device(host, channel, target, lun, NULL);
+       if (IS_ERR(sdev))
+               return PTR_ERR(sdev);
+
+       scsi_device_put(sdev);
+       return 0;
+}
+EXPORT_SYMBOL(scsi_add_device);
 
 void scsi_rescan_device(struct device *dev)
 {
@@ -1140,32 +1354,15 @@ 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
- *     target.
- * @sdevsca:   Scsi_Device handle for scanning
- * @shost:     host to scan
- * @channel:   channel to scan
- * @id:                target id to scan
- *
- * Description:
- *     Scan the target id on @shost, @channel, and @id. Scan at least LUN
- *     0, and possibly all LUNs on the target id.
- *
- *     Use the pre-allocated @sdevscan as a handle for the scanning. This
- *     function sets sdevscan->host, sdevscan->id and sdevscan->lun; the
- *     scanning functions modify sdevscan->lun.
- *
- *     First try a REPORT LUN scan, if that does not scan the target, do a
- *     sequential scan of LUNs on the target id.
- **/
-static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,
-                            unsigned int id, unsigned int lun, int rescan)
+static void __scsi_scan_target(struct device *parent, unsigned int channel,
+               unsigned int id, unsigned int lun, int rescan)
 {
+       struct Scsi_Host *shost = dev_to_shost(parent);
        int bflags = 0;
        int res;
-       struct scsi_device *sdev;
+       struct scsi_target *starget;
 
        if (shost->this_id == id)
                /*
@@ -1173,41 +1370,70 @@ static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,
                 */
                return;
 
+       starget = scsi_alloc_target(parent, channel, id);
+       if (!starget)
+               return;
+
+       get_device(&starget->dev);
        if (lun != SCAN_WILD_CARD) {
                /*
                 * Scan for a specific host/chan/id/lun.
                 */
-               scsi_probe_and_add_lun(shost, channel, id, lun, NULL, NULL,
-                                      rescan);
-               return;
+               scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL);
+               goto out_reap;
        }
 
        /*
         * Scan LUN 0, if there is some response, scan further. Ideally, we
         * would not configure LUN 0 until all LUNs are scanned.
         */
-       res = scsi_probe_and_add_lun(shost, channel, id, 0, &bflags, &sdev,
-                                    rescan);
-       if (res == SCSI_SCAN_LUN_PRESENT) {
-               if (scsi_report_lun_scan(sdev, bflags, rescan) != 0)
+       res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL);
+       if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) {
+               if (scsi_report_lun_scan(starget, bflags, rescan) != 0)
                        /*
                         * The REPORT LUN did not scan the target,
                         * do a sequential scan.
                         */
-                       scsi_sequential_lun_scan(shost, channel, id, bflags,
-                                       res, sdev->scsi_level, rescan);
-       } else if (res == SCSI_SCAN_TARGET_PRESENT) {
-               /*
-                * There's a target here, but lun 0 is offline so we
-                * can't use the report_lun scan.  Fall back to a
-                * sequential lun scan with a bflags of SPARSELUN and
-                * a default scsi level of SCSI_2
-                */
-               scsi_sequential_lun_scan(shost, channel, id, BLIST_SPARSELUN,
-                               SCSI_SCAN_TARGET_PRESENT, SCSI_2, rescan);
+                       scsi_sequential_lun_scan(starget, bflags,
+                                       res, starget->scsi_level, rescan);
        }
+
+ out_reap:
+       /* now determine if the target has any children at all
+        * and if not, nuke it */
+       scsi_target_reap(starget);
+
+       put_device(&starget->dev);
 }
 
+/**
+ * scsi_scan_target - scan a target id, possibly including all LUNs on the
+ *     target.
+ * @parent:    host to scan
+ * @channel:   channel to scan
+ * @id:                target id to scan
+ * @lun:       Specific LUN to scan or SCAN_WILD_CARD
+ * @rescan:    passed to LUN scanning routines
+ *
+ * Description:
+ *     Scan the target id on @parent, @channel, and @id. Scan at least LUN 0,
+ *     and possibly all LUNs on the target id.
+ *
+ *     First try a REPORT LUN scan, if that does not scan the target, do a
+ *     sequential scan of LUNs on the target id.
+ **/
+void scsi_scan_target(struct device *parent, unsigned int channel,
+                     unsigned int id, unsigned int lun, int rescan)
+{
+       struct Scsi_Host *shost = dev_to_shost(parent);
+
+       mutex_lock(&shost->scan_mutex);
+       if (scsi_host_scan_allowed(shost))
+               __scsi_scan_target(parent, channel, id, lun, rescan);
+       mutex_unlock(&shost->scan_mutex);
+}
+EXPORT_SYMBOL(scsi_scan_target);
+
 static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
                              unsigned int id, unsigned int lun, int rescan)
 {
@@ -1231,30 +1457,37 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
                                order_id = shost->max_id - id - 1;
                        else
                                order_id = id;
-                       scsi_scan_target(shost, channel, order_id, lun, rescan);
+                       __scsi_scan_target(&shost->shost_gendev, channel,
+                                       order_id, lun, rescan);
                }
        else
-               scsi_scan_target(shost, channel, id, lun, rescan);
+               __scsi_scan_target(&shost->shost_gendev, channel,
+                               id, lun, rescan);
 }
 
 int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
                            unsigned int id, unsigned int lun, int rescan)
 {
-       SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "%s: <%u:%u:%u:%u>\n",
-               __FUNCTION__, shost->host_no, channel, id, lun));
+       SCSI_LOG_SCAN_BUS(3, shost_printk (KERN_INFO, shost,
+               "%s: <%u:%u:%u>\n",
+               __FUNCTION__, channel, id, lun));
 
        if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
            ((id != SCAN_WILD_CARD) && (id > shost->max_id)) ||
            ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
                return -EINVAL;
 
-       down(&shost->scan_mutex);
-       if (channel == SCAN_WILD_CARD) 
-               for (channel = 0; channel <= shost->max_channel; channel++)
+       mutex_lock(&shost->scan_mutex);
+       if (scsi_host_scan_allowed(shost)) {
+               if (channel == SCAN_WILD_CARD)
+                       for (channel = 0; channel <= shost->max_channel;
+                            channel++)
+                               scsi_scan_channel(shost, channel, id, lun,
+                                                 rescan);
+               else
                        scsi_scan_channel(shost, channel, id, lun, rescan);
-       else
-               scsi_scan_channel(shost, channel, id, lun, rescan);
-       up(&shost->scan_mutex);
+       }
+       mutex_unlock(&shost->scan_mutex);
 
        return 0;
 }
@@ -1268,26 +1501,21 @@ 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);
 
 void scsi_forget_host(struct Scsi_Host *shost)
 {
-       struct scsi_device *sdev, *tmp;
+       struct scsi_device *sdev;
        unsigned long flags;
 
-       /*
-        * Ok, this look a bit strange.  We always look for the first device
-        * on the list as scsi_remove_device removes them from it - thus we
-        * also have to release the lock.
-        * We don't need to get another reference to the device before
-        * releasing the lock as we already own the reference from
-        * scsi_register_device that's release in scsi_remove_device.  And
-        * after that we don't look at sdev anymore.
-        */
+ restart:
        spin_lock_irqsave(shost->host_lock, flags);
-       list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
+       list_for_each_entry(sdev, &shost->__devices, siblings) {
+               if (sdev->sdev_state == SDEV_DEL)
+                       continue;
                spin_unlock_irqrestore(shost->host_lock, flags);
-               scsi_remove_device(sdev);
-               spin_lock_irqsave(shost->host_lock, flags);
+               __scsi_remove_device(sdev);
+               goto restart;
        }
        spin_unlock_irqrestore(shost->host_lock, flags);
 }
@@ -1295,16 +1523,16 @@ void scsi_forget_host(struct Scsi_Host *shost)
 /*
  * Function:    scsi_get_host_dev()
  *
- * Purpose:     Create a Scsi_Device that points to the host adapter itself.
+ * Purpose:     Create a scsi_device that points to the host adapter itself.
  *
- * Arguments:   SHpnt   - Host that needs a Scsi_Device
+ * Arguments:   SHpnt   - Host that needs a scsi_device
  *
  * Lock status: None assumed.
  *
- * Returns:     The Scsi_Device or NULL
+ * Returns:     The scsi_device or NULL
  *
  * Notes:
- *     Attach a single Scsi_Device to the Scsi_Host - this should
+ *     Attach a single scsi_device to the Scsi_Host - this should
  *     be made to look like a "pseudo-device" that points to the
  *     HA itself.
  *
@@ -1314,21 +1542,34 @@ void scsi_forget_host(struct Scsi_Host *shost)
  */
 struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
 {
-       struct scsi_device *sdev;
+       struct scsi_device *sdev = NULL;
+       struct scsi_target *starget;
 
-       sdev = scsi_alloc_sdev(shost, 0, shost->this_id, 0);
+       mutex_lock(&shost->scan_mutex);
+       if (!scsi_host_scan_allowed(shost))
+               goto out;
+       starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id);
+       if (!starget)
+               goto out;
+
+       sdev = scsi_alloc_sdev(starget, 0, NULL);
        if (sdev) {
+               sdev->sdev_gendev.parent = get_device(&starget->dev);
                sdev->borken = 0;
        }
+       put_device(&starget->dev);
+ out:
+       mutex_unlock(&shost->scan_mutex);
        return sdev;
 }
+EXPORT_SYMBOL(scsi_get_host_dev);
 
 /*
  * Function:    scsi_free_host_dev()
  *
  * Purpose:     Free a scsi_device that points to the host adapter itself.
  *
- * Arguments:   SHpnt   - Host that needs a Scsi_Device
+ * Arguments:   SHpnt   - Host that needs a scsi_device
  *
  * Lock status: None assumed.
  *
@@ -1340,9 +1581,7 @@ void scsi_free_host_dev(struct scsi_device *sdev)
 {
        BUG_ON(sdev->id != sdev->host->this_id);
 
-       if (sdev->host->hostt->slave_destroy)
-               sdev->host->hostt->slave_destroy(sdev);
-       if (sdev->host->transportt->cleanup)
-               sdev->host->transportt->cleanup(sdev);
-       put_device(&sdev->sdev_gendev);
+       scsi_destroy_sdev(sdev);
 }
+EXPORT_SYMBOL(scsi_free_host_dev);
+