Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/agpgart
[pandora-kernel.git] / drivers / scsi / scsi_scan.c
index 31d05ab..b83d03c 100644 (file)
@@ -29,7 +29,9 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
-#include <asm/semaphore.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/spinlock.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -87,6 +89,17 @@ module_param_named(max_luns, max_scsi_luns, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(max_luns,
                 "last scsi LUN (should be between 1 and 2^32-1)");
 
+#ifdef CONFIG_SCSI_SCAN_ASYNC
+#define SCSI_SCAN_TYPE_DEFAULT "async"
+#else
+#define SCSI_SCAN_TYPE_DEFAULT "sync"
+#endif
+
+static char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT;
+
+module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO);
+MODULE_PARM_DESC(scan, "sync, async or none");
+
 /*
  * max_scsi_report_luns: the maximum number of LUNS that will be
  * returned from the REPORT LUNS command. 8 times this value must
@@ -108,6 +121,71 @@ MODULE_PARM_DESC(inq_timeout,
                 "Timeout (in seconds) waiting for devices to answer INQUIRY."
                 " Default is 5. Some non-compliant devices need more.");
 
+static DEFINE_SPINLOCK(async_scan_lock);
+static LIST_HEAD(scanning_hosts);
+
+struct async_scan_data {
+       struct list_head list;
+       struct Scsi_Host *shost;
+       struct completion prev_finished;
+};
+
+/**
+ * scsi_complete_async_scans - Wait for asynchronous scans to complete
+ *
+ * When this function returns, any host which started scanning before
+ * this function was called will have finished its scan.  Hosts which
+ * started scanning after this function was called may or may not have
+ * finished.
+ */
+int scsi_complete_async_scans(void)
+{
+       struct async_scan_data *data;
+
+       do {
+               if (list_empty(&scanning_hosts))
+                       return 0;
+               /* If we can't get memory immediately, that's OK.  Just
+                * sleep a little.  Even if we never get memory, the async
+                * scans will finish eventually.
+                */
+               data = kmalloc(sizeof(*data), GFP_KERNEL);
+               if (!data)
+                       msleep(1);
+       } while (!data);
+
+       data->shost = NULL;
+       init_completion(&data->prev_finished);
+
+       spin_lock(&async_scan_lock);
+       /* Check that there's still somebody else on the list */
+       if (list_empty(&scanning_hosts))
+               goto done;
+       list_add_tail(&data->list, &scanning_hosts);
+       spin_unlock(&async_scan_lock);
+
+       printk(KERN_INFO "scsi: waiting for bus probes to complete ...\n");
+       wait_for_completion(&data->prev_finished);
+
+       spin_lock(&async_scan_lock);
+       list_del(&data->list);
+       if (!list_empty(&scanning_hosts)) {
+               struct async_scan_data *next = list_entry(scanning_hosts.next,
+                               struct async_scan_data, list);
+               complete(&next->prev_finished);
+       }
+ done:
+       spin_unlock(&async_scan_lock);
+
+       kfree(data);
+       return 0;
+}
+
+#ifdef MODULE
+/* Only exported for the benefit of scsi_wait_scan */
+EXPORT_SYMBOL_GPL(scsi_complete_async_scans);
+#endif
+
 /**
  * scsi_unlock_floptical - unlock device via a special MODE SENSE command
  * @sdev:      scsi device to send command to
@@ -266,6 +344,18 @@ static struct scsi_target *__scsi_find_target(struct device *parent,
        return found_starget;
 }
 
+/**
+ * scsi_alloc_target - allocate a new or find an existing target
+ * @parent:    parent of the target (need not be a scsi host)
+ * @channel:   target channel number (zero if no channels)
+ * @id:                target id number
+ *
+ * Return an existing target if one exists, provided it hasn't already
+ * gone into STARGET_DEL state, otherwise allocate a new target.
+ *
+ * The target is returned with an incremented reference, so the caller
+ * is responsible for both reaping and doing a last put
+ */
 static struct scsi_target *scsi_alloc_target(struct device *parent,
                                             int channel, uint id)
 {
@@ -331,14 +421,15 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
                        return NULL;
                }
        }
+       get_device(dev);
 
        return starget;
 
  found:
        found_target->reap_ref++;
        spin_unlock_irqrestore(shost->host_lock, flags);
-       put_device(parent);
        if (found_target->state != STARGET_DEL) {
+               put_device(parent);
                kfree(starget);
                return found_target;
        }
@@ -349,9 +440,10 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
        goto retry;
 }
 
-static void scsi_target_reap_usercontext(void *data)
+static void scsi_target_reap_usercontext(struct work_struct *work)
 {
-       struct scsi_target *starget = data;
+       struct scsi_target *starget =
+               container_of(work, struct scsi_target, ew.work);
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
        unsigned long flags;
 
@@ -387,7 +479,7 @@ void scsi_target_reap(struct scsi_target *starget)
                starget->state = STARGET_DEL;
                spin_unlock_irqrestore(shost->host_lock, flags);
                execute_in_process_context(scsi_target_reap_usercontext,
-                                          starget, &starget->ew);
+                                          &starget->ew);
                return;
 
        }
@@ -606,7 +698,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
  *     SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
  **/
 static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
-               int *bflags)
+               int *bflags, int async)
 {
        /*
         * XXX do not save the inquiry, since it can change underneath us,
@@ -618,12 +710,22 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
         * scanning run at their own risk, or supply a user level program
         * that can correctly scan.
         */
-       sdev->inquiry = kmalloc(sdev->inquiry_len, GFP_ATOMIC);
-       if (sdev->inquiry == NULL) {
+
+       /*
+        * Copy at least 36 bytes of INQUIRY data, so that we don't
+        * dereference unallocated memory when accessing the Vendor,
+        * Product, and Revision strings.  Badly behaved devices may set
+        * the INQUIRY Additional Length byte to a small value, indicating
+        * these strings are invalid, but often they contain plausible data
+        * nonetheless.  It doesn't matter if the device sent < 36 bytes
+        * total, since scsi_probe_lun() initializes inq_result with 0s.
+        */
+       sdev->inquiry = kmemdup(inq_result,
+                               max_t(size_t, sdev->inquiry_len, 36),
+                               GFP_ATOMIC);
+       if (sdev->inquiry == NULL)
                return SCSI_SCAN_NO_RESPONSE;
-       }
 
-       memcpy(sdev->inquiry, inq_result, sdev->inquiry_len);
        sdev->vendor = (char *) (sdev->inquiry + 8);
        sdev->model = (char *) (sdev->inquiry + 16);
        sdev->rev = (char *) (sdev->inquiry + 32);
@@ -640,6 +742,14 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
                sdev->no_uld_attach = 1;
 
        switch (sdev->type = (inq_result[0] & 0x1f)) {
+       case TYPE_RBC:
+               /* RBC devices can return SCSI-3 compliance and yet
+                * still not support REPORT LUNS, so make them act as
+                * BLIST_NOREPORTLUN unless BLIST_REPORTLUN2 is
+                * specifically set */
+               if ((*bflags & BLIST_REPORTLUN2) == 0)
+                       *bflags |= BLIST_NOREPORTLUN;
+               /* fall through */
        case TYPE_TAPE:
        case TYPE_DISK:
        case TYPE_PRINTER:
@@ -650,11 +760,17 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
        case TYPE_ENCLOSURE:
        case TYPE_COMM:
        case TYPE_RAID:
-       case TYPE_RBC:
                sdev->writeable = 1;
                break;
-       case TYPE_WORM:
        case TYPE_ROM:
+               /* MMC devices can return SCSI-3 compliance and yet
+                * still not support REPORT LUNS, so make them act as
+                * BLIST_NOREPORTLUN unless BLIST_REPORTLUN2 is
+                * specifically set */
+               if ((*bflags & BLIST_REPORTLUN2) == 0)
+                       *bflags |= BLIST_NOREPORTLUN;
+               /* fall through */
+       case TYPE_WORM:
                sdev->writeable = 0;
                break;
        default:
@@ -782,7 +898,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
         * register it and tell the rest of the kernel
         * about it.
         */
-       if (scsi_sysfs_add_sdev(sdev) != 0)
+       if (!async && scsi_sysfs_add_sdev(sdev) != 0)
                return SCSI_SCAN_NO_RESPONSE;
 
        return SCSI_SCAN_LUN_PRESENT;
@@ -951,7 +1067,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
                goto out_free_result;
        }
 
-       res = scsi_add_lun(sdev, result, &bflags);
+       res = scsi_add_lun(sdev, result, &bflags, shost->async_scan);
        if (res == SCSI_SCAN_LUN_PRESENT) {
                if (bflags & BLIST_KEY) {
                        sdev->lockable = 0;
@@ -1341,7 +1457,6 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
        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);
@@ -1400,7 +1515,6 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel,
        if (!starget)
                return;
 
-       get_device(&starget->dev);
        if (lun != SCAN_WILD_CARD) {
                /*
                 * Scan for a specific host/chan/id/lun.
@@ -1453,6 +1567,12 @@ void scsi_scan_target(struct device *parent, unsigned int channel,
 {
        struct Scsi_Host *shost = dev_to_shost(parent);
 
+       if (strncmp(scsi_scan_type, "none", 4) == 0)
+               return;
+
+       if (!shost->async_scan)
+               scsi_complete_async_scans();
+
        mutex_lock(&shost->scan_mutex);
        if (scsi_host_scan_allowed(shost))
                __scsi_scan_target(parent, channel, id, lun, rescan);
@@ -1498,6 +1618,9 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
                "%s: <%u:%u:%u>\n",
                __FUNCTION__, channel, id, lun));
 
+       if (!shost->async_scan)
+               scsi_complete_async_scans();
+
        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)))
@@ -1518,14 +1641,143 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
        return 0;
 }
 
+static void scsi_sysfs_add_devices(struct Scsi_Host *shost)
+{
+       struct scsi_device *sdev;
+       shost_for_each_device(sdev, shost) {
+               if (scsi_sysfs_add_sdev(sdev) != 0)
+                       scsi_destroy_sdev(sdev);
+       }
+}
+
+/**
+ * scsi_prep_async_scan - prepare for an async scan
+ * @shost: the host which will be scanned
+ * Returns: a cookie to be passed to scsi_finish_async_scan()
+ *
+ * Tells the midlayer this host is going to do an asynchronous scan.
+ * It reserves the host's position in the scanning list and ensures
+ * that other asynchronous scans started after this one won't affect the
+ * ordering of the discovered devices.
+ */
+static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost)
+{
+       struct async_scan_data *data;
+
+       if (strncmp(scsi_scan_type, "sync", 4) == 0)
+               return NULL;
+
+       if (shost->async_scan) {
+               printk("%s called twice for host %d", __FUNCTION__,
+                               shost->host_no);
+               dump_stack();
+               return NULL;
+       }
+
+       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               goto err;
+       data->shost = scsi_host_get(shost);
+       if (!data->shost)
+               goto err;
+       init_completion(&data->prev_finished);
+
+       spin_lock(&async_scan_lock);
+       shost->async_scan = 1;
+       if (list_empty(&scanning_hosts))
+               complete(&data->prev_finished);
+       list_add_tail(&data->list, &scanning_hosts);
+       spin_unlock(&async_scan_lock);
+
+       return data;
+
+ err:
+       kfree(data);
+       return NULL;
+}
+
+/**
+ * scsi_finish_async_scan - asynchronous scan has finished
+ * @data: cookie returned from earlier call to scsi_prep_async_scan()
+ *
+ * All the devices currently attached to this host have been found.
+ * This function announces all the devices it has found to the rest
+ * of the system.
+ */
+static void scsi_finish_async_scan(struct async_scan_data *data)
+{
+       struct Scsi_Host *shost;
+
+       if (!data)
+               return;
+
+       shost = data->shost;
+       if (!shost->async_scan) {
+               printk("%s called twice for host %d", __FUNCTION__,
+                               shost->host_no);
+               dump_stack();
+               return;
+       }
+
+       wait_for_completion(&data->prev_finished);
+
+       scsi_sysfs_add_devices(shost);
+
+       spin_lock(&async_scan_lock);
+       shost->async_scan = 0;
+       list_del(&data->list);
+       if (!list_empty(&scanning_hosts)) {
+               struct async_scan_data *next = list_entry(scanning_hosts.next,
+                               struct async_scan_data, list);
+               complete(&next->prev_finished);
+       }
+       spin_unlock(&async_scan_lock);
+
+       scsi_host_put(shost);
+       kfree(data);
+}
+
+static void do_scsi_scan_host(struct Scsi_Host *shost)
+{
+       if (shost->hostt->scan_finished) {
+               unsigned long start = jiffies;
+               if (shost->hostt->scan_start)
+                       shost->hostt->scan_start(shost);
+
+               while (!shost->hostt->scan_finished(shost, jiffies - start))
+                       msleep(10);
+       } else {
+               scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
+                               SCAN_WILD_CARD, 0);
+       }
+}
+
+static int do_scan_async(void *_data)
+{
+       struct async_scan_data *data = _data;
+       do_scsi_scan_host(data->shost);
+       scsi_finish_async_scan(data);
+       return 0;
+}
+
 /**
  * scsi_scan_host - scan the given adapter
  * @shost:     adapter to scan
  **/
 void scsi_scan_host(struct Scsi_Host *shost)
 {
-       scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
-                               SCAN_WILD_CARD, 0);
+       struct async_scan_data *data;
+
+       if (strncmp(scsi_scan_type, "none", 4) == 0)
+               return;
+
+       data = scsi_prep_async_scan(shost);
+       if (!data) {
+               do_scsi_scan_host(shost);
+               return;
+       }
+
+       kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no);
 }
 EXPORT_SYMBOL(scsi_scan_host);
 
@@ -1582,7 +1834,8 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
        if (sdev) {
                sdev->sdev_gendev.parent = get_device(&starget->dev);
                sdev->borken = 0;
-       }
+       } else
+               scsi_target_reap(starget);
        put_device(&starget->dev);
  out:
        mutex_unlock(&shost->scan_mutex);