[SCSI] qla2xxx: Use PCI-SIG nomenclature for PCIe bandwidth units.
[safe/jmp/linux-2.6] / drivers / scsi / hosts.c
index 6828ca3..1592640 100644 (file)
 #include <linux/module.h>
 #include <linux/blkdev.h>
 #include <linux/kernel.h>
+#include <linux/kthread.h>
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/completion.h>
 #include <linux/transport_class.h>
+#include <linux/platform_device.h>
 
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
@@ -52,8 +54,7 @@ static struct class shost_class = {
 };
 
 /**
- *     scsi_host_set_state - Take the given host through the host
- *             state model.
+ *     scsi_host_set_state - Take the given host through the host state model.
  *     @shost: scsi host to change the state of.
  *     @state: state to change to.
  *
@@ -97,6 +98,7 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
                switch (oldstate) {
                case SHOST_CREATED:
                case SHOST_RUNNING:
+               case SHOST_CANCEL_RECOVERY:
                        break;
                default:
                        goto illegal;
@@ -106,58 +108,75 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
        case SHOST_DEL:
                switch (oldstate) {
                case SHOST_CANCEL:
+               case SHOST_DEL_RECOVERY:
                        break;
                default:
                        goto illegal;
                }
                break;
 
+       case SHOST_CANCEL_RECOVERY:
+               switch (oldstate) {
+               case SHOST_CANCEL:
+               case SHOST_RECOVERY:
+                       break;
+               default:
+                       goto illegal;
+               }
+               break;
+
+       case SHOST_DEL_RECOVERY:
+               switch (oldstate) {
+               case SHOST_CANCEL_RECOVERY:
+                       break;
+               default:
+                       goto illegal;
+               }
+               break;
        }
        shost->shost_state = state;
        return 0;
 
  illegal:
        SCSI_LOG_ERROR_RECOVERY(1,
-                               dev_printk(KERN_ERR, &shost->shost_gendev,
-                                          "Illegal host state transition"
-                                          "%s->%s\n",
-                                          scsi_host_state_name(oldstate),
-                                          scsi_host_state_name(state)));
+                               shost_printk(KERN_ERR, shost,
+                                            "Illegal host state transition"
+                                            "%s->%s\n",
+                                            scsi_host_state_name(oldstate),
+                                            scsi_host_state_name(state)));
        return -EINVAL;
 }
 EXPORT_SYMBOL(scsi_host_set_state);
 
 /**
- * scsi_host_cancel - cancel outstanding IO to this host
- * @shost:     pointer to struct Scsi_Host
- * recovery:   recovery requested to run.
- **/
-static void scsi_host_cancel(struct Scsi_Host *shost, int recovery)
-{
-       struct scsi_device *sdev;
-
-       scsi_host_set_state(shost, SHOST_CANCEL);
-       shost_for_each_device(sdev, shost) {
-               scsi_device_cancel(sdev, recovery);
-       }
-       wait_event(shost->host_wait, (shost->shost_state != SHOST_RECOVERY));
-}
-
-/**
  * scsi_remove_host - remove a scsi host
  * @shost:     a pointer to a scsi host to remove
  **/
 void scsi_remove_host(struct Scsi_Host *shost)
 {
+       unsigned long flags;
+       mutex_lock(&shost->scan_mutex);
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (scsi_host_set_state(shost, SHOST_CANCEL))
+               if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) {
+                       spin_unlock_irqrestore(shost->host_lock, flags);
+                       mutex_unlock(&shost->scan_mutex);
+                       return;
+               }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+       mutex_unlock(&shost->scan_mutex);
        scsi_forget_host(shost);
-       scsi_host_cancel(shost, 0);
        scsi_proc_host_rm(shost);
 
-       scsi_host_set_state(shost, SHOST_DEL);
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (scsi_host_set_state(shost, SHOST_DEL))
+               BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY));
+       spin_unlock_irqrestore(shost->host_lock, flags);
 
        transport_unregister_device(&shost->shost_gendev);
        class_device_unregister(&shost->shost_classdev);
        device_del(&shost->shost_gendev);
+       scsi_proc_hostdir_rm(shost->hostt);
 }
 EXPORT_SYMBOL(scsi_remove_host);
 
@@ -199,18 +218,24 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
 
        get_device(&shost->shost_gendev);
 
-       if (shost->transportt->host_size &&
-           (shost->shost_data = kmalloc(shost->transportt->host_size,
-                                        GFP_KERNEL)) == NULL)
-               goto out_del_classdev;
+       if (shost->transportt->host_size) {
+               shost->shost_data = kzalloc(shost->transportt->host_size,
+                                        GFP_KERNEL);
+               if (shost->shost_data == NULL) {
+                       error = -ENOMEM;
+                       goto out_del_classdev;
+               }
+       }
 
        if (shost->transportt->create_work_queue) {
                snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d",
                        shost->host_no);
                shost->work_q = create_singlethread_workqueue(
                                        shost->work_q_name);
-               if (!shost->work_q)
+               if (!shost->work_q) {
+                       error = -EINVAL;
                        goto out_free_shost_data;
+               }
        }
 
        error = scsi_sysfs_add_host(shost);
@@ -239,20 +264,19 @@ static void scsi_host_dev_release(struct device *dev)
        struct Scsi_Host *shost = dev_to_shost(dev);
        struct device *parent = dev->parent;
 
-       if (shost->ehandler) {
-               DECLARE_COMPLETION(sem);
-               shost->eh_notify = &sem;
-               shost->eh_kill = 1;
-               up(shost->eh_wait);
-               wait_for_completion(&sem);
-               shost->eh_notify = NULL;
-       }
-
+       if (shost->ehandler)
+               kthread_stop(shost->ehandler);
        if (shost->work_q)
                destroy_workqueue(shost->work_q);
+       if (shost->uspace_req_q) {
+               kfree(shost->uspace_req_q->queuedata);
+               scsi_free_queue(shost->uspace_req_q);
+       }
 
-       scsi_proc_hostdir_rm(shost->hostt);
        scsi_destroy_command_freelist(shost);
+       if (shost->bqt)
+               blk_free_tags(shost->bqt);
+
        kfree(shost->shost_data);
 
        if (parent)
@@ -276,31 +300,18 @@ static void scsi_host_dev_release(struct device *dev)
 struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
 {
        struct Scsi_Host *shost;
-       int gfp_mask = GFP_KERNEL, rval;
-       DECLARE_COMPLETION(complete);
+       gfp_t gfp_mask = GFP_KERNEL;
+       int rval;
 
        if (sht->unchecked_isa_dma && privsize)
                gfp_mask |= __GFP_DMA;
 
-        /* Check to see if this host has any error handling facilities */
-        if (!sht->eh_strategy_handler && !sht->eh_abort_handler &&
-           !sht->eh_device_reset_handler && !sht->eh_bus_reset_handler &&
-            !sht->eh_host_reset_handler) {
-               printk(KERN_ERR "ERROR: SCSI host `%s' has no error handling\n"
-                               "ERROR: This is not a safe way to run your "
-                                       "SCSI host\n"
-                               "ERROR: The error handling must be added to "
-                               "this driver\n", sht->proc_name);
-               dump_stack();
-        }
-
-       shost = kmalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask);
+       shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask);
        if (!shost)
                return NULL;
-       memset(shost, 0, sizeof(struct Scsi_Host) + privsize);
 
-       spin_lock_init(&shost->default_lock);
-       scsi_assign_lock(shost, &shost->default_lock);
+       shost->host_lock = &shost->default_lock;
+       spin_lock_init(shost->host_lock);
        shost->shost_state = SHOST_CREATED;
        INIT_LIST_HEAD(&shost->__devices);
        INIT_LIST_HEAD(&shost->__targets);
@@ -308,7 +319,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
        INIT_LIST_HEAD(&shost->starved_list);
        init_waitqueue_head(&shost->host_wait);
 
-       init_MUTEX(&shost->scan_mutex);
+       mutex_init(&shost->scan_mutex);
 
        shost->host_no = scsi_host_next_hn++; /* XXX(hch): still racy */
        shost->dma_channel = 0xff;
@@ -335,16 +346,13 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
        shost->cmd_per_lun = sht->cmd_per_lun;
        shost->unchecked_isa_dma = sht->unchecked_isa_dma;
        shost->use_clustering = sht->use_clustering;
-       shost->ordered_flush = sht->ordered_flush;
        shost->ordered_tag = sht->ordered_tag;
 
-       /*
-        * hosts/devices that do queueing must support ordered tags
-        */
-       if (shost->can_queue > 1 && shost->ordered_flush) {
-               printk(KERN_ERR "scsi: ordered flushes don't support queueing\n");
-               shost->ordered_flush = 0;
-       }
+       if (sht->supported_mode == MODE_UNKNOWN)
+               /* means we didn't set it ... default to INITIATOR */
+               shost->active_mode = MODE_INITIATOR;
+       else
+               shost->active_mode = sht->supported_mode;
 
        if (sht->max_host_blocked)
                shost->max_host_blocked = sht->max_host_blocked;
@@ -383,12 +391,12 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
        snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d",
                  shost->host_no);
 
-       shost->eh_notify = &complete;
-       rval = kernel_thread(scsi_error_handler, shost, 0);
-       if (rval < 0)
+       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;
-       wait_for_completion(&complete);
-       shost->eh_notify = NULL;
+       }
 
        scsi_proc_hostdir_add(shost->hostt);
        return shost;
@@ -424,9 +432,17 @@ void scsi_unregister(struct Scsi_Host *shost)
 }
 EXPORT_SYMBOL(scsi_unregister);
 
+static int __scsi_host_match(struct class_device *cdev, void *data)
+{
+       struct Scsi_Host *p;
+       unsigned short *hostnum = (unsigned short *)data;
+
+       p = class_to_shost(cdev);
+       return p->host_no == *hostnum;
+}
+
 /**
  * scsi_host_lookup - get a reference to a Scsi_Host by host no
- *
  * @hostnum:   host number to locate
  *
  * Return value:
@@ -434,19 +450,12 @@ EXPORT_SYMBOL(scsi_unregister);
  **/
 struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
 {
-       struct class *class = &shost_class;
        struct class_device *cdev;
-       struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p;
+       struct Scsi_Host *shost = ERR_PTR(-ENXIO);
 
-       down_read(&class->subsys.rwsem);
-       list_for_each_entry(cdev, &class->children, node) {
-               p = class_to_shost(cdev);
-               if (p->host_no == hostnum) {
-                       shost = scsi_host_get(p);
-                       break;
-               }
-       }
-       up_read(&class->subsys.rwsem);
+       cdev = class_find_child(&shost_class, &hostnum, __scsi_host_match);
+       if (cdev)
+               shost = scsi_host_get(class_to_shost(cdev));
 
        return shost;
 }
@@ -497,7 +506,9 @@ EXPORT_SYMBOL(scsi_is_host_device);
  * @work:      Work to queue for execution.
  *
  * Return value:
- *     0 on success / != 0 for error
+ *     1 - work queued for execution
+ *     0 - work is already queued
+ *     -EINVAL - work queue doesn't exist
  **/
 int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work)
 {