scsi: hpsa: check for tag collision
[linux-2.6-microblaze.git] / drivers / scsi / hpsa.c
index 1bef1da..283fd60 100644 (file)
@@ -5526,6 +5526,9 @@ static int hpsa_ioaccel_submit(struct ctlr_info *h,
        if (!dev)
                return SCSI_MLQUEUE_HOST_BUSY;
 
+       if (hpsa_simple_mode)
+               return IO_ACCEL_INELIGIBLE;
+
        cmd->host_scribble = (unsigned char *) c;
 
        if (dev->offload_enabled) {
@@ -5632,6 +5635,8 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
                return 0;
        }
        c = cmd_tagged_alloc(h, cmd);
+       if (c == NULL)
+               return SCSI_MLQUEUE_DEVICE_BUSY;
 
        /*
         * Call alternate submit routine for I/O accelerated commands.
@@ -6038,7 +6043,6 @@ static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
                BUG();
        }
 
-       atomic_inc(&c->refcount);
        if (unlikely(!hpsa_is_cmd_idle(c))) {
                /*
                 * We expect that the SCSI layer will hand us a unique tag
@@ -6046,14 +6050,20 @@ static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
                 * two requests...because if the selected command isn't idle
                 * then someone is going to be very disappointed.
                 */
-               dev_err(&h->pdev->dev,
-                       "tag collision (tag=%d) in cmd_tagged_alloc().\n",
-                       idx);
-               if (c->scsi_cmd != NULL)
-                       scsi_print_command(c->scsi_cmd);
-               scsi_print_command(scmd);
+               if (idx != h->last_collision_tag) { /* Print once per tag */
+                       dev_warn(&h->pdev->dev,
+                               "%s: tag collision (tag=%d)\n", __func__, idx);
+                       if (c->scsi_cmd != NULL)
+                               scsi_print_command(c->scsi_cmd);
+                       if (scmd)
+                               scsi_print_command(scmd);
+                       h->last_collision_tag = idx;
+               }
+               return NULL;
        }
 
+       atomic_inc(&c->refcount);
+
        hpsa_cmd_partial_init(h, idx, c);
        return c;
 }
@@ -7978,10 +7988,15 @@ clean_up:
 static void hpsa_free_irqs(struct ctlr_info *h)
 {
        int i;
+       int irq_vector = 0;
+
+       if (hpsa_simple_mode)
+               irq_vector = h->intr_mode;
 
        if (!h->msix_vectors || h->intr_mode != PERF_MODE_INT) {
                /* Single reply queue, only one irq to free */
-               free_irq(pci_irq_vector(h->pdev, 0), &h->q[h->intr_mode]);
+               free_irq(pci_irq_vector(h->pdev, irq_vector),
+                               &h->q[h->intr_mode]);
                h->q[h->intr_mode] = 0;
                return;
        }
@@ -8000,6 +8015,10 @@ static int hpsa_request_irqs(struct ctlr_info *h,
        irqreturn_t (*intxhandler)(int, void *))
 {
        int rc, i;
+       int irq_vector = 0;
+
+       if (hpsa_simple_mode)
+               irq_vector = h->intr_mode;
 
        /*
         * initialize h->q[x] = x so that interrupt handlers know which
@@ -8035,14 +8054,14 @@ static int hpsa_request_irqs(struct ctlr_info *h,
                if (h->msix_vectors > 0 || h->pdev->msi_enabled) {
                        sprintf(h->intrname[0], "%s-msi%s", h->devname,
                                h->msix_vectors ? "x" : "");
-                       rc = request_irq(pci_irq_vector(h->pdev, 0),
+                       rc = request_irq(pci_irq_vector(h->pdev, irq_vector),
                                msixhandler, 0,
                                h->intrname[0],
                                &h->q[h->intr_mode]);
                } else {
                        sprintf(h->intrname[h->intr_mode],
                                "%s-intx", h->devname);
-                       rc = request_irq(pci_irq_vector(h->pdev, 0),
+                       rc = request_irq(pci_irq_vector(h->pdev, irq_vector),
                                intxhandler, IRQF_SHARED,
                                h->intrname[0],
                                &h->q[h->intr_mode]);
@@ -8050,7 +8069,7 @@ static int hpsa_request_irqs(struct ctlr_info *h,
        }
        if (rc) {
                dev_err(&h->pdev->dev, "failed to get irq %d for %s\n",
-                      pci_irq_vector(h->pdev, 0), h->devname);
+                      pci_irq_vector(h->pdev, irq_vector), h->devname);
                hpsa_free_irqs(h);
                return -ENODEV;
        }
@@ -8116,6 +8135,11 @@ static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
                destroy_workqueue(h->rescan_ctlr_wq);
                h->rescan_ctlr_wq = NULL;
        }
+       if (h->monitor_ctlr_wq) {
+               destroy_workqueue(h->monitor_ctlr_wq);
+               h->monitor_ctlr_wq = NULL;
+       }
+
        kfree(h);                               /* init_one 1 */
 }
 
@@ -8451,8 +8475,8 @@ static void hpsa_event_monitor_worker(struct work_struct *work)
 
        spin_lock_irqsave(&h->lock, flags);
        if (!h->remove_in_progress)
-               schedule_delayed_work(&h->event_monitor_work,
-                                       HPSA_EVENT_MONITOR_INTERVAL);
+               queue_delayed_work(h->monitor_ctlr_wq, &h->event_monitor_work,
+                               HPSA_EVENT_MONITOR_INTERVAL);
        spin_unlock_irqrestore(&h->lock, flags);
 }
 
@@ -8497,7 +8521,7 @@ static void hpsa_monitor_ctlr_worker(struct work_struct *work)
 
        spin_lock_irqsave(&h->lock, flags);
        if (!h->remove_in_progress)
-               schedule_delayed_work(&h->monitor_ctlr_work,
+               queue_delayed_work(h->monitor_ctlr_wq, &h->monitor_ctlr_work,
                                h->heartbeat_sample_interval);
        spin_unlock_irqrestore(&h->lock, flags);
 }
@@ -8665,6 +8689,12 @@ reinit_after_soft_reset:
                goto clean7;    /* aer/h */
        }
 
+       h->monitor_ctlr_wq = hpsa_create_controller_wq(h, "monitor");
+       if (!h->monitor_ctlr_wq) {
+               rc = -ENOMEM;
+               goto clean7;
+       }
+
        /*
         * At this point, the controller is ready to take commands.
         * Now, if reset_devices and the hard reset didn't work, try
@@ -8794,6 +8824,10 @@ clean1:  /* wq/aer/h */
                destroy_workqueue(h->rescan_ctlr_wq);
                h->rescan_ctlr_wq = NULL;
        }
+       if (h->monitor_ctlr_wq) {
+               destroy_workqueue(h->monitor_ctlr_wq);
+               h->monitor_ctlr_wq = NULL;
+       }
        kfree(h);
        return rc;
 }
@@ -8941,6 +8975,7 @@ static void hpsa_remove_one(struct pci_dev *pdev)
        cancel_delayed_work_sync(&h->event_monitor_work);
        destroy_workqueue(h->rescan_ctlr_wq);
        destroy_workqueue(h->resubmit_wq);
+       destroy_workqueue(h->monitor_ctlr_wq);
 
        hpsa_delete_sas_host(h);