ARM: 6165/1: trap overflows on highmem pages from kmap_atomic when debugging
[safe/jmp/linux-2.6] / drivers / scsi / hosts.c
index 4e811ca..6660fa9 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/blkdev.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/kthread.h>
 #include <linux/string.h>
 #include <linux/mm.h>
@@ -40,7 +41,7 @@
 #include "scsi_logging.h"
 
 
-static int scsi_host_next_hn;          /* host_no for next new host */
+static atomic_t scsi_host_next_hn;     /* host_no for next new host */
 
 
 static void scsi_host_cls_release(struct device *dev)
@@ -164,8 +165,8 @@ void scsi_remove_host(struct Scsi_Host *shost)
                        return;
                }
        spin_unlock_irqrestore(shost->host_lock, flags);
-       mutex_unlock(&shost->scan_mutex);
        scsi_forget_host(shost);
+       mutex_unlock(&shost->scan_mutex);
        scsi_proc_host_rm(shost);
 
        spin_lock_irqsave(shost->host_lock, flags);
@@ -176,19 +177,24 @@ void scsi_remove_host(struct Scsi_Host *shost)
        transport_unregister_device(&shost->shost_gendev);
        device_unregister(&shost->shost_dev);
        device_del(&shost->shost_gendev);
-       scsi_proc_hostdir_rm(shost->hostt);
 }
 EXPORT_SYMBOL(scsi_remove_host);
 
 /**
- * scsi_add_host - add a scsi host
+ * scsi_add_host_with_dma - add a scsi host with dma device
  * @shost:     scsi host pointer to add
  * @dev:       a struct device of type scsi class
+ * @dma_dev:   dma device for the host
+ *
+ * Note: You rarely need to worry about this unless you're in a
+ * virtualised host environments, so use the simpler scsi_add_host()
+ * function instead.
  *
  * Return value: 
  *     0 on success / != 0 for error
  **/
-int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
+int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
+                          struct device *dma_dev)
 {
        struct scsi_host_template *sht = shost->hostt;
        int error = -EINVAL;
@@ -199,11 +205,18 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
        if (!shost->can_queue) {
                printk(KERN_ERR "%s: can_queue = 0 no longer supported\n",
                                sht->name);
-               goto out;
+               goto fail;
        }
 
+       error = scsi_setup_command_freelist(shost);
+       if (error)
+               goto fail;
+
        if (!shost->shost_gendev.parent)
                shost->shost_gendev.parent = dev ? dev : &platform_bus;
+       shost->dma_dev = dma_dev;
+
+       device_enable_async_suspend(&shost->shost_gendev);
 
        error = device_add(&shost->shost_gendev);
        if (error)
@@ -212,6 +225,8 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
        scsi_host_set_state(shost, SHOST_RUNNING);
        get_device(shost->shost_gendev.parent);
 
+       device_enable_async_suspend(&shost->shost_dev);
+
        error = device_add(&shost->shost_dev);
        if (error)
                goto out_del_gendev;
@@ -228,8 +243,8 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
        }
 
        if (shost->transportt->create_work_queue) {
-               snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d",
-                       shost->host_no);
+               snprintf(shost->work_q_name, sizeof(shost->work_q_name),
+                        "scsi_wq_%d", shost->host_no);
                shost->work_q = create_singlethread_workqueue(
                                        shost->work_q_name);
                if (!shost->work_q) {
@@ -255,15 +270,19 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
  out_del_gendev:
        device_del(&shost->shost_gendev);
  out:
+       scsi_destroy_command_freelist(shost);
+ fail:
        return error;
 }
-EXPORT_SYMBOL(scsi_add_host);
+EXPORT_SYMBOL(scsi_add_host_with_dma);
 
 static void scsi_host_dev_release(struct device *dev)
 {
        struct Scsi_Host *shost = dev_to_shost(dev);
        struct device *parent = dev->parent;
 
+       scsi_proc_hostdir_rm(shost->hostt);
+
        if (shost->ehandler)
                kthread_stop(shost->ehandler);
        if (shost->work_q)
@@ -284,7 +303,7 @@ static void scsi_host_dev_release(struct device *dev)
        kfree(shost);
 }
 
-struct device_type scsi_host_type = {
+static struct device_type scsi_host_type = {
        .name =         "scsi_host",
        .release =      scsi_host_dev_release,
 };
@@ -326,7 +345,11 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
 
        mutex_init(&shost->scan_mutex);
 
-       shost->host_no = scsi_host_next_hn++; /* XXX(hch): still racy */
+       /*
+        * subtract one because we increment first then return, but we need to
+        * know what the next host number was before increment
+        */
+       shost->host_no = atomic_inc_return(&scsi_host_next_hn) - 1;
        shost->dma_channel = 0xff;
 
        /* These three are default values which can be overridden */
@@ -381,13 +404,8 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
        else
                shost->dma_boundary = 0xffffffff;
 
-       rval = scsi_setup_command_freelist(shost);
-       if (rval)
-               goto fail_kfree;
-
        device_initialize(&shost->shost_gendev);
-       snprintf(shost->shost_gendev.bus_id, BUS_ID_SIZE, "host%d",
-               shost->host_no);
+       dev_set_name(&shost->shost_gendev, "host%d", shost->host_no);
 #ifndef CONFIG_SYSFS_DEPRECATED
        shost->shost_gendev.bus = &scsi_bus_type;
 #endif
@@ -396,22 +414,19 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
        device_initialize(&shost->shost_dev);
        shost->shost_dev.parent = &shost->shost_gendev;
        shost->shost_dev.class = &shost_class;
-       snprintf(shost->shost_dev.bus_id, BUS_ID_SIZE, "host%d",
-                shost->host_no);
+       dev_set_name(&shost->shost_dev, "host%d", shost->host_no);
        shost->shost_dev.groups = scsi_sysfs_shost_attr_groups;
 
        shost->ehandler = kthread_run(scsi_error_handler, shost,
                        "scsi_eh_%d", shost->host_no);
        if (IS_ERR(shost->ehandler)) {
                rval = PTR_ERR(shost->ehandler);
-               goto fail_destroy_freelist;
+               goto fail_kfree;
        }
 
        scsi_proc_hostdir_add(shost->hostt);
        return shost;
 
- fail_destroy_freelist:
-       scsi_destroy_command_freelist(shost);
  fail_kfree:
        kfree(shost);
        return NULL;
@@ -456,16 +471,22 @@ static int __scsi_host_match(struct device *dev, void *data)
  *
  * Return value:
  *     A pointer to located Scsi_Host or NULL.
+ *
+ *     The caller must do a scsi_host_put() to drop the reference
+ *     that scsi_host_get() took. The put_device() below dropped
+ *     the reference from class_find_device().
  **/
 struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
 {
        struct device *cdev;
-       struct Scsi_Host *shost = ERR_PTR(-ENXIO);
+       struct Scsi_Host *shost = NULL;
 
-       cdev = class_find_device(&shost_class, &hostnum, __scsi_host_match);
-       if (cdev)
+       cdev = class_find_device(&shost_class, NULL, &hostnum,
+                                __scsi_host_match);
+       if (cdev) {
                shost = scsi_host_get(class_to_shost(cdev));
-
+               put_device(cdev);
+       }
        return shost;
 }
 EXPORT_SYMBOL(scsi_host_lookup);